aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--Makefile.common5
-rw-r--r--NEWS5
-rw-r--r--audio/softsynth/mt32.cpp4
-rw-r--r--backends/platform/android/android.cpp6
-rw-r--r--backends/platform/android/asset-archive.cpp26
-rw-r--r--backends/platform/android/jni.cpp4
-rw-r--r--backends/platform/maemo/debian/changelog10
-rw-r--r--backends/platform/psp/audio.cpp2
-rwxr-xr-xconfigure2
-rw-r--r--devtools/create_neverhood/create_neverhood.cpp537
-rw-r--r--devtools/create_neverhood/create_neverhood.h28
-rw-r--r--devtools/create_neverhood/module.mk12
-rw-r--r--devtools/create_neverhood/tables.h718
-rw-r--r--devtools/create_neverhood/util.cpp152
-rw-r--r--devtools/create_neverhood/util.h57
-rw-r--r--dists/engine-data/neverhood.datbin0 -> 25416 bytes
-rw-r--r--dists/scummvm.rc3
-rw-r--r--dists/scummvm.rc.in3
-rw-r--r--doc/se/LasMig141
-rw-r--r--engines/cge/cge.h6
-rw-r--r--engines/cge/detection.cpp106
-rw-r--r--engines/configure.engines1
-rw-r--r--engines/drascula/detection.cpp126
-rw-r--r--engines/drascula/interface.cpp4
-rw-r--r--engines/engines.mk5
-rw-r--r--engines/hopkins/debugger.cpp12
-rw-r--r--engines/hopkins/debugger.h1
-rw-r--r--engines/hopkins/detection.cpp1
-rw-r--r--engines/hopkins/detection_tables.h134
-rw-r--r--engines/hopkins/graphics.cpp37
-rw-r--r--engines/hopkins/graphics.h6
-rw-r--r--engines/hopkins/hopkins.cpp53
-rw-r--r--engines/hopkins/hopkins.h13
-rw-r--r--engines/hopkins/lines.cpp106
-rw-r--r--engines/hopkins/lines.h4
-rw-r--r--engines/lure/menu.cpp1
-rw-r--r--engines/made/database.cpp22
-rw-r--r--engines/mohawk/detection_tables.h2
-rw-r--r--engines/neverhood/background.cpp60
-rw-r--r--engines/neverhood/background.h49
-rw-r--r--engines/neverhood/blbarchive.cpp159
-rw-r--r--engines/neverhood/blbarchive.h77
-rw-r--r--engines/neverhood/detection.cpp271
-rw-r--r--engines/neverhood/diskplayerscene.cpp511
-rw-r--r--engines/neverhood/diskplayerscene.h110
-rw-r--r--engines/neverhood/entity.cpp159
-rw-r--r--engines/neverhood/entity.h120
-rw-r--r--engines/neverhood/gamemodule.cpp849
-rw-r--r--engines/neverhood/gamemodule.h86
-rw-r--r--engines/neverhood/gamevars.cpp133
-rw-r--r--engines/neverhood/gamevars.h193
-rw-r--r--engines/neverhood/graphics.cpp349
-rw-r--r--engines/neverhood/graphics.h159
-rw-r--r--engines/neverhood/klaymen.cpp6141
-rw-r--r--engines/neverhood/klaymen.h771
-rw-r--r--engines/neverhood/menumodule.cpp1116
-rw-r--r--engines/neverhood/menumodule.h265
-rw-r--r--engines/neverhood/messages.h37
-rw-r--r--engines/neverhood/microtiles.cpp160
-rw-r--r--engines/neverhood/microtiles.h64
-rw-r--r--engines/neverhood/module.cpp123
-rw-r--r--engines/neverhood/module.h71
-rw-r--r--engines/neverhood/module.mk58
-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.cpp2565
-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.cpp3203
-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
-rw-r--r--engines/neverhood/mouse.cpp256
-rw-r--r--engines/neverhood/mouse.h63
-rw-r--r--engines/neverhood/navigationscene.cpp225
-rw-r--r--engines/neverhood/navigationscene.h61
-rw-r--r--engines/neverhood/neverhood.cpp189
-rw-r--r--engines/neverhood/neverhood.h141
-rw-r--r--engines/neverhood/palette.cpp201
-rw-r--r--engines/neverhood/palette.h68
-rw-r--r--engines/neverhood/resource.cpp583
-rw-r--r--engines/neverhood/resource.h205
-rw-r--r--engines/neverhood/resourceman.cpp128
-rw-r--r--engines/neverhood/resourceman.h94
-rw-r--r--engines/neverhood/saveload.cpp164
-rw-r--r--engines/neverhood/scene.cpp581
-rw-r--r--engines/neverhood/scene.h226
-rw-r--r--engines/neverhood/screen.cpp420
-rw-r--r--engines/neverhood/screen.h106
-rw-r--r--engines/neverhood/smackerplayer.cpp237
-rw-r--r--engines/neverhood/smackerplayer.h90
-rw-r--r--engines/neverhood/smackerscene.cpp122
-rw-r--r--engines/neverhood/smackerscene.h54
-rw-r--r--engines/neverhood/sound.cpp788
-rw-r--r--engines/neverhood/sound.h286
-rw-r--r--engines/neverhood/sprite.cpp486
-rw-r--r--engines/neverhood/sprite.h192
-rw-r--r--engines/neverhood/staticdata.cpp200
-rw-r--r--engines/neverhood/staticdata.h116
-rw-r--r--engines/neverhood/todo.txt45
-rw-r--r--engines/pegasus/neighborhood/norad/delta/globegame.cpp8
-rw-r--r--engines/plugins_table.h3
-rw-r--r--engines/saga/events.cpp3
-rw-r--r--engines/sci/detection_tables.h16
-rw-r--r--engines/sci/engine/workarounds.cpp1
-rw-r--r--engines/sky/music/musicbase.cpp5
-rw-r--r--engines/sword1/animation.cpp2
-rw-r--r--engines/sword1/sound.cpp3
-rw-r--r--engines/sword1/sound.h3
-rw-r--r--engines/sword1/sword1.cpp2
-rw-r--r--engines/toltecs/movie.cpp19
-rw-r--r--engines/toltecs/movie.h4
-rw-r--r--engines/wintermute/base/base_game.cpp4
-rw-r--r--engines/wintermute/base/base_quick_msg.cpp10
-rw-r--r--engines/wintermute/base/base_quick_msg.h8
-rw-r--r--gui/browser.h1
-rw-r--r--gui/browser_osx.mm73
-rw-r--r--gui/themes/translations.datbin436158 -> 441486 bytes
-rw-r--r--po/da_DA.po121
-rw-r--r--po/pl_PL.po134
-rw-r--r--po/se_SE.po130
-rw-r--r--video/smk_decoder.h6
156 files changed, 45595 insertions, 585 deletions
diff --git a/.gitignore b/.gitignore
index 80adcb7551..b834214a00 100644
--- a/.gitignore
+++ b/.gitignore
@@ -116,8 +116,10 @@ project.xcworkspace
/devtools/create_kyradat/create_kyradat
/devtools/create_lure/create_lure
/devtools/create_mads/create_mads
+/devtools/create_neverhood/create_neverhood
/devtools/create_project/create_project
/devtools/create_teenagent/create_teenagent
+/devtools/create_tony/create_tony
/devtools/create_toon/create_toon
/devtools/create_translations/create_translations
/devtools/qtable/qtable
diff --git a/Makefile.common b/Makefile.common
index 08ff411bb1..6b23bd5b1c 100644
--- a/Makefile.common
+++ b/Makefile.common
@@ -78,7 +78,7 @@ endif
$(EXECUTABLE): $(OBJS)
$(QUIET_LINK)$(LD) $(LDFLAGS) $(PRE_OBJS_FLAGS) $+ $(POST_OBJS_FLAGS) $(LIBS) -o $@
-distclean: clean
+distclean: clean clean-devtools
$(RM) config.h config.mk config.log
clean:
@@ -253,6 +253,9 @@ endif
ifdef ENABLE_LURE
DIST_FILES_ENGINEDATA+=lure.dat
endif
+ifdef ENABLE_NEVERHOOD
+DIST_FILES_ENGINEDATA+=neverhood.dat
+endif
ifdef ENABLE_QUEEN
DIST_FILES_ENGINEDATA+=queen.tbl
endif
diff --git a/NEWS b/NEWS
index e52daeccd7..cf9a07e8cb 100644
--- a/NEWS
+++ b/NEWS
@@ -1,7 +1,10 @@
For a more comprehensive changelog of the latest experimental code, see:
https://github.com/scummvm/scummvm/commits/
-1.6.0 (????-??-??)
+1.7.0 (????-??-??)
+
+
+1.6.0 (2013-05-31)
New Games:
- Added support for 3 Skulls of the Toltecs.
- Added support for Eye of the Beholder.
diff --git a/audio/softsynth/mt32.cpp b/audio/softsynth/mt32.cpp
index 3ae8e14b36..00d0469356 100644
--- a/audio/softsynth/mt32.cpp
+++ b/audio/softsynth/mt32.cpp
@@ -147,10 +147,6 @@ MidiDriver_MT32::MidiDriver_MT32(Audio::Mixer *mixer) : MidiDriver_Emulated(mixe
}
_reportHandler = NULL;
_synth = NULL;
- // A higher baseFreq reduces the length used in generateSamples(),
- // and means that the timer callback will be called more often.
- // That results in more accurate timing.
- _baseFreq = 10000;
// Unfortunately bugs in the emulator cause inaccurate tuning
// at rates other than 32KHz, thus we produce data at 32KHz and
// rely on Mixer to convert.
diff --git a/backends/platform/android/android.cpp b/backends/platform/android/android.cpp
index 0b31ee717c..f06e4be19e 100644
--- a/backends/platform/android/android.cpp
+++ b/backends/platform/android/android.cpp
@@ -116,7 +116,7 @@ OSystem_Android::OSystem_Android(int audio_sample_rate, int audio_buffer_size) :
_screen_changeid(0),
_egl_surface_width(0),
_egl_surface_height(0),
- _htc_fail(false),
+ _htc_fail(true),
_force_redraw(false),
_game_texture(0),
_overlay_texture(0),
@@ -162,10 +162,10 @@ OSystem_Android::OSystem_Android(int audio_sample_rate, int audio_buffer_size) :
getSystemProperty("ro.product.cpu.abi").c_str());
mf.toLowercase();
- _htc_fail = mf.contains("htc");
+ /*_htc_fail = mf.contains("htc");
if (_htc_fail)
- LOGI("Enabling HTC workaround");
+ LOGI("Enabling HTC workaround");*/
}
OSystem_Android::~OSystem_Android() {
diff --git a/backends/platform/android/asset-archive.cpp b/backends/platform/android/asset-archive.cpp
index 14840de996..da378fb082 100644
--- a/backends/platform/android/asset-archive.cpp
+++ b/backends/platform/android/asset-archive.cpp
@@ -97,7 +97,9 @@ JavaInputStream::JavaInputStream(JNIEnv *env, jobject is) :
{
_input_stream = env->NewGlobalRef(is);
_buflen = 8192;
- _buf = (jbyteArray)env->NewGlobalRef(env->NewByteArray(_buflen));
+ jobject buf = env->NewByteArray(_buflen);
+ _buf = (jbyteArray)env->NewGlobalRef(buf);
+ env->DeleteLocalRef(buf);
jclass cls = env->GetObjectClass(_input_stream);
MID_mark = env->GetMethodID(cls, "mark", "(I)V");
@@ -112,6 +114,7 @@ JavaInputStream::JavaInputStream(JNIEnv *env, jobject is) :
assert(MID_reset);
MID_skip = env->GetMethodID(cls, "skip", "(J)J");
assert(MID_skip);
+ env->DeleteLocalRef(cls);
// Mark start of stream, so we can reset back to it.
// readlimit is set to something bigger than anything we might
@@ -142,7 +145,9 @@ uint32 JavaInputStream::read(void *dataPtr, uint32 dataSize) {
_buflen = dataSize;
env->DeleteGlobalRef(_buf);
- _buf = static_cast<jbyteArray>(env->NewGlobalRef(env->NewByteArray(_buflen)));
+ jobject buf = env->NewByteArray(_buflen);
+ _buf = static_cast<jbyteArray>(env->NewGlobalRef(buf));
+ env->DeleteLocalRef(buf);
}
jint ret = env->CallIntMethod(_input_stream, MID_read, _buf, 0, dataSize);
@@ -290,6 +295,7 @@ AssetFdReadStream::AssetFdReadStream(JNIEnv *env, jobject assetfd) :
jclass cls = env->GetObjectClass(_assetfd);
MID_close = env->GetMethodID(cls, "close", "()V");
assert(MID_close);
+ env->DeleteLocalRef(cls);
jmethodID MID_getStartOffset =
env->GetMethodID(cls, "getStartOffset", "()J");
@@ -311,8 +317,10 @@ AssetFdReadStream::AssetFdReadStream(JNIEnv *env, jobject assetfd) :
jclass fd_cls = env->GetObjectClass(javafd);
jfieldID FID_descriptor = env->GetFieldID(fd_cls, "descriptor", "I");
assert(FID_descriptor);
+ env->DeleteLocalRef(fd_cls);
_fd = env->GetIntField(javafd, FID_descriptor);
+ env->DeleteLocalRef(javafd);
}
AssetFdReadStream::~AssetFdReadStream() {
@@ -382,6 +390,7 @@ AndroidAssetArchive::AndroidAssetArchive(jobject am) {
MID_list = env->GetMethodID(cls, "list",
"(Ljava/lang/String;)[Ljava/lang/String;");
assert(MID_list);
+ env->DeleteLocalRef(cls);
}
AndroidAssetArchive::~AndroidAssetArchive() {
@@ -452,7 +461,9 @@ int AndroidAssetArchive::listMembers(Common::ArchiveMemberList &member_list) con
member_list.push_back(getMember(thispath));
++count;
} else {
- dirlist.push_back(thispath);
+ // AssetManager is ridiculously slow and we don't care
+ // about subdirectories at the moment, so ignore them.
+ // dirlist.push_back(thispath);
}
}
@@ -481,8 +492,10 @@ Common::SeekableReadStream *AndroidAssetArchive::createReadStreamForMember(const
env->ExceptionClear();
else if (afd != 0) {
// success :)
+ Common::SeekableReadStream *stream = new AssetFdReadStream(env, afd);
env->DeleteLocalRef(jpath);
- return new AssetFdReadStream(env, afd);
+ env->DeleteLocalRef(afd);
+ return stream;
}
// ... and fallback to normal open() if that doesn't work
@@ -498,7 +511,10 @@ Common::SeekableReadStream *AndroidAssetArchive::createReadStreamForMember(const
return 0;
}
- return new JavaInputStream(env, is);
+ Common::SeekableReadStream *stream = new JavaInputStream(env, is);
+ env->DeleteLocalRef(jpath);
+ env->DeleteLocalRef(is);
+ return stream;
}
#endif
diff --git a/backends/platform/android/jni.cpp b/backends/platform/android/jni.cpp
index a7ebb87651..2b738dd63f 100644
--- a/backends/platform/android/jni.cpp
+++ b/backends/platform/android/jni.cpp
@@ -603,10 +603,10 @@ void JNI::setPause(JNIEnv *env, jobject self, jboolean value) {
g_engine->pauseEngine(value);
- if (value &&
+ /*if (value &&
g_engine->hasFeature(Engine::kSupportsSavingDuringRuntime) &&
g_engine->canSaveGameStateCurrently())
- g_engine->saveGameState(0, "Android parachute");
+ g_engine->saveGameState(0, "Android parachute");*/
}
pause = value;
diff --git a/backends/platform/maemo/debian/changelog b/backends/platform/maemo/debian/changelog
index ea44574e96..568edd0282 100644
--- a/backends/platform/maemo/debian/changelog
+++ b/backends/platform/maemo/debian/changelog
@@ -1,8 +1,14 @@
-scummvm (1.6.0~git) unstable; urgency=low
+scummvm (1.7.0~git) unstable; urgency=low
* Development snapshot
- -- Tarek Soliman <tsoliman@scummvm.org> Tue, 10 Jul 2012 23:02:00 -0500
+ -- Tarek Soliman <tsoliman@scummvm.org> Sat, 01 Jun 2013 21:03:52 -0500
+
+scummvm (1.6.0) unstable; urgency=low
+
+ * 1.6.0 release
+
+ -- Tarek Soliman <tsoliman@scummvm.org> Fri, 31 May 2013 23:02:00 -0500
scummvm (1.5.0) unstable; urgency=low
diff --git a/backends/platform/psp/audio.cpp b/backends/platform/psp/audio.cpp
index 4fab9fdd3f..dcbf0b2239 100644
--- a/backends/platform/psp/audio.cpp
+++ b/backends/platform/psp/audio.cpp
@@ -100,8 +100,6 @@ void PspAudio::threadFunction() {
PSP_DEBUG_PRINT("audio thread unpaused\n");
}
- PSP_DEBUG_PRINT("remaining samples[%d]\n", _remainingSamples);
-
PSP_DEBUG_PRINT("filling buffer[%d]\n", _bufferToFill);
_callback(_userData, _buffers[_bufferToFill], _bufferSize); // ask mixer to fill in data
nextBuffer(_bufferToFill);
diff --git a/configure b/configure
index db347c160d..5016a86239 100755
--- a/configure
+++ b/configure
@@ -2869,7 +2869,7 @@ esac
# Enable 16bit support only for backends which support it
#
case $_backend in
- android | bada | dingux | dreamcast | gph | iphone | maemo | openpandora | psp | samsungtv | sdl | webos | wii)
+ android | bada | dingux | dc | gph | iphone | maemo | openpandora | psp | samsungtv | sdl | webos | wii)
if test "$_16bit" = auto ; then
_16bit=yes
else
diff --git a/devtools/create_neverhood/create_neverhood.cpp b/devtools/create_neverhood/create_neverhood.cpp
new file mode 100644
index 0000000000..323066d8b1
--- /dev/null
+++ b/devtools/create_neverhood/create_neverhood.cpp
@@ -0,0 +1,537 @@
+/* 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.
+ *
+ */
+
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+
+// HACK to allow building with the SDL backend on MinGW
+// see bug #1800764 "TOOLS: MinGW tools building broken"
+#ifdef main
+#undef main
+#endif // main
+
+#include <vector>
+#include "create_neverhood.h"
+#include "tables.h"
+
+const int DAT_VERSION = 0;
+
+uint32 dataSize;
+byte *data;
+uint32 dataStart = 0x004AE000;
+uint32 fileStart = 0x000AC600;
+
+class HitRectList;
+class RectList;
+class MessageList;
+class NavigationList;
+
+void addMessageList(uint32 messageListCount, uint32 messageListOffset);
+
+void loadExe(const char *filename) {
+ FILE *exe = fopen(filename, "rb");
+ dataSize = fileSize(exe);
+ data = new byte[dataSize];
+ fread(data, dataSize, 1, exe);
+ fclose(exe);
+}
+
+byte *getData(uint32 offset) {
+ return data + offset - dataStart + fileStart;
+}
+
+const char *getStringP(uint32 offset) {
+ return offset != 0 ? (const char*)getData(offset) : NULL;
+}
+
+uint32 calcHash(const char *value) {
+ if (!value)
+ return 0;
+ uint32 hash = 0, shiftValue = 0;
+ while (*value != 0) {
+ char ch = *value++;
+ if (ch >= 'a' && ch <= 'z')
+ ch -= 32;
+ else if (ch >= '0' && ch <= '9')
+ ch += 22;
+ shiftValue += ch - 64;
+ if (shiftValue >= 32)
+ shiftValue -= 32;
+ hash ^= 1 << shiftValue;
+ }
+ return hash;
+}
+
+struct HitRect {
+ int16 x1, y1, x2, y2;
+ uint16 messageNum;
+
+ void load(uint32 offset) {
+ byte *item = getData(offset);
+ x1 = READ_LE_UINT16(item + 0);
+ y1 = READ_LE_UINT16(item + 2);
+ x2 = READ_LE_UINT16(item + 4);
+ y2 = READ_LE_UINT16(item + 6);
+ messageNum = READ_LE_UINT16(item + 8);
+ }
+
+ void save(FILE *fd) {
+ writeUint16LE(fd, x1);
+ writeUint16LE(fd, y1);
+ writeUint16LE(fd, x2);
+ writeUint16LE(fd, y2);
+ writeUint16LE(fd, messageNum);
+ }
+
+ int getItemSize() const {
+ return 10;
+ }
+
+};
+
+struct MessageItem {
+ uint16 messageNum;
+ uint32 messageParam;
+ MessageItem() {}
+ MessageItem(uint16 msgNum, uint32 msgParam) : messageNum(msgNum), messageParam(msgParam) {}
+
+ void load(uint32 offset) {
+ byte *item = getData(offset);
+ messageNum = READ_LE_UINT16(item + 0);
+ messageParam = READ_LE_UINT32(item + 4);
+ }
+
+ void save(FILE *fd) {
+ writeUint16LE(fd, messageNum);
+ writeUint32LE(fd, messageParam);
+ }
+
+ int getItemSize() const {
+ return 8;
+ }
+
+};
+
+struct SubRectItem {
+ int16 x1, y1, x2, y2;
+ uint32 messageListCount;
+ uint32 messageListOffset;
+
+ void load(uint32 offset) {
+ byte *item = getData(offset);
+ x1 = READ_LE_UINT16(item + 0);
+ y1 = READ_LE_UINT16(item + 2);
+ x2 = READ_LE_UINT16(item + 4);
+ y2 = READ_LE_UINT16(item + 6);
+ messageListCount = READ_LE_UINT32(item + 8);
+ messageListOffset = READ_LE_UINT32(item + 12);
+ // Add the message to the message list
+ addMessageList(messageListCount, messageListOffset);
+ }
+
+ void save(FILE *fd) {
+ writeUint16LE(fd, x1);
+ writeUint16LE(fd, y1);
+ writeUint16LE(fd, x2);
+ writeUint16LE(fd, y2);
+ writeUint32LE(fd, messageListOffset);
+ }
+
+ int getItemSize() const {
+ return 16;
+ }
+
+};
+
+struct RectItem {
+ int16 x1, y1, x2, y2;
+ uint32 subRectListCount;
+ uint32 subRectListOffset;
+ std::vector<SubRectItem> subRectItems;
+
+ void load(uint32 offset) {
+ byte *item = getData(offset);
+ uint32 subItemOffset;
+ x1 = READ_LE_UINT16(item + 0);
+ y1 = READ_LE_UINT16(item + 2);
+ x2 = READ_LE_UINT16(item + 4);
+ y2 = READ_LE_UINT16(item + 6);
+ subRectListCount = READ_LE_UINT32(item + 8);
+ subRectListOffset = READ_LE_UINT32(item + 12);
+ subItemOffset = subRectListOffset;
+ for (uint32 j = 0; j < subRectListCount; j++) {
+ SubRectItem subRectItem;
+ subRectItem.load(subItemOffset);
+ subItemOffset += 16;
+ subRectItems.push_back(subRectItem);
+ }
+ }
+
+ void save(FILE *fd) {
+ writeUint16LE(fd, x1);
+ writeUint16LE(fd, y1);
+ writeUint16LE(fd, x2);
+ writeUint16LE(fd, y2);
+ writeUint32LE(fd, subRectItems.size());
+ for (uint32 j = 0; j < subRectItems.size(); j++)
+ subRectItems[j].save(fd);
+ }
+
+ int getItemSize() const {
+ return 16;
+ }
+
+};
+
+struct NavigationItem {
+ uint32 fileHash;
+ uint32 leftSmackerFileHash;
+ uint32 rightSmackerFileHash;
+ uint32 middleSmackerFileHash;
+ byte interactive;
+ byte middleFlag;
+ uint32 mouseCursorFileHash;
+
+ void load(uint32 offset) {
+ byte *item = getData(offset);
+ fileHash = READ_LE_UINT32(item + 0);
+ leftSmackerFileHash = READ_LE_UINT32(item + 4);
+ rightSmackerFileHash = READ_LE_UINT32(item + 8);
+ middleSmackerFileHash = READ_LE_UINT32(item + 12);
+ interactive = item[16];
+ middleFlag = item[17];
+ mouseCursorFileHash = READ_LE_UINT32(item + 20);
+ }
+
+ void save(FILE *fd) {
+ writeUint32LE(fd, fileHash);
+ writeUint32LE(fd, leftSmackerFileHash);
+ writeUint32LE(fd, rightSmackerFileHash);
+ writeUint32LE(fd, middleSmackerFileHash);
+ writeByte(fd, interactive);
+ writeByte(fd, middleFlag);
+ writeUint32LE(fd, mouseCursorFileHash);
+ }
+
+ int getItemSize() const {
+ return 24;
+ }
+
+};
+
+struct SceneInfo140Item {
+ uint32 id;
+ uint32 bgFilename1;
+ uint32 bgFilename2;
+ uint32 txFilename;
+ uint32 bgFilename3;
+ byte xPosIndex;
+ byte count;
+
+ void load(uint32 offset) {
+ byte *item = getData(offset);
+ id = offset;
+ // Only save the hashes instead of the full names
+ bgFilename1 = calcHash(getStringP(READ_LE_UINT32(item + 0)));
+ bgFilename2 = calcHash(getStringP(READ_LE_UINT32(item + 4)));
+ txFilename = calcHash(getStringP(READ_LE_UINT32(item + 8)));
+ bgFilename3 = calcHash(getStringP(READ_LE_UINT32(item + 12)));
+ xPosIndex = item[16];
+ count = item[17];
+ }
+
+ void save(FILE *fd) {
+ writeUint32LE(fd, id);
+ writeUint32LE(fd, bgFilename1);
+ writeUint32LE(fd, bgFilename2);
+ writeUint32LE(fd, txFilename);
+ writeUint32LE(fd, bgFilename3);
+ writeByte(fd, xPosIndex);
+ writeByte(fd, count);
+ }
+
+};
+
+struct SceneInfo2700Item {
+ uint32 id;
+ uint32 bgFilename;
+ uint32 class437Filename;
+ uint32 dataResourceFilename;
+ uint32 pointListName;
+ uint32 rectListName;
+ uint32 exPaletteFilename2;
+ uint32 exPaletteFilename1;
+ uint32 mouseCursorFilename;
+ int16 which1;
+ int16 which2;
+
+ void load(uint32 offset) {
+ byte *item = getData(offset);
+ id = offset;
+ // Only save the hashes instead of the full names
+ bgFilename = calcHash(getStringP(READ_LE_UINT32(item + 0)));
+ class437Filename = calcHash(getStringP(READ_LE_UINT32(item + 4)));
+ dataResourceFilename = calcHash(getStringP(READ_LE_UINT32(item + 8)));
+ pointListName = calcHash(getStringP(READ_LE_UINT32(item + 12)));
+ rectListName = calcHash(getStringP(READ_LE_UINT32(item + 16)));
+ exPaletteFilename2 = calcHash(getStringP(READ_LE_UINT32(item + 20)));
+ exPaletteFilename1 = calcHash(getStringP(READ_LE_UINT32(item + 24)));
+ mouseCursorFilename = calcHash(getStringP(READ_LE_UINT32(item + 28)));
+ which1 = READ_LE_UINT16(item + 32);
+ which2 = READ_LE_UINT16(item + 34);
+ }
+
+ void save(FILE *fd) {
+ writeUint32LE(fd, id);
+ writeUint32LE(fd, bgFilename);
+ writeUint32LE(fd, class437Filename);
+ writeUint32LE(fd, dataResourceFilename);
+ writeUint32LE(fd, pointListName);
+ writeUint32LE(fd, rectListName);
+ writeUint32LE(fd, exPaletteFilename2);
+ writeUint32LE(fd, exPaletteFilename1);
+ writeUint32LE(fd, mouseCursorFilename);
+ writeUint16LE(fd, which1);
+ writeUint16LE(fd, which2);
+ }
+
+};
+
+template<class ITEMCLASS>
+class StaticDataList {
+public:
+ uint32 id;
+ std::vector<ITEMCLASS> items;
+
+ virtual ~StaticDataList() {
+ }
+
+ void add(ITEMCLASS item) {
+ items.push_back(item);
+ }
+
+ int getCount() const {
+ return items.size();
+ }
+
+ ITEMCLASS *getListItem(int index) {
+ return &items[index];
+ }
+
+ virtual bool specialLoadList(uint32 count, uint32 offset) {
+ return false;
+ }
+
+ void loadList(uint32 count, uint32 offset) {
+ id = offset;
+ if (!specialLoadList(count, offset)) {
+ for (uint32 i = 0; i < count; i++) {
+ ITEMCLASS listItem;
+ listItem.load(offset);
+ offset += listItem.getItemSize();
+ add(listItem);
+ }
+ }
+ }
+
+ void saveList(FILE *fd) {
+ writeUint32LE(fd, id);
+ writeUint32LE(fd, getCount());
+ for (int i = 0; i < getCount(); i++) {
+ items[i].save(fd);
+ }
+ }
+
+};
+
+class HitRectList : public StaticDataList<HitRect> {
+};
+
+class RectList : public StaticDataList<RectItem> {
+};
+
+class MessageList : public StaticDataList<MessageItem> {
+public:
+
+ virtual bool specialLoadList(uint32 count, uint32 offset) {
+ // Special code for message lists which are set at runtime (but otherwise constant)
+ switch (offset) {
+ // Scene 1002 rings
+ case 0x004B4200:
+ add(MessageItem(0x4800, 258));
+ add(MessageItem(0x100D, 0x4A845A00));
+ add(MessageItem(0x4805, 1));
+ return true;
+ case 0x004B4218:
+ add(MessageItem(0x4800, 297));
+ add(MessageItem(0x100D, 0x43807801));
+ add(MessageItem(0x4805, 2));
+ return true;
+ case 0x004B4230:
+ add(MessageItem(0x4800, 370));
+ add(MessageItem(0x100D, 0x46C26A01));
+ return true;
+ case 0x004B4240:
+ add(MessageItem(0x4800, 334));
+ add(MessageItem(0x100D, 0x468C7B11));
+ add(MessageItem(0x4805, 1));
+ return true;
+ case 0x004B4258:
+ add(MessageItem(0x4800, 425));
+ add(MessageItem(0x100D, 0x42845B19));
+ add(MessageItem(0x4805, 1));
+ return true;
+ // Scene 1302 rings
+ case 0x004B0888:
+ add(MessageItem(0x4800, 218));
+ add(MessageItem(0x100D, 0x4A845A00));
+ add(MessageItem(0x4805, 1));
+ return true;
+ case 0x004B08A0:
+ add(MessageItem(0x4800, 218 + 32));
+ add(MessageItem(0x100D, 0x43807801));
+ return true;
+ case 0x004B08B0:
+ add(MessageItem(0x4800, 218 + 32 + 32));
+ add(MessageItem(0x100D, 0x46C26A01));
+ add(MessageItem(0x4805, 1));
+ return true;
+ case 0x004B08C8:
+ add(MessageItem(0x4800, 218 + 32 + 32 + 32));
+ add(MessageItem(0x100D, 0x468C7B11));
+ return true;
+ case 0x004B08D8:
+ add(MessageItem(0x4800, 218 + 32 + 32 + 32 + 32));
+ add(MessageItem(0x100D, 0x42845B19));
+ add(MessageItem(0x4805, 4));
+ return true;
+ }
+ return false;
+ }
+
+};
+
+class NavigationList : public StaticDataList<NavigationItem> {
+};
+
+template<class LISTCLASS>
+class StaticDataListVector {
+public:
+ std::vector<LISTCLASS*> lists;
+
+ void add(LISTCLASS *list) {
+ lists.push_back(list);
+ }
+
+ void loadListVector(const uint32 *offsets) {
+ for (int i = 0; offsets[i] != 0; i += 2) {
+ LISTCLASS *list = new LISTCLASS();
+ list->loadList(offsets[i], offsets[i + 1]);
+ bool doAppend = true;
+ // Bad
+ for (typename std::vector<LISTCLASS*>::iterator it = lists.begin(); it != lists.end(); it++) {
+ if ((*it)->id == list->id) {
+ doAppend = false;
+ break;
+ }
+ }
+ if (doAppend)
+ lists.push_back(list);
+ }
+ }
+
+ void saveListVector(FILE *fd) {
+ writeUint32LE(fd, lists.size());
+ for (typename std::vector<LISTCLASS*>::iterator it = lists.begin(); it != lists.end(); it++) {
+ (*it)->saveList(fd);
+ }
+ }
+
+};
+
+template<class ITEMCLASS>
+class StaticDataVector {
+public:
+ std::vector<ITEMCLASS> items;
+
+ void loadVector(const uint32 *offsets) {
+ for (int i = 0; offsets[i] != 0; i++) {
+ ITEMCLASS item;
+ item.load(offsets[i]);
+ items.push_back(item);
+ }
+ }
+
+ void saveVector(FILE *fd) {
+ writeUint32LE(fd, items.size());
+ for (typename std::vector<ITEMCLASS>::iterator it = items.begin(); it != items.end(); it++) {
+ (*it).save(fd);
+ }
+ }
+
+};
+
+StaticDataListVector<HitRectList> hitRectLists;
+StaticDataListVector<RectList> rectLists;
+StaticDataListVector<MessageList> messageLists;
+StaticDataListVector<NavigationList> navigationLists;
+StaticDataVector<SceneInfo140Item> sceneInfo140Items;
+StaticDataVector<SceneInfo2700Item> sceneInfo2700Items;
+
+void addMessageList(uint32 messageListCount, uint32 messageListOffset) {
+ MessageList *messageList = new MessageList();
+ messageList->loadList(messageListCount, messageListOffset);
+ messageLists.add(messageList);
+}
+
+int main(int argc, char *argv[]) {
+
+ FILE *datFile;
+
+ loadExe("nhc.exe");
+
+ hitRectLists.loadListVector(hitRectListOffsets);
+ rectLists.loadListVector(rectListOffsets);
+ messageLists.loadListVector(messageListOffsets);
+ navigationLists.loadListVector(navigationListOffsets);
+ sceneInfo140Items.loadVector(sceneInfo140Offsets);
+ sceneInfo2700Items.loadVector(sceneInfo2700Offsets);
+
+ datFile = fopen("neverhood.dat", "wb");
+
+ writeUint32LE(datFile, 0x11223344); // Some magic
+ writeUint32LE(datFile, DAT_VERSION);
+
+ messageLists.saveListVector(datFile);
+ rectLists.saveListVector(datFile);
+ hitRectLists.saveListVector(datFile);
+ navigationLists.saveListVector(datFile);
+ sceneInfo140Items.saveVector(datFile);
+ sceneInfo2700Items.saveVector(datFile);
+
+ fclose(datFile);
+
+ printf("Done.\n");
+
+ return 0;
+}
diff --git a/devtools/create_neverhood/create_neverhood.h b/devtools/create_neverhood/create_neverhood.h
new file mode 100644
index 0000000000..6382c87375
--- /dev/null
+++ b/devtools/create_neverhood/create_neverhood.h
@@ -0,0 +1,28 @@
+/* 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 CREATE_NEVERHOOD_H
+#define CREATE_NEVERHOOD_H
+
+#include "util.h"
+
+#endif
diff --git a/devtools/create_neverhood/module.mk b/devtools/create_neverhood/module.mk
new file mode 100644
index 0000000000..284e19105d
--- /dev/null
+++ b/devtools/create_neverhood/module.mk
@@ -0,0 +1,12 @@
+
+MODULE := devtools/create_neverhood
+
+MODULE_OBJS := \
+ create_neverhood.o \
+ util.o
+
+# Set the name of the executable
+TOOL_EXECUTABLE := create_neverhood
+
+# Include common rules
+include $(srcdir)/rules.mk
diff --git a/devtools/create_neverhood/tables.h b/devtools/create_neverhood/tables.h
new file mode 100644
index 0000000000..ea39aa807d
--- /dev/null
+++ b/devtools/create_neverhood/tables.h
@@ -0,0 +1,718 @@
+/* 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.
+ *
+ */
+
+static const uint32 hitRectListOffsets[] = {
+ // Scene1001
+ 1, 0x004B4860,
+ // Scene1002
+ 1, 0x004B4138,
+ // Scene1201
+ 4, 0x004AEBD0,
+ // Scene1302
+ 1, 0x004B0858,
+ // Scene1705
+ 1, 0x004B69D8,
+ // Scene2203
+ 1, 0x004B8320,
+ // Scene2205
+ 1, 0x004B0620,
+ 0, 0
+};
+
+static const uint32 rectListOffsets[] = {
+ // Scene1001
+ 1, 0x004B49F0,
+ 1, 0x004B4A00,
+ // Scene1002
+ 3, 0x004B43A0,
+ 1, 0x004B4418,
+ 3, 0x004B43A0,
+ // Scene1004
+ 1, 0x004B7C70,
+ // Scene1109
+ 1, 0x004B63A8,
+ 1, 0x004B6398,
+ // Scene1201
+ 1, 0x004AEE58,
+ 1, 0x004AEDC8,
+ 1, 0x004AEE18,
+ 1, 0x004AED88,
+ // Scene1302
+ 2, 0x004B0A38,
+ // Scene1303
+ 1, 0x004AF9E8,
+ // Scene1304
+ 1, 0x004B91A8,
+ // Scene1305
+ 1, 0x004B6E98,
+ // Scene1306
+ 1, 0x004AFD28,
+ 1, 0x004AFD18,
+ // Scene1308
+ 1, 0x004B5990,
+ 1, 0x004B5980,
+ 1, 0x004B59A0,
+ // Scene1401
+ 1, 0x004B6758,
+ // Scene1402
+ 1, 0x004B0C48,
+ 1, 0x004B0C98,
+ // Scene1403
+ 1, 0x004B1FF8,
+ 1, 0x004B2008,
+ // Scene1404
+ 1, 0x004B8D80,
+ // Scene1608
+ 1, 0x004B47D0,
+ 1, 0x004B4810,
+ // Scene1705
+ 1, 0x004B6B40,
+ 1, 0x004B6B30,
+ // Scene1901
+ 1, 0x004B34C8,
+ // Scene2001
+ 1, 0x004B3680,
+ 1, 0x004B3670,
+ // Scene2101
+ 1, 0x004B9008,
+ 1, 0x004B8FF8,
+ // Scene2203
+ 1, 0x004B8420,
+ // Scene2206
+ 1, 0x004B8AF8,
+ 1, 0x004B8B58,
+ // Scene2207
+ 3, 0x004B38B8,
+ 1, 0x004B3948,
+ // Scene2242
+ 1, 0x004B3DC8,
+ 1, 0x004B3E18,
+ // HallOfRecordsScene
+ 1, 0x004B2BF8,
+ 1, 0x004B2BB8,
+ // Scene2247
+ 1, 0x004B5588,
+ 1, 0x004B55C8,
+ // Scene2401
+ 1, 0x004B3140,
+ // Scene2402
+ 1, 0x004AF900,
+ // Scene2403
+ 1, 0x004B5E18,
+ 1, 0x004B5E28,
+ // Scene2406
+ 1, 0x004B78C8,
+ 1, 0x004B78D8,
+ // Scene2501
+ 1, 0x004B2608,
+ // Scene2732
+ 1, 0x004AE360,
+ // Scene2801
+ 1, 0x004B6CE0,
+ 1, 0x004B6CD0,
+ 1, 0x004B6CF0,
+ // Scene2805
+ 1, 0x004AE318,
+ 1, 0x004AE308,
+ // Scene2810
+ 1, 0x004AE810,
+ 1, 0x004AE800,
+ // Scene2812
+ 1, 0x004AF700,
+ 1, 0x004AF710,
+ 0, 0
+};
+
+static const uint32 messageListOffsets[] = {
+ // Scene1001
+ 1, 0x004B4888,
+ 2, 0x004B4898,
+ 1, 0x004B4970,
+ 1, 0x004B4890,
+ 3, 0x004B4910,
+ 5, 0x004B4938,
+ 1, 0x004B4960,
+ 4, 0x004B48A8,
+ 3, 0x004B48C8,
+ // Scene1002
+ 1, 0x004B4270,
+ 1, 0x004B4478,
+ 3, 0x004B4298,
+ 1, 0x004B4470,
+ 4, 0x004B4428,
+ 5, 0x004B4448,
+ 1, 0x004B44B8,
+ 2, 0x004B44A8,
+ 1, 0x004B44A0,
+ 2, 0x004B43D0,
+ 4, 0x004B4480,
+ 2, 0x004B41E0,
+ 5, 0x004B4148,
+ // Scene1004
+ 3, 0x004B7BF0,
+ 2, 0x004B7C08,
+ 1, 0x004B7C18,
+ 2, 0x004B7C20,
+ // Scene1109
+ 1, 0x004B6260,
+ 2, 0x004B6268,
+ 4, 0x004B6318,
+ 2, 0x004B6278,
+ 1, 0x004B6258,
+ // Scene1201
+ 1, 0x004AEC08,
+ 2, 0x004AEC10,
+ 2, 0x004AEC20,
+ 2, 0x004AEC30,
+ 4, 0x004AEC90,
+ 2, 0x004AECB0,
+ 2, 0x004AECC0,
+ 5, 0x004AECF0,
+ 2, 0x004AECD0,
+ 2, 0x004AECE0,
+ 2, 0x004AED38,
+ // Scene1302
+ 4, 0x004B08F0,
+ 3, 0x004B0920,
+ 1, 0x004B0950,
+ 2, 0x004B0940,
+ 1, 0x004B0938,
+ 2, 0x004B0910,
+ 1, 0x004B0968,
+ 2, 0x004B0878,
+ 4, 0x004B0978,
+ 1, 0x004B0870,
+ 1, 0x004B0868,
+ // Scene1303
+ 1, 0x004AF9A0,
+ 2, 0x004AF9B8,
+ // Scene1304
+ 1, 0x004B90E8,
+ 1, 0x004B90F0,
+ 2, 0x004B9158,
+ 2, 0x004B9130,
+ 2, 0x004B9140,
+ // Scene1305
+ 1, 0x004B6E40,
+ 1, 0x004B6E48,
+ // Scene1306
+ 1, 0x004AFAD0,
+ 2, 0x004AFAF0,
+ 1, 0x004AFBC8,
+ 1, 0x004AFC30,
+ 4, 0x004AFC38,
+ 2, 0x004AFB00,
+ 1, 0x004AFBD0,
+ 4, 0x004AFBD8,
+ 2, 0x004AFAE0,
+ 1, 0x004AFAD8,
+ 2, 0x004AFC58,
+ 2, 0x004AFC68,
+ // Scene1308
+ 1, 0x004B57C0,
+ 1, 0x004B57C8,
+ 1, 0x004B58B0,
+ 3, 0x004B57D0,
+ 3, 0x004B57E8,
+ 2, 0x004B5868,
+ 4, 0x004B5848,
+ 3, 0x004B5830,
+ 2, 0x004B5800,
+ 2, 0x004B5868,
+ 2, 0x004B58E0,
+ // Scene1401
+ 1, 0x004B65C8,
+ 1, 0x004B65D0,
+ 1, 0x004B65D8,
+ 1, 0x004B65E8,
+ 3, 0x004B6670,
+ 4, 0x004B6690,
+ 1, 0x004B66B0,
+ 3, 0x004B6658,
+ 2, 0x004B65F0,
+ // Scene1402
+ 1, 0x004B0B48,
+ 1, 0x004B0B50,
+ 1, 0x004B0B58,
+ 1, 0x004B0B60,
+ 2, 0x004B0B68,
+ 3, 0x004B0BB8,
+ 3, 0x004B0BD0,
+ // Scene1403
+ 1, 0x004B1F18,
+ 1, 0x004B1F20,
+ 3, 0x004B1F70,
+ 2, 0x004B1FA8,
+ 4, 0x004B1F88,
+ 3, 0x004B1F58,
+ 2, 0x004B1F28,
+ 2, 0x004B1FB8,
+ // Scene1404
+ 1, 0x004B8C28,
+ 1, 0x004B8C30,
+ 1, 0x004B8C38,
+ 1, 0x004B8D28,
+ 3, 0x004B8CB8,
+ 2, 0x004B8C40,
+ 6, 0x004B8CE8,
+ 3, 0x004B8CA0,
+ 2, 0x004B8CD0,
+ 2, 0x004B8D18,
+ // Scene1608
+ 1, 0x004B46A8,
+ 1, 0x004B46B0,
+ 1, 0x004B47A8,
+ 3, 0x004B4748,
+ 2, 0x004B4770,
+ 2, 0x004B46C8,
+ 2, 0x004B4760,
+ // Scene1705
+ 1, 0x004B69E8,
+ 2, 0x004B6A08,
+ 4, 0x004B6AA0,
+ 2, 0x004B6A18,
+ 1, 0x004B69F0,
+ 2, 0x004B6AC0,
+ // Scene1901
+ 1, 0x004B3408,
+ 1, 0x004B3410,
+ 1, 0x004B3400,
+ // Scene2001
+ 1, 0x004B3538,
+ 2, 0x004B3540,
+ 4, 0x004B35F0,
+ 2, 0x004B3550,
+ 1, 0x004B3530,
+ // Scene2101
+ 1, 0x004B8E48,
+ 3, 0x004B8E50,
+ 4, 0x004B8F58,
+ 2, 0x004B8EB0,
+ 2, 0x004B8EA0,
+ 1, 0x004B8F50,
+ 1, 0x004B8F48,
+ 4, 0x004B8E80,
+ 1, 0x004B8EC8,
+ 2, 0x004B8F78,
+ 3, 0x004B8F00,
+ // Scene2201
+ 1, 0x004B8118,
+ 1, 0x004B8130,
+ 1, 0x004B8178,
+ 2, 0x004B8120,
+ 3, 0x004B81A0,
+ 1, 0x004B81B8,
+ 2, 0x004B8108,
+ 5, 0x004B8150,
+ 4, 0x004B8180,
+ 3, 0x004B8138,
+ 2, 0x004B8108,
+ 2, 0x004B81C8,
+ // Scene2203
+ 1, 0x004B8340,
+ 1, 0x004B8350,
+ 1, 0x004B8358,
+ 1, 0x004B8348,
+ 3, 0x004B83B0,
+ 3, 0x004B83C8,
+ 2, 0x004B8370,
+ 2, 0x004B8360,
+ 2, 0x004B83E0,
+ 2, 0x004B83F0,
+ // Scene2205
+ 1, 0x004B0658,
+ 2, 0x004B0648,
+ 1, 0x004B0640,
+ 4, 0x004B0690,
+ 2, 0x004B0630,
+ // Scene2206
+ 1, 0x004B88A8,
+ 2, 0x004B88B8,
+ 1, 0x004B88C8,
+ 1, 0x004B8A70,
+ 1, 0x004B88B0,
+ 5, 0x004B8948,
+ 2, 0x004B8970,
+ 2, 0x004B8988,
+ 4, 0x004B8998,
+ 4, 0x004B89B8,
+ 4, 0x004B89D8,
+ 5, 0x004B89F8,
+ 5, 0x004B8A20,
+ 5, 0x004B8A48,
+ // Scene2207
+ 1, 0x004B38E8,
+ 4, 0x004B38F0,
+ 2, 0x004B37D8,
+ 2, 0x004B3958,
+ 3, 0x004B3920,
+ // Scene2242
+ 1, 0x004B3C18,
+ 1, 0x004B3D60,
+ 1, 0x004B3D48,
+ 1, 0x004B3C20,
+ 2, 0x004B3D50,
+ 5, 0x004B3CF8,
+ 5, 0x004B3D20,
+ 4, 0x004B3CB8,
+ 4, 0x004B3CD8,
+ // HallOfRecordsScene
+ 1, 0x004B2900,
+ 2, 0x004B2910,
+ 1, 0x004B2B70,
+ 1, 0x004B2908,
+ 2, 0x004B2920,
+ 4, 0x004B2978,
+ 4, 0x004B2998,
+ 4, 0x004B29B8,
+ 4, 0x004B29D8,
+ 4, 0x004B29F8,
+ 4, 0x004B2A18,
+ 4, 0x004B2A38,
+ 5, 0x004B2A58,
+ 5, 0x004B2A80,
+ 5, 0x004B2AA8,
+ 5, 0x004B2AD0,
+ 5, 0x004B2AF8,
+ 5, 0x004B2B20,
+ 5, 0x004B2B48,
+ // Scene2247
+ 1, 0x004B5428,
+ 2, 0x004B5438,
+ 1, 0x004B5530,
+ 1, 0x004B5430,
+ 4, 0x004B54A0,
+ 4, 0x004B54C0,
+ 5, 0x004B54E0,
+ 5, 0x004B5508,
+ // Scene2401
+ 1, 0x004B2F70,
+ 1, 0x004B2F80,
+ 1, 0x004B2F78,
+ 4, 0x004B3090,
+ 2, 0x004B30B0,
+ 6, 0x004B3020,
+ 2, 0x004B3050,
+ 4, 0x004B2FA8,
+ 4, 0x004B2FC8,
+ // Scene2402
+ 1, 0x004AF7C8,
+ 2, 0x004AF7D8,
+ 1, 0x004AF888,
+ 1, 0x004AF7D0,
+ 3, 0x004AF800,
+ 1, 0x004AF818,
+ 2, 0x004AF890,
+ // Scene2403
+ 1, 0x004B5C98,
+ 1, 0x004B5D70,
+ 4, 0x004B5CA0,
+ 2, 0x004B5D98,
+ // Scene2406
+ 1, 0x004B76C8,
+ 3, 0x004B76D8,
+ 1, 0x004B77C0,
+ 1, 0x004B7810,
+ 1, 0x004B76D0,
+ 2, 0x004B77C8,
+ 2, 0x004B77D8,
+ 2, 0x004B7758,
+ 4, 0x004B7738,
+ // Scene2501
+ 7, 0x004B2538,
+ 6, 0x004B2570,
+ // Scene2732
+ 1, 0x004AE328,
+ // Scene2801
+ 1, 0x004B6BB8,
+ 1, 0x004B6BC0,
+ 1, 0x004B6C10,
+ 1, 0x004B6BB0,
+ 2, 0x004B6C40,
+ // Scene2803b
+ 1, 0x004B60D8,
+ 1, 0x004B6100,
+ 1, 0x004B60F8,
+ 1, 0x004B6100,
+ 3, 0x004B6138,
+ 3, 0x004B60E0,
+ 3, 0x004B6180,
+ 1, 0x004B6198,
+ 6, 0x004B6108,
+ 3, 0x004B6150,
+ 3, 0x004B6168,
+ 1, 0x004B61A0,
+ 5, 0x004B61A8,
+ // Scene2803
+ 1, 0x004B79F0,
+ 5, 0x004B79C8,
+ 1, 0x004B7A00,
+ 2, 0x004B7A78,
+ 1, 0x004B79F8,
+ 1, 0x004B79C0,
+ 1, 0x004B7A50,
+ 2, 0x004B7A58,
+ 5, 0x004B7A08,
+ 4, 0x004B7A30,
+ 2, 0x004B7A68,
+ 7, 0x004B7A88,
+ // Scene2805
+ 1, 0x004AE1C8,
+ 2, 0x004AE1D0,
+ 4, 0x004AE288,
+ 2, 0x004AE1E0,
+ 1, 0x004AE1C0,
+ // Scene2806
+ 1, 0x004AF098,
+ 1, 0x004AF098,
+ 3, 0x004AF0C8,
+ 5, 0x004AF0A0,
+ 1, 0x004AF090,
+ 2, 0x004AF0E0,
+ // Scene2809
+ 1, 0x004B5B90,
+ 3, 0x004B5BD0,
+ 5, 0x004B5BA8,
+ 1, 0x004B5B88,
+ 2, 0x004B5B98,
+ 1, 0x004AE438,
+ 3, 0x004AE440,
+ 3, 0x004AE738,
+ 1, 0x004AE6D8,
+ 2, 0x004AE6E8,
+ 1, 0x004AE6E0,
+ 2, 0x004AE428,
+ 2, 0x004AE418,
+ 1, 0x004AE410,
+ 4, 0x004AE458,
+ 5, 0x004AE4A8,
+ 5, 0x004AE4D0,
+ 5, 0x004AE4F8,
+ 5, 0x004AE520,
+ 5, 0x004AE548,
+ 5, 0x004AE570,
+ 5, 0x004AE598,
+ 5, 0x004AE5C0,
+ 5, 0x004AE5E8,
+ 5, 0x004AE610,
+ 5, 0x004AE638,
+ 5, 0x004AE660,
+ 5, 0x004AE688,
+ 2, 0x004AE750,
+ // Scene2812
+ 1, 0x004AF560,
+ 1, 0x004AF588,
+ 1, 0x004AF5F0,
+ 4, 0x004AF568,
+ 2, 0x004AF658,
+ 2, 0x004AF668,
+ 0, 0
+};
+
+static const uint32 navigationListOffsets[] = {
+ // Module1100
+ 2, 0x004B8430,
+ 2, 0x004B8460,
+ 4, 0x004B84F0,
+ 4, 0x004B8490,
+ 2, 0x004B8580,
+ 2, 0x004B8550,
+ // Module1300
+ 6, 0x004B2718,
+ 2, 0x004B27A8,
+ 2, 0x004B27D8,
+ 2, 0x004B2808,
+ 2, 0x004B2838,
+ // Module1600
+ 4, 0x004B39D0,
+ 2, 0x004B3A30,
+ 2, 0x004B3A60,
+ 6, 0x004B3A90,
+ 2, 0x004B3B20,
+ 2, 0x004B3B50,
+ 2, 0x004B3B80,
+ // Module1700
+ 2, 0x004AE8B8,
+ 3, 0x004AE8E8,
+ // Module1800
+ 4, 0x004AFD38,
+ 1, 0x004AFD98,
+ 2, 0x004AFDB0,
+ 4, 0x004AFDE0,
+ 2, 0x004AFE40,
+ // Module2300
+ 2, 0x004B67B8,
+ 6, 0x004B67E8,
+ 2, 0x004B6878,
+ 3, 0x004B68F0,
+ 3, 0x004B68A8,
+ // Module2000
+ 3, 0x004B7B48,
+ 3, 0x004B7B00,
+ // Module2600
+ 2, 0x004B8608,
+ 4, 0x004B8638,
+ 2, 0x004B8698,
+ 2, 0x004B86C8,
+ 4, 0x004B8758,
+ 4, 0x004B86F8,
+ 2, 0x004B87B8,
+ // Module3000
+ 2, 0x004B7C80,
+ 2, 0x004B7CE0,
+ 2, 0x004B7CB0,
+ 3, 0x004B7D58,
+ 3, 0x004B7D10,
+ 4, 0x004B7E60,
+ 4, 0x004B7DA0,
+ 4, 0x004B7E00,
+ 4, 0x004B7F20,
+ 4, 0x004B7EC0,
+ 2, 0x004B7F80,
+ 1, 0x004B7FB0,
+ 0, 0
+};
+
+// Hall of Records scene definitions
+
+static const uint32 sceneInfo140Offsets[] = {
+ 0x004B7180,
+ 0x004B7198,
+ 0x004B71B0,
+ 0x004B71C8,
+ 0x004B71E0,
+ 0x004B71F8,
+ 0x004B7210,
+ 0x004B7228,
+ 0x004B7240,
+ 0x004B7258,
+ 0x004B7270,
+ 0x004B7288,
+ 0x004B72A0,
+ 0x004B72B8,
+ 0x004B72D0,
+ 0x004B72E8,
+ 0x004B7300,
+ 0x004B7318,
+ 0x004B7330,
+ 0x004B7348,
+ 0x004B7360,
+ 0x004B7378,
+ 0x004B7390,
+ 0x004B73A8,
+ 0x004B73C0,
+ 0x004B73D8,
+ 0x004B73F0,
+ 0x004B7408,
+ 0x004B7420,
+ 0x004B7438,
+ 0x004B7450,
+ 0x004B7468,
+ 0x004B7480,
+ 0x004B7498,
+ 0x004B74B0,
+ 0x004B74C8,
+ 0
+};
+
+static const uint32 sceneInfo2700Offsets[] = {
+ //
+ 0x004B1710,
+ 0x004B1738,
+ 0x004B1760,
+ 0x004B1788,
+ 0x004B17B0,
+ 0x004B17D8,
+ 0x004B1800,
+ 0x004B1828,
+ 0x004B1850,
+ 0x004B1878,
+ 0x004B18A0,
+ 0x004B18C8,
+ 0x004B18F0,
+ 0x004B1918,
+ //
+ 0x004B19E0,
+ 0x004B1A08,
+ 0x004B1A30,
+ 0x004B1A58,
+ 0x004B1A80,
+ 0x004B1AA8,
+ 0x004B1AD0,
+ 0x004B1AF8,
+ 0x004B1B20,
+ 0x004B1B48,
+ 0x004B1B70,
+ 0x004B1B98,
+ 0x004B1BC0,
+ 0x004B1BE8,
+ 0x004B1C10,
+ 0x004B1C38,
+ 0x004B1C60,
+ 0x004B1C88,
+ 0x004B1CB0,
+ 0x004B1CD8,
+ 0x004B1D00,
+ 0x004B1D28,
+ 0x004B1D50,
+ 0x004B1D78,
+ //
+ 0x004B1DB0,
+ //
+ 0x004B1DE8,
+ 0x004B1E10,
+ 0x004B1E38,
+ 0x004B1E60,
+ //
+ 0x004B1950,
+ //
+ 0x004B2240,
+ //
+ 0x004B5F68,
+ 0x004B5F8C,
+ 0x004B5FB0,
+ 0x004B5FD8,
+ 0x004B5FFC,
+ 0x004B6020,
+ // Scene2501
+ 0x004B2628,
+ 0x004B264C,
+ 0x004B2670,
+ // Scene2502
+ 0x004B01B8,
+ // Scene2503
+ 0x004B01E0,
+ 0x004B0208,
+ // Scene2505
+ 0x004B0230,
+ // Scene2506
+ 0x004B0268,
+ // Scene2507
+ 0x004B02A0,
+ // Scene2508
+ 0x004B02C8,
+ // Scene2706
+ 0x004B22A0,
+ 0x004B22C4,
+ 0x004B22E8,
+ 0
+};
diff --git a/devtools/create_neverhood/util.cpp b/devtools/create_neverhood/util.cpp
new file mode 100644
index 0000000000..5ce8237b85
--- /dev/null
+++ b/devtools/create_neverhood/util.cpp
@@ -0,0 +1,152 @@
+/* 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.
+ *
+ */
+
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include "util.h"
+#include <stdarg.h>
+
+#ifdef _MSC_VER
+ #define vsnprintf _vsnprintf
+#endif
+
+void error(const char *s, ...) {
+ char buf[1024];
+ va_list va;
+
+ va_start(va, s);
+ vsnprintf(buf, 1024, s, va);
+ va_end(va);
+
+ fprintf(stderr, "ERROR: %s!\n", buf);
+
+ exit(1);
+}
+
+void warning(const char *s, ...) {
+ char buf[1024];
+ va_list va;
+
+ va_start(va, s);
+ vsnprintf(buf, 1024, s, va);
+ va_end(va);
+
+ fprintf(stderr, "WARNING: %s!\n", buf);
+}
+
+int scumm_stricmp(const char *s1, const char *s2) {
+ byte l1, l2;
+ do {
+ // Don't use ++ inside tolower, in case the macro uses its
+ // arguments more than once.
+ l1 = (byte)*s1++;
+ l1 = tolower(l1);
+ l2 = (byte)*s2++;
+ l2 = tolower(l2);
+ } while (l1 == l2 && l1 != 0);
+ return l1 - l2;
+}
+
+void debug(int level, const char *s, ...) {
+ char buf[1024];
+ va_list va;
+
+ va_start(va, s);
+ vsnprintf(buf, 1024, s, va);
+ va_end(va);
+
+ fprintf(stderr, "DEBUG: %s!\n", buf);
+}
+
+uint8 readByte(FILE *fp) {
+ return fgetc(fp);
+}
+
+uint16 readUint16BE(FILE *fp) {
+ uint16 ret = 0;
+ ret |= fgetc(fp) << 8;
+ ret |= fgetc(fp);
+ return ret;
+}
+
+uint16 readUint16LE(FILE *fp) {
+ uint16 ret = 0;
+ ret |= fgetc(fp);
+ ret |= fgetc(fp) << 8;
+ return ret;
+}
+
+uint32 readUint32BE(FILE *fp) {
+ uint32 ret = 0;
+ ret |= fgetc(fp) << 24;
+ ret |= fgetc(fp) << 16;
+ ret |= fgetc(fp) << 8;
+ ret |= fgetc(fp);
+ return ret;
+}
+
+uint32 readUint32LE(FILE *fp) {
+ uint32 ret = 0;
+ ret |= fgetc(fp);
+ ret |= fgetc(fp) << 8;
+ ret |= fgetc(fp) << 16;
+ ret |= fgetc(fp) << 24;
+ return ret;
+}
+
+void writeByte(FILE *fp, uint8 b) {
+ fwrite(&b, 1, 1, fp);
+}
+
+void writeUint16BE(FILE *fp, uint16 value) {
+ writeByte(fp, (uint8)(value >> 8));
+ writeByte(fp, (uint8)(value));
+}
+
+void writeUint16LE(FILE *fp, uint16 value) {
+ writeByte(fp, (uint8)(value));
+ writeByte(fp, (uint8)(value >> 8));
+}
+
+void writeUint32BE(FILE *fp, uint32 value) {
+ writeByte(fp, (uint8)(value >> 24));
+ writeByte(fp, (uint8)(value >> 16));
+ writeByte(fp, (uint8)(value >> 8));
+ writeByte(fp, (uint8)(value));
+}
+
+void writeUint32LE(FILE *fp, uint32 value) {
+ writeByte(fp, (uint8)(value));
+ writeByte(fp, (uint8)(value >> 8));
+ writeByte(fp, (uint8)(value >> 16));
+ writeByte(fp, (uint8)(value >> 24));
+}
+
+uint32 fileSize(FILE *fp) {
+ uint32 sz;
+ uint32 pos = ftell(fp);
+ fseek(fp, 0, SEEK_END);
+ sz = ftell(fp);
+ fseek(fp, pos, SEEK_SET);
+ return sz;
+}
diff --git a/devtools/create_neverhood/util.h b/devtools/create_neverhood/util.h
new file mode 100644
index 0000000000..a2783cca71
--- /dev/null
+++ b/devtools/create_neverhood/util.h
@@ -0,0 +1,57 @@
+/* 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 UTIL_H
+#define UTIL_H
+
+#include "common/scummsys.h"
+#include "common/endian.h"
+#include "common/util.h"
+
+#ifdef WIN32
+#include <io.h>
+#include <process.h>
+#endif
+
+
+/* File I/O */
+uint8 readByte(FILE *fp);
+uint16 readUint16BE(FILE *fp);
+uint16 readUint16LE(FILE *fp);
+uint32 readUint32BE(FILE *fp);
+uint32 readUint32LE(FILE *fp);
+void writeByte(FILE *fp, uint8 b);
+void writeUint16BE(FILE *fp, uint16 value);
+void writeUint16LE(FILE *fp, uint16 value);
+void writeUint32BE(FILE *fp, uint32 value);
+void writeUint32LE(FILE *fp, uint32 value);
+uint32 fileSize(FILE *fp);
+
+/* Misc stuff */
+void NORETURN_PRE error(const char *s, ...) NORETURN_POST;
+void warning(const char *s, ...);
+void debug(int level, const char *s, ...);
+int scumm_stricmp(const char *s1, const char *s2);
+
+using namespace Common;
+
+#endif
diff --git a/dists/engine-data/neverhood.dat b/dists/engine-data/neverhood.dat
new file mode 100644
index 0000000000..dc95c00965
--- /dev/null
+++ b/dists/engine-data/neverhood.dat
Binary files differ
diff --git a/dists/scummvm.rc b/dists/scummvm.rc
index c457e23b16..56848be9e9 100644
--- a/dists/scummvm.rc
+++ b/dists/scummvm.rc
@@ -32,6 +32,9 @@ kyra.dat FILE "dists/engine-data/kyra.dat"
#if ENABLE_LURE == STATIC_PLUGIN
lure.dat FILE "dists/engine-data/lure.dat"
#endif
+#if ENABLE_NEVERHOOD == STATIC_PLUGIN
+neverhood.dat FILE "dists/engine-data/neverhood.dat"
+#endif
#if ENABLE_QUEEN == STATIC_PLUGIN
queen.tbl FILE "dists/engine-data/queen.tbl"
#endif
diff --git a/dists/scummvm.rc.in b/dists/scummvm.rc.in
index f986cc52e8..4bbfb47116 100644
--- a/dists/scummvm.rc.in
+++ b/dists/scummvm.rc.in
@@ -32,6 +32,9 @@ kyra.dat FILE "dists/engine-data/kyra.dat"
#if ENABLE_LURE == STATIC_PLUGIN
lure.dat FILE "dists/engine-data/lure.dat"
#endif
+#if ENABLE_NEVERHOOD == STATIC_PLUGIN
+neverhood.dat FILE "dists/engine-data/neverhood.dat"
+#endif
#if ENABLE_QUEEN == STATIC_PLUGIN
queen.tbl FILE "dists/engine-data/queen.tbl"
#endif
diff --git a/doc/se/LasMig b/doc/se/LasMig
index 9ac79b4c35..d47918d1f4 100644
--- a/doc/se/LasMig
+++ b/doc/se/LasMig
@@ -214,10 +214,15 @@ MADE-spel från Activision:
Discworld 2: Missing Presumed ...!? [dw2]
Dragon History [draci]
Drascula: The Vampire Strikes Back [drascula]
+ Eye of the Beholder [eob]
+ Eye of the Beholder II: The Legend of
+ Darkmoon [eob2]
Flight of the Amazon Queen [queen]
Future Wars [fw]
Inherit the Earth: Quest for the Orb [ite]
Nippon Safes Inc. [nippon]
+ Lands of Lore: The Throne of Chaos [lol]
+ The Journeyman Project: Pegasus Prime [pegasus]
The Legend of Kyrandia [kyra1]
The Legend of Kyrandia: The Hand of Fate [kyra2]
The Legend of Kyrandia: Malcolm's Revenge [kyra3]
@@ -317,21 +322,24 @@ I vissa fall visar ScummVM fortfarande skärmen för kopieringsskydd. Försök a
ScummVM skippar kopieringsskyddet för följande spel:
- * Maniac Mansion
- * Zak McKracken and the Alien Mindbenders
- * Loom (EGA)
- * The Secret of Monkey Island (VGA)
- * Monkey Island 2: LeChuck's Revenge
* Beneath a Steel Sky
-- överskridit med tillstånd från Revolution Software.
+ * Dreamweb
+ -- en lista med tillgängliga kommandon i spelets terminaler visas nu
+ när spelaren använder 'help'-kommandot.
* Inherit the Earth: Quest for the Orb (Diskettversionen)
-- överskridit med tillstånd från Wyrmkeep Entertainment,
då det överskridits på alla CD-utgåvor av spelet.
+ * Loom (EGA DOS)
+ * Maniac Mansion
+ * Monkey Island 2: LeChuck's Revenge
* Simon the Sorcerer 1 (Diskettversionen)
* Simon the Sorcerer 2 (Diskettversionen)
-- överskridit med tillstånd från Adventure Soft,
då det överskridits på alla CD-utgåvor av spelet.
+ * The Secret of Monkey Island (VGA)
* Waxworks
+ * Zak McKracken and the Alien Mindbenders
3.2) Notiser om Commodore64-spel:
@@ -889,9 +897,9 @@ ScummVM stöder ett antal kortkommandon medan du spelar. De är olika för SCUMM
- och + - Texthastighet långsammare/snabbare
F5 - Visar en ladda/spara-ruta
Alt-F5 - Visar spelets originalruta för att ladda/spara,
- förutsatt att det finns en. Du kan ladda och spara
- på det här viset, men det rekommenderas ej då det
- kan orsaka krascher.
+ förutsatt att det finns en. Du kan ladda och
+ spara på det här viset, men det
+ rekommenderas ej då det kan orsaka krascher.
i - Visar IQ-poäng (Indiana Jones and the Last
Crusade och Indiana Jones and the Fate of
Atlantis)
@@ -1512,7 +1520,8 @@ Ett exempel på en konfigurationsfil ser ut så här:
Följande nyckelord kan användas:
path string Sökvägen dit spelets datafiler ligger
- autosave_period number Antal sekunder mellan autosparningar (standard: 300)
+ autosave_period number Antal sekunder mellan autosparningar
+ (standard: 300)
save_slot number Positionsnumret för spardata att ladda vid
uppstart.
savepath string Sökvägen där spelet lagrar sina spardata.
@@ -1527,24 +1536,29 @@ Följande nyckelord kan användas:
language string Bestäm språk (en, us, de, fr, it, pt, es,
jp, zh, kr, se, gb, hb, cz, ru)
- speech_mute bool Ställ in till “true†för att stänga av röster
- subtitles bool Ställ in till “true†för att visa undertexter
- talkspeed number Textfördröjning i SCUMM-spel, eller texthastighet
- i andra spel.
+ speech_mute bool Ställ in till “true†för att
+ stänga av röster
+ subtitles bool Ställ in till “true†för att
+ visa undertexter
+ talkspeed number Textfördröjning i SCUMM-spel, eller
+ texthastighet i andra spel.
fullscreen bool Fullskärmsläge
aspect_ratio bool Aktivera korrektion av bildförhållande
gfx_mode string Grafikläge (normalt, 2x, 3x, 2xsai,
- super2xsai, supereagle, advmame2x, advmame3x,
- hq2x, hq3x, tv2x, dotmatrix)
-
- confirm_exit bool Be om bekräftelse från användaren innan avslutnig
- (endast SDL-back-end).
- console bool Öppna konsolfönstret (standard: på) (endast Windows)
- cdrom number Nummer på CD-ROM enhet för använding av ljud.
- Försök inte använda CD-ROM enheten om detta numret
- är negativt.
- joystick_num number Nummer på joystick-enhet att använda för input
+ super2xsai, supereagle, advmame2x,
+ advmame3x, hq2x, hq3x, tv2x, dotmatrix)
+
+ confirm_exit bool Be om bekräftelse från användaren innan
+ avslutnig (endast SDL-back-end).
+ console bool Öppna konsolfönstret (standard: på)
+ (endast Windows)
+ cdrom number Nummer på CD-ROM enhet för
+ använding av ljud.
+ Försök inte använda CD-ROM enheten om
+ detta numret är negativt.
+ joystick_num number Nummer på joystick-enhet att använda
+ för input
music_driver string Musikenheten att använda.
opl_driver string AdLib (OPL)-emulatorn att använda.
output_rate number Uppspelningsvärdet i Hz. Lämpliga värden är
@@ -1554,28 +1568,30 @@ Följande nyckelord kan användas:
music_volume number Musikvolym (0-255)
multi_midi bool Ställ in till “true†för att kombinera AdLib
och Native MIDI.
- soundfont string SoundFont att använda för MIDI-uppspelning. (Stöds
- endast av vissa MIDI-drivers.)
+ soundfont string SoundFont att använda för MIDI-uppspelning.
+ (Stöds endast av vissa MIDI-drivers.)
native_mt32 bool Ställ in till “true†för att deaktivera GM-
- emulation och utgå ifrån att en verklig Roland
- MT-32 finns tillhanda.
- enable_gs bool Ställ in till “true†för att aktivera Roland GS-
- specifika funktioner för att förbättra GM-
- emulation. Om native_mt32 även ställts till â€trueâ€
- kommer GS-enheten att välja en MT-32 karta för att
- spela rätt instrument.
+ emulation och utgå ifrån att en verklig
+ Roland MT-32 finns tillhanda.
+ enable_gs bool Ställ in till “true†för att aktivera
+ Roland GS-specifika funktioner för att
+ förbättra GM-emulation. Om native_mt32 även
+ ställts till â€true†sÃ¥ kommer GS-enheten att
+ välja en MT-32 karta för att spela
+ rätt instrument.
sfx_volume number Ljudeffektsvolym (0-255)
tempo number Musiktempo (50-200) (standard: 100)
speech_volume number Röstvolym (0-255)
- midi_gain number MIDI gain (0-1000) (standard: 100) (Stöds endast
- av vissa MIDI-drivers.)
+ midi_gain number MIDI gain (0-1000) (standard: 100)
+ (Stöds endast av vissa MIDI-drivers.)
- copy_protection bool Aktiverar kopieringsskyddet för vissa spel i fall
- där ScummVM automatiskt deaktiverar det.
+ copy_protection bool Aktiverar kopieringsskyddet för vissa spel
+ i fall där ScummVM automatiskt
+ deaktiverar det.
demo_mode bool Öppna demon i Maniac Mansion
- alt_intro bool Använd alternativt intro för CD-versionerna av
- Beneath a Steel Sky och Flight of the Amazon
- Queen
+ alt_intro bool Använd alternativt intro för
+ CD-versionerna av Beneath a Steel Sky
+ och Flight of the Amazon Queen
boot_param number Skicka det här numret till boot script
@@ -1605,9 +1621,10 @@ Sierra-spel som använder SCI-motorn använder även följande nyckelord:
Broken Sword II lägger till följande nyckelord:
gfx_details number Grafisk detalj (0-3)
- music_mute bool Ställ in till “true†för att deaktivera musik
- object_labels bool Ställ in till “true†för att aktivera etiketter för
- föremål
+ music_mute bool Ställ in till “true†för att
+ deaktivera musik
+ object_labels bool Ställ in till “true†för att aktivera
+ etiketter för föremål
reverse_stereo bool Ställ in till “true†för att ivertera
stereokanalerna
sfx_mute bool Ställ in till “true†för att deaktivera
@@ -1615,9 +1632,10 @@ Broken Sword II lägger till följande nyckelord:
Flight of the Amazon Queen lägger till följande nyckelord:
- music_mute bool Ställ in till “true†för att deaktivera musik
- sfx_mute bool Ställ in till “true†för att deaktivera
- ljudeffekter
+ music_mute bool Ställ in till “true†för att
+ deaktivera musik
+ sfx_mute bool Ställ in till “true†för att
+ deaktivera ljudeffekter
Jones in the Fast Lane använder även följande nyckelord:
@@ -1628,10 +1646,12 @@ Jones in the Fast Lane använder även följande nyckelord:
King's Quest VI Windows lägger till följande nyckelord:
- windows_cursors bool Ställ in till “true†för att använda de svartvita
- Windows-muspekarna istället för DOS-muspekarna.
- Ställ in till â€false†för att använda de uppskalade
- muspekarna som matchar resten av grafiken.
+ windows_cursors bool Ställ in till “true†för att använda
+ de svartvita Windows-muspekarna istället
+ för DOS-muspekarna.
+ Ställ in till â€false†för att använda
+ de uppskalade muspekarna som matchar
+ resten av grafiken.
Lands of Lore: The Throne of Chaos använder även följande nyckelord:
@@ -1654,18 +1674,25 @@ Space Quest IV CD använder även följande nyckelord:
Simon the Sorcerer 1 och 2 lägger till följande nyckelord:
- music_mute bool Ställ in till “true†för att deaktivera musik
- sfx_mute bool Ställ in till “true†för att deaktivera
- ljudeffekter
+ music_mute bool Ställ in till “true†för att
+ deaktivera musik
+ sfx_mute bool Ställ in till “true†för att
+ deaktivera ljudeffekter
The Legend of Kyrandia lägger till följande nyckelord:
- walkspeed int GÃ¥nghastighet (0-4)
+ walkspeed number GÃ¥nghastighet (0-4)
+
+The Legend of Kyrandia: The Hand of Fate använder även följande nyckelord:
+ walkspeed number Gånghastighet (3 eller 5, för fast
+ eller långsamt)
The Legend of Kyrandia: Malcolm's Revenge använder även följande nyckelord:
+ walkspeed number Gånghastighet (3 eller 5, för fast
+ eller långsamt)
studio_audience bool Ställ in till “true†för att höra
skratt och applåder när Malcolm
berättar ett skämt
@@ -1676,9 +1703,13 @@ The Legend of Kyrandia: Malcolm's Revenge använder även följande nyckelord:
The 7th Guest använder även följande nyckelord:
- t7g_speed string Hastighet på videouppspelning
- (normal, tweaked, im_an_ios)
+ fast_movie_speed bool Ställ in till true för att spela filmer
+ med högre hastighet för att matcha
+ hastigheten av iOS-versionen.
+ Filmer utan ljud spelas fortfarande
+ med dess normala hastighet för att
+ undvika problem med musiksynkronisering.
8.2) Spelinställningar som kan aktiveras via användargränssnittet
---- ------------------------------------------------------------
diff --git a/engines/cge/cge.h b/engines/cge/cge.h
index 0e8c5a05bb..61558c0989 100644
--- a/engines/cge/cge.h
+++ b/engines/cge/cge.h
@@ -80,6 +80,12 @@ class Talk;
#define kSayTheEnd 41
+enum GameType {
+ kGameTypeNone = 0,
+ kGameTypeSoltys,
+ kGameTypeSfinx
+};
+
// our engine debug channels
enum {
kCGEDebugBitmap = 1 << 0,
diff --git a/engines/cge/detection.cpp b/engines/cge/detection.cpp
index 9e40c7b925..d29c1224fd 100644
--- a/engines/cge/detection.cpp
+++ b/engines/cge/detection.cpp
@@ -30,72 +30,110 @@
static const PlainGameDescriptor CGEGames[] = {
{ "soltys", "Soltys" },
+ { "sfinx", "Sfinx" },
{ 0, 0 }
};
namespace CGE {
-static const ADGameDescription gameDescriptions[] = {
+struct CgeGameDescription {
+ ADGameDescription desc;
+ GameType gameType;
+};
+
+static const CgeGameDescription gameDescriptions[] = {
{
- "soltys", "",
{
- {"vol.cat", 0, "0c33e2c304821a2444d297fc5e2d67c6", 50176},
- {"vol.dat", 0, "f9ae2e7f8f7cac91378cdafca43faf1e", 8437572},
- AD_LISTEND
+ "soltys", "",
+ {
+ {"vol.cat", 0, "0c33e2c304821a2444d297fc5e2d67c6", 50176},
+ {"vol.dat", 0, "f9ae2e7f8f7cac91378cdafca43faf1e", 8437572},
+ AD_LISTEND
+ },
+ Common::PL_POL, Common::kPlatformDOS, ADGF_NO_FLAGS, GUIO0()
},
- Common::PL_POL, Common::kPlatformDOS, ADGF_NO_FLAGS, GUIO0()
+ kGameTypeSoltys
},
{
- "soltys", "Soltys Freeware",
{
- {"vol.cat", 0, "0c33e2c304821a2444d297fc5e2d67c6", 50176},
- {"vol.dat", 0, "f9ae2e7f8f7cac91378cdafca43faf1e", 8437676},
- AD_LISTEND
+ "soltys", "Soltys Freeware",
+ {
+ {"vol.cat", 0, "0c33e2c304821a2444d297fc5e2d67c6", 50176},
+ {"vol.dat", 0, "f9ae2e7f8f7cac91378cdafca43faf1e", 8437676},
+ AD_LISTEND
+ },
+ Common::PL_POL, Common::kPlatformDOS, ADGF_NO_FLAGS, GUIO0()
},
- Common::PL_POL, Common::kPlatformDOS, ADGF_NO_FLAGS, GUIO0()
+ kGameTypeSoltys
},
{
- "soltys", "Soltys Demo (not supported)",
{
- {"vol.cat", 0, "1e077c8ff58109a187f07ac54b0c873a", 18788},
- {"vol.dat", 0, "75d385a6074c58b69f7730481f256051", 1796710},
- AD_LISTEND
+ "soltys", "Soltys Demo (not supported)",
+ {
+ {"vol.cat", 0, "1e077c8ff58109a187f07ac54b0c873a", 18788},
+ {"vol.dat", 0, "75d385a6074c58b69f7730481f256051", 1796710},
+ AD_LISTEND
+ },
+ Common::EN_ANY, Common::kPlatformDOS, ADGF_DEMO , GUIO0()
},
- Common::EN_ANY, Common::kPlatformDOS, ADGF_DEMO , GUIO0()
+ kGameTypeSoltys
},
{
- "soltys", "Soltys Demo (not supported)",
{
- {"vol.cat", 0, "f17987487fab1ebddd781d8d02fedecc", 7168},
- {"vol.dat", 0, "c5d9b15863cab61dc125551576dece04", 1075272},
- AD_LISTEND
+ "soltys", "Soltys Demo (not supported)",
+ {
+ {"vol.cat", 0, "f17987487fab1ebddd781d8d02fedecc", 7168},
+ {"vol.dat", 0, "c5d9b15863cab61dc125551576dece04", 1075272},
+ AD_LISTEND
+ },
+ Common::PL_POL, Common::kPlatformDOS, ADGF_DEMO , GUIO0()
},
- Common::PL_POL, Common::kPlatformDOS, ADGF_DEMO , GUIO0()
+ kGameTypeSoltys
},
{
- "soltys", "Soltys Freeware v1.0",
{
- {"vol.cat", 0, "f1675684c68ab90272f5776f8f2c3974", 50176},
- {"vol.dat", 0, "4ffeff4abc99ac5999b55ccfc56ab1df", 8430868},
- AD_LISTEND
+ "soltys", "Soltys Freeware v1.0",
+ {
+ {"vol.cat", 0, "f1675684c68ab90272f5776f8f2c3974", 50176},
+ {"vol.dat", 0, "4ffeff4abc99ac5999b55ccfc56ab1df", 8430868},
+ AD_LISTEND
+ },
+ Common::EN_ANY, Common::kPlatformDOS, ADGF_NO_FLAGS , GUIO0()
},
- Common::EN_ANY, Common::kPlatformDOS, ADGF_NO_FLAGS , GUIO0()
+ kGameTypeSoltys
},
{
- "soltys", "Soltys Freeware v1.0",
{
- {"vol.cat", 0, "20fdce799adb618100ef9ee2362be875", 50176},
- {"vol.dat", 0, "0e43331c846094d77f5dd201827e0a3b", 8439339},
- AD_LISTEND
+ "soltys", "Soltys Freeware v1.0",
+ {
+ {"vol.cat", 0, "20fdce799adb618100ef9ee2362be875", 50176},
+ {"vol.dat", 0, "0e43331c846094d77f5dd201827e0a3b", 8439339},
+ AD_LISTEND
+ },
+ Common::PL_POL, Common::kPlatformDOS, ADGF_NO_FLAGS, GUIO0()
},
- Common::PL_POL, Common::kPlatformDOS, ADGF_NO_FLAGS, GUIO0()
+ kGameTypeSoltys
},
- AD_TABLE_END_MARKER
+ {
+ {
+ // Polish version, provided by Strangerke
+ "sfinx", "Sfinx Freeware",
+ {
+ {"vol.cat", 0, "21197b287d397c53261b6616bf0dd880", 129024},
+ {"vol.dat", 0, "de14291869a8eb7c2732ab783c7542ef", 34180844},
+ AD_LISTEND
+ },
+ Common::PL_POL, Common::kPlatformDOS, ADGF_NO_FLAGS, GUIO0()
+ },
+ kGameTypeSfinx
+ },
+
+ {AD_TABLE_END_MARKER, kGameTypeNone}
};
static const ADFileBasedFallback fileBasedFallback[] = {
- { &gameDescriptions[0], { "vol.cat", "vol.dat", 0 } },
+ { &gameDescriptions[0].desc, { "vol.cat", "vol.dat", 0 } },
{ 0, { 0 } }
};
@@ -103,7 +141,7 @@ static const ADFileBasedFallback fileBasedFallback[] = {
class CGEMetaEngine : public AdvancedMetaEngine {
public:
- CGEMetaEngine() : AdvancedMetaEngine(CGE::gameDescriptions, sizeof(ADGameDescription), CGEGames) {
+ CGEMetaEngine() : AdvancedMetaEngine(CGE::gameDescriptions, sizeof(CGE::CgeGameDescription), CGEGames) {
_singleid = "soltys";
}
diff --git a/engines/configure.engines b/engines/configure.engines
index a52276130a..15aa11d0ef 100644
--- a/engines/configure.engines
+++ b/engines/configure.engines
@@ -28,6 +28,7 @@ add_engine mohawk "Mohawk" yes "cstime myst riven" "Living Books"
add_engine cstime "Where in Time is Carmen Sandiego?" no
add_engine riven "Riven: The Sequel to Myst" no "" "" "16bit"
add_engine myst "Myst" no "" "" "16bit"
+add_engine neverhood "Neverhood" no
add_engine parallaction "Parallaction" yes
add_engine pegasus "The Journeyman Project: Pegasus Prime" yes "" "" "16bit"
add_engine queen "Flight of the Amazon Queen" yes
diff --git a/engines/drascula/detection.cpp b/engines/drascula/detection.cpp
index 8764b82a04..1917bc879d 100644
--- a/engines/drascula/detection.cpp
+++ b/engines/drascula/detection.cpp
@@ -68,33 +68,57 @@ static const PlainGameDescriptor drasculaGames[] = {
namespace Drascula {
static const DrasculaGameDescription gameDescriptions[] = {
+
+ //// Packed versions //////////////////////////////////////////////////////
+
{
- // Drascula English version
+ // Drascula English version (original packed files)
{
"drascula",
0,
- AD_ENTRY1s("14.ald", "09b2735953edcd43af115c65ae00b10e", 1595),
+ {
+ {"packet.001", 0, "c6a8697396e213a18472542d5f547cb4", 32847563},
+ // HACK: List packet.001 twice to ensure this detector entry
+ // is ranked just as high as the others (which each have two
+ // detection files).
+ {"packet.001", 0, "c6a8697396e213a18472542d5f547cb4", 32847563},
+ {NULL, 0, NULL, 0}
+ },
Common::EN_ANY,
Common::kPlatformDOS,
- ADGF_NO_FLAGS,
+ GF_PACKED,
GUIO0()
},
},
{
- // Drascula English version (original packed files)
+ // Drascula French version (original packed files)
{
"drascula",
0,
{
{"packet.001", 0, "c6a8697396e213a18472542d5f547cb4", 32847563},
- // HACK: List packet.001 twice to ensure this detector entry
- // is ranked just as high as the others (which each have two
- // detection files).
+ {"packet.002", 1, "4401123400f22f212b89f15fb4b43013", 721122},
+ {NULL, 0, NULL, 0}
+ },
+ Common::FR_FRA,
+ Common::kPlatformDOS,
+ GF_PACKED,
+ GUIO0()
+ },
+ },
+
+ {
+ // Drascula French version (ScummVM repacked files)
+ {
+ "drascula",
+ 0,
+ {
{"packet.001", 0, "c6a8697396e213a18472542d5f547cb4", 32847563},
+ {"packet.002", 1, "7b83cedb9bb326ed5143e5c459508d43", 722383},
{NULL, 0, NULL, 0}
},
- Common::EN_ANY,
+ Common::FR_FRA,
Common::kPlatformDOS,
GF_PACKED,
GUIO0()
@@ -119,16 +143,29 @@ static const DrasculaGameDescription gameDescriptions[] = {
},
{
- // Drascula French version (original packed files)
+ // Drascula Italian version (original packed version)
+ {
+ "drascula",
+ 0,
+ AD_ENTRY1s("packet.001", "0253e924af223f5fe52537023385159b", 32564209),
+ Common::IT_ITA,
+ Common::kPlatformDOS,
+ GF_PACKED,
+ GUIO0()
+ },
+ },
+
+ {
+ // Drascula Italian version (ScummVM repacked files)
{
"drascula",
0,
{
{"packet.001", 0, "c6a8697396e213a18472542d5f547cb4", 32847563},
- {"packet.002", 1, "4401123400f22f212b89f15fb4b43013", 721122},
+ {"packet.005", 1, "58caac54b891f5d7f335e710e45e5d29", 16209623},
{NULL, 0, NULL, 0}
},
- Common::FR_FRA,
+ Common::IT_ITA,
Common::kPlatformDOS,
GF_PACKED,
GUIO0()
@@ -149,25 +186,31 @@ static const DrasculaGameDescription gameDescriptions[] = {
},
{
- // Drascula Spanish version
+ // Drascula Spanish version (ScummVM repacked files)
{
"drascula",
0,
- AD_ENTRY1s("14.ald", "0746ed1a5cc8d9728f790c29813f4b43", 23059),
+ {
+ {"packet.001", 0, "c6a8697396e213a18472542d5f547cb4", 32847563},
+ {"packet.004", 1, "a289d3cf80d50f25ec569b653248437e", 17205838},
+ {NULL, 0, NULL, 0}
+ },
Common::ES_ESP,
Common::kPlatformDOS,
- ADGF_NO_FLAGS,
+ GF_PACKED,
GUIO0()
},
},
+ //// Unpacked versions ////////////////////////////////////////////////////
+
{
- // Drascula German version
+ // Drascula English version
{
"drascula",
0,
- AD_ENTRY1s("14.ald", "72e46089033d56bad1c179ac36e2a9d2", 610),
- Common::DE_DEU,
+ AD_ENTRY1s("14.ald", "09b2735953edcd43af115c65ae00b10e", 1595),
+ Common::EN_ANY,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO0()
@@ -188,24 +231,25 @@ static const DrasculaGameDescription gameDescriptions[] = {
},
{
- // Drascula Italian version (original packed version)
+ // Drascula French version (updated - bug #3612236)
{
"drascula",
0,
- AD_ENTRY1s("packet.001", "0253e924af223f5fe52537023385159b", 32564209),
- Common::IT_ITA,
+ AD_ENTRY1s("14.ald", "1f9fbded768bee061cc22bc5bdeab540", 611),
+ Common::FR_FRA,
Common::kPlatformDOS,
- GF_PACKED,
+ ADGF_NO_FLAGS,
GUIO0()
},
},
+
{
- // Drascula Italian version
+ // Drascula German version
{
"drascula",
0,
- AD_ENTRY1s("14.ald", "02b49a18328d0bf2efe6ba658c9c7a1d", 2098),
- Common::IT_ITA,
+ AD_ENTRY1s("14.ald", "72e46089033d56bad1c179ac36e2a9d2", 610),
+ Common::DE_DEU,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO0()
@@ -213,52 +257,40 @@ static const DrasculaGameDescription gameDescriptions[] = {
},
{
- // Drascula Spanish version (ScummVM repacked files)
+ // Drascula Italian version
{
"drascula",
0,
- {
- {"packet.001", 0, "c6a8697396e213a18472542d5f547cb4", 32847563},
- {"packet.004", 1, "a289d3cf80d50f25ec569b653248437e", 17205838},
- {NULL, 0, NULL, 0}
- },
- Common::ES_ESP,
+ AD_ENTRY1s("14.ald", "02b49a18328d0bf2efe6ba658c9c7a1d", 2098),
+ Common::IT_ITA,
Common::kPlatformDOS,
- GF_PACKED,
+ ADGF_NO_FLAGS,
GUIO0()
},
},
{
- // Drascula Italian version (ScummVM repacked files)
+ // Drascula Italian version (updated - bug #3612236)
{
"drascula",
0,
- {
- {"packet.001", 0, "c6a8697396e213a18472542d5f547cb4", 32847563},
- {"packet.005", 1, "58caac54b891f5d7f335e710e45e5d29", 16209623},
- {NULL, 0, NULL, 0}
- },
+ AD_ENTRY1s("14.ald", "ccaee939bb3b344c048f28f9205710d1", 2925),
Common::IT_ITA,
Common::kPlatformDOS,
- GF_PACKED,
+ ADGF_NO_FLAGS,
GUIO0()
},
},
{
- // Drascula French version (ScummVM repacked files)
+ // Drascula Spanish version
{
"drascula",
0,
- {
- {"packet.001", 0, "c6a8697396e213a18472542d5f547cb4", 32847563},
- {"packet.002", 1, "7b83cedb9bb326ed5143e5c459508d43", 722383},
- {NULL, 0, NULL, 0}
- },
- Common::FR_FRA,
+ AD_ENTRY1s("14.ald", "0746ed1a5cc8d9728f790c29813f4b43", 23059),
+ Common::ES_ESP,
Common::kPlatformDOS,
- GF_PACKED,
+ ADGF_NO_FLAGS,
GUIO0()
},
},
diff --git a/engines/drascula/interface.cpp b/engines/drascula/interface.cpp
index 70212db9e8..fca8040f59 100644
--- a/engines/drascula/interface.cpp
+++ b/engines/drascula/interface.cpp
@@ -121,10 +121,8 @@ bool DrasculaEngine::confirmExit() {
void DrasculaEngine::showMenu() {
int h, n, x;
- char textIcon[13];
byte *srcSurface = (currentChapter == 6) ? tableSurface : frontSurface;
x = whichObject();
- strcpy(textIcon, iconName[x]);
for (n = 1; n < ARRAYSIZE(inventoryObjects); n++) {
h = inventoryObjects[n];
@@ -138,7 +136,7 @@ void DrasculaEngine::showMenu() {
}
if (x < 7)
- print_abc(textIcon, _itemLocations[x].x - 2, _itemLocations[x].y - 7);
+ print_abc(iconName[x], _itemLocations[x].x - 2, _itemLocations[x].y - 7);
}
void DrasculaEngine::clearMenu() {
diff --git a/engines/engines.mk b/engines/engines.mk
index bcf97df991..b905a288c9 100644
--- a/engines/engines.mk
+++ b/engines/engines.mk
@@ -130,6 +130,11 @@ DEFINES += -DENABLE_RIVEN
endif
endif
+ifdef ENABLE_NEVERHOOD
+DEFINES += -DENABLE_NEVERHOOD=$(ENABLE_NEVERHOOD)
+MODULES += engines/neverhood
+endif
+
ifdef ENABLE_PARALLACTION
DEFINES += -DENABLE_PARALLACTION=$(ENABLE_PARALLACTION)
MODULES += engines/parallaction
diff --git a/engines/hopkins/debugger.cpp b/engines/hopkins/debugger.cpp
index 762df0adaa..f111eb50d3 100644
--- a/engines/hopkins/debugger.cpp
+++ b/engines/hopkins/debugger.cpp
@@ -35,6 +35,7 @@ Debugger::Debugger(HopkinsEngine *vm) : GUI::Debugger() {
DCmd_Register("teleport", WRAP_METHOD(Debugger, cmd_Teleport));
DCmd_Register("show_room", WRAP_METHOD(Debugger, cmd_ShowCurrentRoom));
DCmd_Register("zones", WRAP_METHOD(Debugger, cmd_Zones));
+ DCmd_Register("lines", WRAP_METHOD(Debugger, cmd_Lines));
}
// Turns dirty rects on or off
@@ -75,4 +76,15 @@ if (argc != 2) {
}
}
+bool Debugger::cmd_Lines(int argc, const char **argv) {
+ if (argc != 2) {
+ DebugPrintf("%s: [on | off]\n", argv[0]);
+ return true;
+ } else {
+ _vm->_graphicsMan->_showLines = !strcmp(argv[1], "on");
+ return false;
+ }
+}
+
+
} // End of namespace Hopkins
diff --git a/engines/hopkins/debugger.h b/engines/hopkins/debugger.h
index 16b5f872d6..746c54a675 100644
--- a/engines/hopkins/debugger.h
+++ b/engines/hopkins/debugger.h
@@ -42,6 +42,7 @@ public:
bool cmd_Teleport(int argc, const char **argv);
bool cmd_ShowCurrentRoom(int argc, const char **argv);
bool cmd_Zones(int argc, const char **argv);
+ bool cmd_Lines(int argc, const char **argv);
};
} // End of namespace Hopkins
diff --git a/engines/hopkins/detection.cpp b/engines/hopkins/detection.cpp
index a42597415b..9d16b0ab51 100644
--- a/engines/hopkins/detection.cpp
+++ b/engines/hopkins/detection.cpp
@@ -67,6 +67,7 @@ static const PlainGameDescriptor hopkinsGames[] = {
const static char *directoryGlobs[] = {
"voice",
+ "link",
0
};
diff --git a/engines/hopkins/detection_tables.h b/engines/hopkins/detection_tables.h
index 3e04375fe9..c3ff563f6f 100644
--- a/engines/hopkins/detection_tables.h
+++ b/engines/hopkins/detection_tables.h
@@ -24,12 +24,11 @@ namespace Hopkins {
static const HopkinsGameDescription gameDescriptions[] = {
{
- // Hopkins FBI Linux Demo 1.00
+ // Hopkins FBI Linux Demo UK 1.00 and 1.02
{
"hopkins",
- "Linux Demo v1.00",
+ "Linux Demo",
{
- {"Hopkins-PDemo.bin", 0, "88b4d6e14b9b1407083cb3d1213c0fa7", 272027},
{"RES_VAN.RES", 0, "29414c05be8f9fe794c61572a65def12", 16060544},
AD_LISTEND
},
@@ -39,32 +38,14 @@ static const HopkinsGameDescription gameDescriptions[] = {
GUIO1(GUIO_NONE)
},
},
-
- {
- // Hopkins FBI Linux Demo 1.02
- {
- "hopkins",
- "Linux Demo v1.02",
- {
- {"Hopkins-PDemo.bin", 0, "f82f4e698f3a189419351be0de2b2f8e", 273760},
- {"RES_VAN.RES", 0, "29414c05be8f9fe794c61572a65def12", 16060544},
- AD_LISTEND
- },
- Common::EN_ANY,
- Common::kPlatformLinux,
- ADGF_DEMO,
- GUIO1(GUIO_NONE)
- },
- },
-
{
// Hopkins FBI OS/2, provided by Strangerke
{
"hopkins",
0,
{
- {"Hopkins.exe", 0, "63d45f882278e5a9fa1027066223e5d9", 292864},
{"ENG_VOI.RES", 0, "fa5789d1d8c19d160bce44a33e742fdf", 66860711},
+ {"CREAN.TXT", 0, "e13aa69d9e043f066776e1d0ef98fdf5", 1871},
AD_LISTEND
},
Common::EN_ANY,
@@ -74,93 +55,41 @@ static const HopkinsGameDescription gameDescriptions[] = {
},
},
{
- // Hopkins FBI Win95 Demo, provided by Strangerke
- // CHECKME: No voice! a second file is required though... Also, it has multi-language support
- {
- "hopkins",
- "Win95 Demo",
- {
- {"Hopkins.exe", 0, "0c9ebfe371f4dcf84a49f333f04839a0", 376897},
- AD_LISTEND
- },
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_DEMO,
- GUIO1(GUIO_NONE)
- },
- },
- {
- // Hopkins FBI Win95 Polish Demo, provided by Strangerke
- {
- "hopkins",
- "Win95 Demo",
- {
- {"Hopkins.exe", 0, "7595c0b9374739b212ee9f8f412ac716", 307200},
- {"RES_VAN.RES", 0, "8262cfba261c200af4451902689dffe0", 12233202},
- AD_LISTEND
- },
- Common::PL_POL,
- Common::kPlatformWindows,
- ADGF_DEMO,
- GUIO1(GUIO_NONE)
- },
- },
- {
- // Hopkins FBI Win95 Spanish
- {
- "hopkins",
- 0,
- {
- {"Hopkins.exe", 0, "31c837378bb2e0b2573befea44956d3f", 421386},
- {"RES_VES.RES", 0, "77ee08896466ae88cc1af3bf1a0bf78c", 32882302},
- AD_LISTEND
- },
- Common::ES_ESP,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO1(GUIO_NONE)
- },
- },
- {
- // Hopkins FBI Win95, provided by Strangerke
+ // Hopkins FBI BeOS, provided by Strangerke & Eriktorbjorn
{
"hopkins",
0,
{
- {"Hopkins.exe", 0, "277a5c144bf9ec7d8450ae37afb85090", 419281},
- {"RES_VAN.RES", 0, "f1693ac0b0859c8ecd8cb30ff43cf55f", 38296346},
+ {"ENG_VOI.RES", 0, "fa5789d1d8c19d160bce44a33e742fdf", 66860711},
AD_LISTEND
},
Common::EN_ANY,
- Common::kPlatformWindows,
+ Common::kPlatformBeOS,
ADGF_NO_FLAGS,
GUIO1(GUIO_NONE)
},
},
{
- // Hopkins FBI Win95, provided by alexbevi
- // Dec 15 1998 hopkins.exe
+ // Hopkins FBI Win95 Spanish
{
"hopkins",
0,
{
- {"Hopkins.exe", 0, "a587762dd50d5933e1c89f9975180764", 378694},
- {"RES_VAN.RES", 0, "f1693ac0b0859c8ecd8cb30ff43cf55f", 38296346},
+ {"RES_VES.RES", 0, "77ee08896466ae88cc1af3bf1a0bf78c", 32882302},
AD_LISTEND
},
- Common::EN_ANY,
+ Common::ES_ESP,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO1(GUIO_NONE)
},
},
{
- // Hopkins FBI Win95 EN, provided by greencis in bug #3612406
+ // Hopkins FBI Win95 UK, provided by Strangerke, alexbevi, greencis
{
"hopkins",
0,
{
- {"hopkins.exe", 0, "020690049fa1dfcd63a18fdafb139a0e", 421386},
{"RES_VAN.RES", 0, "f1693ac0b0859c8ecd8cb30ff43cf55f", 38296346},
AD_LISTEND
},
@@ -176,7 +105,6 @@ static const HopkinsGameDescription gameDescriptions[] = {
"hopkins",
0,
{
- {"hopkins.exe", 0, "3043fef0bd3bfeba8252647cd090ce09", 419281},
{"res_van.res", 0, "bf17c710e184a25a6c8e9d1d9503c38e", 32197685},
AD_LISTEND
},
@@ -192,7 +120,6 @@ static const HopkinsGameDescription gameDescriptions[] = {
"hopkins",
0,
{
- {"Hopkins.bin", 0, "71611380cb31744bf909b8319a65e6e6", 275844},
{"RES_VFR.RES", 0, "0490d4d1aa71075ebf71cc79e5dc7894", 39817945},
AD_LISTEND
},
@@ -208,7 +135,6 @@ static const HopkinsGameDescription gameDescriptions[] = {
"hopkins",
0,
{
- {"Hopkins.bin", 0, "71611380cb31744bf909b8319a65e6e6", 275844},
{"RES_VAN.RES", 0, "29414c05be8f9fe794c61572a65def12", 38832455},
AD_LISTEND
},
@@ -218,61 +144,53 @@ static const HopkinsGameDescription gameDescriptions[] = {
GUIO1(GUIO_NONE)
},
},
-
{
- // Hopkins FBI BeOS, provided by Strangerke
+ // Hopkins FBI Win95, French, provided by SylvainTV
{
"hopkins",
0,
{
- {"ENG_VOI.RES", 0, "fa5789d1d8c19d160bce44a33e742fdf", 66860711},
- {"Hopkins_ FBI", 0, "8940ce2e618c42691b66aad5d6c223b0", 757936},
+ {"RES_VFR.RES", 0, "b8a3849063c9eeefe80e82cfce1ad3cd", 39269361},
AD_LISTEND
},
- Common::EN_ANY,
- Common::kPlatformBeOS,
+ Common::FR_FRA,
+ Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO1(GUIO_NONE)
},
- },
+ },
{
- // Hopkins FBI BeOS, uninstalled, provided by eriktorbjorn
+ // Hopkins FBI Win95 Demo, provided by Strangerke
+ // CHECKME: No voice! a second file is required though... Also, it has multi-language support
{
"hopkins",
- 0,
+ "Win95 Demo",
{
- {"ENG_VOI.RES", 0, "fa5789d1d8c19d160bce44a33e742fdf", 66860711},
- {"Hopkins.pkg", 0, "72f97806dd3d5fc0c0eb24196f180618", 285017},
+ {"Hopkins.exe", 0, "0c9ebfe371f4dcf84a49f333f04839a0", 376897},
AD_LISTEND
},
Common::EN_ANY,
- Common::kPlatformBeOS,
- ADGF_NO_FLAGS,
+ Common::kPlatformWindows,
+ ADGF_DEMO,
GUIO1(GUIO_NONE)
},
-
},
-
{
- // Hopkins FBI Win32, French uninstalled, provided by SylvainTV
+ // Hopkins FBI Win95 Polish Demo, provided by Strangerke
{
"hopkins",
- 0,
+ "Win95 Demo",
{
- {"Hopkins.exe", 0, "277a5c144bf9ec7d8450ae37afb85090", 419281},
- {"RES_VFR.RES", 0, "b8a3849063c9eeefe80e82cfce1ad3cd", 39269361},
+ {"RES_VAN.RES", 0, "8262cfba261c200af4451902689dffe0", 12233202},
AD_LISTEND
},
- Common::FR_FRA,
+ Common::PL_POL,
Common::kPlatformWindows,
- ADGF_NO_FLAGS,
+ ADGF_DEMO,
GUIO1(GUIO_NONE)
},
-
},
-
-
{ AD_TABLE_END_MARKER }
};
diff --git a/engines/hopkins/graphics.cpp b/engines/hopkins/graphics.cpp
index aa71b2c4c0..ebc5cfa8da 100644
--- a/engines/hopkins/graphics.cpp
+++ b/engines/hopkins/graphics.cpp
@@ -74,6 +74,7 @@ GraphicsManager::GraphicsManager(HopkinsEngine *vm) {
_width = 0;
_specialWidth = 0;
_showZones = false;
+ _showLines = false;
Common::fill(&_paletteBuffer[0], &_paletteBuffer[PALETTE_SIZE * 2], 0);
Common::fill(&_colorTable[0], &_colorTable[PALETTE_EXT_BLOCK_SIZE], 0);
@@ -672,6 +673,9 @@ void GraphicsManager::updateScreen() {
if (_showZones)
displayZones();
+ if (_showLines)
+ displayLines();
+
// Update the screen
g_system->updateScreen();
}
@@ -1176,7 +1180,7 @@ void GraphicsManager::displayZones() {
_vm->_objectsMan->_bob[bobId]._oldX + _vm->_objectsMan->_bob[bobId]._oldWidth,
_vm->_objectsMan->_bob[bobId]._oldY + _vm->_objectsMan->_bob[bobId]._oldHeight);
- displayDebugRect(screenSurface, r);
+ displayDebugRect(screenSurface, r, 0xff0000);
}
}
@@ -1185,14 +1189,39 @@ void GraphicsManager::displayZones() {
Common::Rect r(_vm->_linesMan->_squareZone[squareZoneId]._left, _vm->_linesMan->_squareZone[squareZoneId]._top,
_vm->_linesMan->_squareZone[squareZoneId]._right, _vm->_linesMan->_squareZone[squareZoneId]._bottom);
- displayDebugRect(screenSurface, r);
+ displayDebugRect(screenSurface, r, 0x00ff00);
+ }
+ }
+
+ g_system->unlockScreen();
+}
+
+/**
+ * Display any zones for the current room
+ */
+void GraphicsManager::displayLines() {
+ Graphics::Surface *screenSurface = g_system->lockScreen();
+
+ uint16* pixels = (uint16*)screenSurface->pixels;
+
+ for (int lineIndex = 0; lineIndex < _vm->_linesMan->_linesNumb; lineIndex++) {
+ int i = 0;
+ do {
+ int x = _vm->_linesMan->_lineItem[lineIndex]._lineData[i] - _scrollPosX;
+ int y = _vm->_linesMan->_lineItem[lineIndex]._lineData[i+1];
+ if (x >= 0 && x < SCREEN_WIDTH && y >= 0 && y < SCREEN_HEIGHT) {
+ pixels[ y * screenSurface->w + x ] = 0xffff;
+ }
+ i += 2;
}
+ while(_vm->_linesMan->_lineItem[lineIndex]._lineData[i] != -1);
}
g_system->unlockScreen();
}
-void GraphicsManager::displayDebugRect(Graphics::Surface *surface, const Common::Rect &srcRect) {
+
+void GraphicsManager::displayDebugRect(Graphics::Surface *surface, const Common::Rect &srcRect, uint32 color) {
Common::Rect r = srcRect;
// Move for scrolling offset and adjust to crop on-screen
@@ -1204,7 +1233,7 @@ void GraphicsManager::displayDebugRect(Graphics::Surface *surface, const Common:
// If there's an on-screen portion, display it
if (r.isValidRect())
- surface->frameRect(r, 0xffffff);
+ surface->frameRect(r, color);
}
/**
diff --git a/engines/hopkins/graphics.h b/engines/hopkins/graphics.h
index 142de129b9..268db7fc2b 100644
--- a/engines/hopkins/graphics.h
+++ b/engines/hopkins/graphics.h
@@ -119,12 +119,13 @@ public:
Common::Array<Common::Rect> _refreshRects;
bool _showDirtyRects;
bool _showZones;
+ bool _showLines;
byte *_palettePixels;
public:
GraphicsManager(HopkinsEngine *vm);
~GraphicsManager();
-
+
void clearPalette();
void clearScreen();
void clearVesaScreen();
@@ -137,7 +138,8 @@ public:
void displayDirtyRects();
void displayRefreshRects();
void displayZones();
- void displayDebugRect(Graphics::Surface *surface, const Common::Rect &srcRect);
+ void displayLines();
+ void displayDebugRect(Graphics::Surface *surface, const Common::Rect &srcRect, uint32 color = 0xffffff);
void copySurface(const byte *surface, int x1, int y1, int width, int height, byte *destSurface, int destX, int destY);
void loadImage(const Common::String &file);
void loadVgaImage(const Common::String &file);
diff --git a/engines/hopkins/hopkins.cpp b/engines/hopkins/hopkins.cpp
index 87d4600187..b023a2fedb 100644
--- a/engines/hopkins/hopkins.cpp
+++ b/engines/hopkins/hopkins.cpp
@@ -39,6 +39,7 @@ HopkinsEngine *g_vm;
HopkinsEngine::HopkinsEngine(OSystem *syst, const HopkinsGameDescription *gameDesc) : Engine(syst),
_gameDescription(gameDesc), _randomSource("Hopkins") {
+ DebugMan.addDebugChannel(kDebugPath, "Path", "Pathfinding debug level");
g_vm = this;
_animMan = new AnimationManager(this);
_computer = new ComputerManager(this);
@@ -87,14 +88,15 @@ Common::String HopkinsEngine::generateSaveName(int slot) {
* Returns true if it is currently okay to restore a game
*/
bool HopkinsEngine::canLoadGameStateCurrently() {
- return !_globals->_exitId && !_globals->_cityMapEnabledFl && _events->_mouseFl;
+ return !_globals->_exitId && !_globals->_cityMapEnabledFl && _events->_mouseFl && _globals->_curRoomNum != 0;
}
/**
* Returns true if it is currently okay to save the game
*/
bool HopkinsEngine::canSaveGameStateCurrently() {
- return !_globals->_exitId && !_globals->_cityMapEnabledFl && _events->_mouseFl;
+ return !_globals->_exitId && !_globals->_cityMapEnabledFl && _events->_mouseFl
+ && _globals->_curRoomNum != 0 && !isUnderwaterSubScene();
}
/**
@@ -166,24 +168,24 @@ bool HopkinsEngine::runWin95Demo() {
if (_events->_rateCounter > 700)
_globals->_speed = 3;
- if (_startGameSlot == -1) {
+ if (_startGameSlot == -1)
_graphicsMan->fadeOutLong();
- _globals->_eventMode = EVENTMODE_IGNORE;
- _globals->_characterSpriteBuf = _fileIO->loadFile("PERSO.SPR");
- }
+
+ _globals->_eventMode = EVENTMODE_IGNORE;
+ _globals->_characterSpriteBuf = _fileIO->loadFile("PERSO.SPR");
_globals->_characterType = CHARACTER_HOPKINS;
_objectsMan->_mapCarPosX = _objectsMan->_mapCarPosY = 0;
memset(_globals->_saveData, 0, 2000);
_globals->_exitId = 0;
- if (_startGameSlot != -1)
- _saveLoad->loadGame(_startGameSlot);
-
if (getLanguage() != Common::PL_POL)
if (!displayAdultDisclaimer())
return Common::kNoError;
+ if (_startGameSlot != -1)
+ _saveLoad->loadGame(_startGameSlot);
+
for (;;) {
if (_globals->_exitId == 300)
_globals->_exitId = 0;
@@ -400,14 +402,27 @@ bool HopkinsEngine::runWin95Demo() {
break;
case 151:
- _soundMan->playSound(28);
- _globals->_eventMode = EVENTMODE_ALT; // CHECKME!
- _graphicsMan->clearScreen();
- _graphicsMan->clearPalette();
- _graphicsMan->loadImage("njour3a");
- _graphicsMan->fadeInLong();
- _events->delay(5000);
- _graphicsMan->fadeOutLong();
+ if (_fileIO->fileExists("JOUR3A.ANM")) {
+ // The Polish demo uses the animation file than the complete versions
+ _soundMan->playSound(16);
+ _globals->_eventMode = EVENTMODE_IGNORE;
+
+ _graphicsMan->clearScreen();
+ _graphicsMan->clearPalette();
+ _graphicsMan->_fadingFl = true;
+ _animMan->playAnim("JOUR3A.ANM", "JOUR3A.ANM", 12, 12, 2000);
+ } else {
+ // The other demos only display a nag screen
+ _soundMan->playSound(28);
+ _globals->_eventMode = EVENTMODE_ALT; // CHECKME!
+ _graphicsMan->clearScreen();
+ _graphicsMan->clearPalette();
+ _graphicsMan->loadImage("njour3a");
+ _graphicsMan->fadeInLong();
+ _events->delay(5000);
+ _graphicsMan->fadeOutLong();
+ }
+
_globals->_exitId = 300;
_globals->_eventMode = EVENTMODE_DEFAULT;
break;
@@ -2881,4 +2896,8 @@ bool HopkinsEngine::displayAdultDisclaimer() {
}
}
+bool HopkinsEngine::isUnderwaterSubScene() {
+ return (_globals->_curRoomNum >= 77) && (_globals->_curRoomNum <= 89);
+}
+
} // End of namespace Hopkins
diff --git a/engines/hopkins/hopkins.h b/engines/hopkins/hopkins.h
index 7af7f962b3..777fd1c335 100644
--- a/engines/hopkins/hopkins.h
+++ b/engines/hopkins/hopkins.h
@@ -59,14 +59,6 @@
*/
namespace Hopkins {
-enum {
- kHopkinsDebugAnimations = 1 << 0,
- kHopkinsDebugActions = 1 << 1,
- kHopkinsDebugSound = 1 << 2,
- kHopkinsDebugMusic = 1 << 3,
- kHopkinsDebugScripts = 1 << 4
-};
-
#define DEBUG_BASIC 1
#define DEBUG_INTERMEDIATE 2
#define DEBUG_DETAILED 3
@@ -74,6 +66,10 @@ enum {
#define SCREEN_WIDTH 640
#define SCREEN_HEIGHT 480
+enum HopkinsDebugChannels {
+ kDebugPath = 1 << 0
+};
+
/**
* A wrapper macro used around three character constants, like 'END', to
* ensure portability. Typical usage: MKTAG24('E','N','D').
@@ -101,6 +97,7 @@ private:
void playUnderwaterBaseCutscene();
void playPlaneCutscene();
void playEnding();
+ bool isUnderwaterSubScene();
/**
* Displays the map screen in the underground base.
diff --git a/engines/hopkins/lines.cpp b/engines/hopkins/lines.cpp
index 791db4267a..aa708fdfb2 100644
--- a/engines/hopkins/lines.cpp
+++ b/engines/hopkins/lines.cpp
@@ -99,6 +99,7 @@ LinesManager::~LinesManager() {
}
int LigneItem::appendToRouteInc(int from, int to, RouteItem *route, int index) {
+ debugC(5, kDebugPath, "appendToRouteInc(%d, %d, route, %d)", from, to, index);
if (to == -1)
to = _lineDataEndIdx;
@@ -106,7 +107,9 @@ int LigneItem::appendToRouteInc(int from, int to, RouteItem *route, int index) {
route[index++].set(_lineData[2*i], _lineData[2*i+1], _directionRouteInc);
return index;
}
+
int LigneItem::appendToRouteDec(int from, int to, RouteItem *route, int index) {
+ debugC(5, kDebugPath, "appendToRouteDecc(%d, %d, route, %d)", from, to, index);
if (from == -1)
from = _lineDataEndIdx - 1;
@@ -119,6 +122,7 @@ int LigneItem::appendToRouteDec(int from, int to, RouteItem *route, int index) {
* Load lines
*/
void LinesManager::loadLines(const Common::String &file) {
+ debugC(5, kDebugPath, "loadLines(%s)", file.c_str());
resetLines();
_linesNumb = 0;
_lastLine = 0;
@@ -140,6 +144,7 @@ void LinesManager::loadLines(const Common::String &file) {
* Returns the ID of the hotspot under mouse
*/
int LinesManager::checkInventoryHotspots(int posX, int posY) {
+ debugC(5, kDebugPath, "checkInventoryHotspots(%d, %d)", posX, posY);
int hotspotId = 0;
if (posY >= 120 && posY <= 153)
hotspotId = checkInventoryHotspotsRow(posX, 1, false);
@@ -166,6 +171,7 @@ int LinesManager::checkInventoryHotspots(int posX, int posY) {
* Returns the hotspot Id under the mouse, if any.
*/
int LinesManager::checkInventoryHotspotsRow(int posX, int minZoneNum, bool lastRow) {
+ debugC(5, kDebugPath, "checkInventoryHotspotsRow(%d, %d, %d)", posX, minZoneNum, lastRow ? 1 : 0);
int result = minZoneNum;
if (posX >= _vm->_graphicsMan->_scrollOffset + 158 && posX < _vm->_graphicsMan->_scrollOffset + 208)
@@ -203,6 +209,7 @@ int LinesManager::checkInventoryHotspotsRow(int posX, int minZoneNum, bool lastR
* Add Zone Line
*/
void LinesManager::addZoneLine(int idx, int fromX, int fromY, int destX, int destY, int bobZoneIdx) {
+ debugC(5, kDebugPath, "addZoneLine(%d, %d, %d, %d, %d, %d)", idx, fromX, fromY, destX, destY, bobZoneIdx);
int16 *zoneData;
if (fromX == fromY && fromY == destX && fromY == destY) {
@@ -253,6 +260,7 @@ void LinesManager::addZoneLine(int idx, int fromX, int fromY, int destX, int des
* Add Line
*/
void LinesManager::addLine(int lineIdx, Directions direction, int fromX, int fromY, int destX, int destY) {
+ debugC(5, kDebugPath, "addLine(%d, %d, %d, %d, %d, %d)", lineIdx, direction, fromX, fromY, destX, destY);
assert(lineIdx < MAX_LINES);
if (_linesNumb < lineIdx)
@@ -367,6 +375,7 @@ void LinesManager::addLine(int lineIdx, Directions direction, int fromX, int fro
* Check collision line
*/
bool LinesManager::checkCollisionLine(int xp, int yp, int *foundDataIdx, int *foundLineIdx, int startLineIdx, int endLineIdx) {
+ debugC(5, kDebugPath, "checkCollisionLine(%d, %d, foundDataIdx, foundLineIdx, %d, %d)", xp, yp, startLineIdx ,endLineIdx);
int16 *lineData;
int left = xp + 4;
@@ -426,6 +435,7 @@ bool LinesManager::checkCollisionLine(int xp, int yp, int *foundDataIdx, int *fo
* Init route
*/
void LinesManager::initRoute() {
+ debugC(5, kDebugPath, "initRoute()");
int lineX = _lineItem[0]._lineData[0];
int lineY = _lineItem[0]._lineData[1];
@@ -462,6 +472,7 @@ void LinesManager::initRoute() {
// Avoid obstacle
int LinesManager::avoidObstacle(int lineIdx, int lineDataIdx, int routeIdx, int destLineIdx, int destLineDataIdx, RouteItem *route) {
+ debugC(5, kDebugPath, "avoidObstacle(%d, %d, %d, %d, %d, route)", lineIdx, lineDataIdx, routeIdx, destLineIdx, destLineDataIdx);
int curLineIdx = lineIdx;
int curLineDataIdx = lineDataIdx;
int curRouteIdx = routeIdx;
@@ -493,6 +504,7 @@ int LinesManager::avoidObstacle(int lineIdx, int lineDataIdx, int routeIdx, int
// Avoid Obstacle, taking into account start/End lind Idx
int LinesManager::avoidObstacleOnSegment(int lineIdx, int lineDataIdx, int routeIdx, int destLineIdx, int destLineDataIdx, RouteItem *route, int startLineIdx, int endLineIdx) {
+ debugC(5, kDebugPath, "avoidObstacleOnSegment(%d, %d, %d, %d, %d, route, %d, %d)", lineIdx, lineDataIdx, routeIdx, destLineIdx, destLineDataIdx, startLineIdx, endLineIdx);
int curLineIdx = lineIdx;
int curLineDataIdx = lineDataIdx;
int curRouteIdx = routeIdx;
@@ -535,6 +547,7 @@ int LinesManager::avoidObstacleOnSegment(int lineIdx, int lineDataIdx, int route
}
bool LinesManager::MIRACLE(int fromX, int fromY, int lineIdx, int destLineIdx, int routeIdx) {
+ debugC(5, kDebugPath, "MIRACLE(%d, %d, %d, %d, %d)", fromX, fromY, lineIdx, destLineIdx, routeIdx);
int newLinesDataIdx = 0;
int newLinesIdx = 0;
int lineIdxLeft = 0;
@@ -776,6 +789,7 @@ bool LinesManager::MIRACLE(int fromX, int fromY, int lineIdx, int destLineIdx, i
}
int LinesManager::computeRouteIdx(int lineIdx, int dataIdx, int fromX, int fromY, int destX, int destY, int routerIdx, RouteItem *route) {
+ debugC(5, kDebugPath, "computeRouteIdx(%d, %d, %d, %d, %d, %d, %d)", lineIdx, dataIdx, fromX, fromY, destX, destY, routerIdx);
int result = routerIdx;
++_pathFindingMaxDepth;
if (_pathFindingMaxDepth > 10) {
@@ -931,48 +945,44 @@ int LinesManager::computeRouteIdx(int lineIdx, int dataIdx, int fromX, int fromY
if (destX >= minLineX && destX <= maxLineX && destY >= minLineY && destY <= maxLineY) {
int curY = destY;
int linesIdxUp = -1;
- for (;;) {
+ do {
--curY;
- if (!checkCollisionLine(destX, curY, &foundDataIdx, &foundLineIdx, startLineIdx, endLineIdx))
+ if (checkCollisionLine(destX, curY, &foundDataIdx, &foundLineIdx, startLineIdx, endLineIdx)) {
+ linesIdxUp = foundLineIdx;
break;
+ }
+ } while (curY && curY >= minLineY);
- linesIdxUp = foundLineIdx;
- if (!curY || minLineY > curY)
- break;
- }
curY = destY;
int lineIdxDown = -1;
- for (;;) {
+ do {
++curY;
- if (!checkCollisionLine(destX, curY, &foundDataIdx, &foundLineIdx, startLineIdx, endLineIdx))
+ if (checkCollisionLine(destX, curY, &foundDataIdx, &foundLineIdx, startLineIdx, endLineIdx)) {
+ lineIdxDown = foundLineIdx;
break;
+ }
+ } while (curY < _vm->_globals->_characterMaxPosY && curY < maxLineY);
- lineIdxDown = foundLineIdx;
- if (_vm->_globals->_characterMaxPosY <= curY || maxLineY <= curY)
- break;
- }
int curX = destX;
int lineIdxRight = -1;
- for (;;) {
+ do {
++curX;
- if (!checkCollisionLine(curX, destY, &foundDataIdx, &foundLineIdx, startLineIdx, endLineIdx))
+ if (checkCollisionLine(curX, destY, &foundDataIdx, &foundLineIdx, startLineIdx, endLineIdx)) {
+ lineIdxRight = foundLineIdx;
break;
+ }
+ } while (curX < _vm->_graphicsMan->_maxX && curX < maxLineX);
- lineIdxRight = foundLineIdx;
-
- if (_vm->_graphicsMan->_maxX <= curX || maxLineX <= curX)
- break;
- }
curX = destX;
int lineIdxLeft = -1;
- for(;;) {
+ do {
--curX;
- if (!checkCollisionLine(curX, destY, &foundDataIdx, &foundLineIdx, startLineIdx, endLineIdx))
+ if (checkCollisionLine(curX, destY, &foundDataIdx, &foundLineIdx, startLineIdx, endLineIdx)) {
+ lineIdxLeft = foundLineIdx;
break;
- lineIdxLeft = foundLineIdx;
- if (curX <= 0 || minLineX >= curX)
- break;
- }
+ }
+ } while (curX > 0 && curX > minLineX);
+
if (lineIdxRight != -1 && lineIdxLeft != -1 && linesIdxUp != -1 && lineIdxDown != -1) {
route[routerIdx].invalidate();
return -1;
@@ -1076,6 +1086,7 @@ int LinesManager::computeRouteIdx(int lineIdx, int dataIdx, int fromX, int fromY
// Find Route from a point to the other
RouteItem *LinesManager::findRoute(int fromX, int fromY, int destX, int destY) {
+ debugC(5, kDebugPath, "findRoute(%d, %d, %d, %d)", fromX, fromY, destX, destY);
int foundLineIdx;
int foundDataIdx;
int curLineY = 0;
@@ -1503,6 +1514,7 @@ RouteItem *LinesManager::findRoute(int fromX, int fromY, int destX, int destY) {
}
void LinesManager::useRoute0(int idx, int curRouteIdx) {
+ debugC(5, kDebugPath, "useRoute0(%d, %d)", idx, curRouteIdx);
if (idx) {
int i = 0;
do {
@@ -1514,6 +1526,7 @@ void LinesManager::useRoute0(int idx, int curRouteIdx) {
}
void LinesManager::useRoute1(int idx, int curRouteIdx) {
+ debugC(5, kDebugPath, "useRoute1(%d, %d)", idx, curRouteIdx);
if (idx) {
int i = 0;
do {
@@ -1525,6 +1538,7 @@ void LinesManager::useRoute1(int idx, int curRouteIdx) {
}
void LinesManager::useRoute2(int idx, int curRouteIdx) {
+ debugC(5, kDebugPath, "useRoute2(%d, %d)", idx, curRouteIdx);
if (idx) {
int i = 0;
do {
@@ -1536,6 +1550,7 @@ void LinesManager::useRoute2(int idx, int curRouteIdx) {
}
int LinesManager::characterRoute(int fromX, int fromY, int destX, int destY, int startLineIdx, int endLineIdx, int routeIdx) {
+ debugC(5, kDebugPath, "characterRoute(%d, %d, %d, %d, %d, %d, %d)", fromX, fromY, destX, destY, startLineIdx, endLineIdx, routeIdx);
int collDataIdxRoute2 = 0;
bool colResult = false;
@@ -1969,8 +1984,8 @@ int LinesManager::characterRoute(int fromX, int fromY, int destX, int destY, int
_newRouteIdx = curRouteIdx;
return 2;
}
- // CHECKME: Checking essai0[0]._x might make more sense here?
- if (_testRoute1[0]._x != -1 && foundLineIdx > collLineIdxRoute0 && collLineIdxRoute1 >= collLineIdxRoute0 && collLineIdxRoute2 >= collLineIdxRoute0 && endLineIdx <= collLineIdxRoute0) {
+
+ if (_testRoute0[0]._x != -1 && foundLineIdx > collLineIdxRoute0 && collLineIdxRoute1 >= collLineIdxRoute0 && collLineIdxRoute2 >= collLineIdxRoute0 && endLineIdx <= collLineIdxRoute0) {
_newLineIdx = collLineIdxRoute0;
_newLineDataIdx = collDataIdxRoute0;
int i = 0;
@@ -1987,6 +2002,7 @@ int LinesManager::characterRoute(int fromX, int fromY, int destX, int destY, int
}
RouteItem *LinesManager::cityMapCarRoute(int x1, int y1, int x2, int y2) {
+ debugC(5, kDebugPath, "cityMapCarRoute(%d, %d, %d, %d)", x1, y1, x2, y2);
RouteItem *result;
int arrDelta[10];
int arrDataIdx[10];
@@ -2165,6 +2181,7 @@ RouteItem *LinesManager::cityMapCarRoute(int x1, int y1, int x2, int y2) {
}
bool LinesManager::checkSmoothMove(int fromX, int fromY, int destX, int destY) {
+ debugC(5, kDebugPath, "checkSmoothMove(%d, %d, %d, %d)", fromX, fromY, destX, destY);
int distX = abs(fromX - destX) + 1;
int distY = abs(fromY - destY) + 1;
if (distX > distY)
@@ -2203,6 +2220,7 @@ bool LinesManager::checkSmoothMove(int fromX, int fromY, int destX, int destY) {
}
bool LinesManager::makeSmoothMove(int fromX, int fromY, int destX, int destY) {
+ debugC(5, kDebugPath, "makeSmoothMove(%d, %d, %d, %d)", fromX, fromY, destX, destY);
int curX = fromX;
int curY = fromY;
if (fromX > destX && destY > fromY) {
@@ -2335,6 +2353,7 @@ bool LinesManager::makeSmoothMove(int fromX, int fromY, int destX, int destY) {
}
bool LinesManager::PLAN_TEST(int paramX, int paramY, int superRouteIdx, int paramStartLineIdx, int paramEndLineIdx) {
+ debugC(5, kDebugPath, "PLAN_TEST(%d, %d, %d, %d, %d)", paramX, paramY, superRouteIdx, paramStartLineIdx, paramEndLineIdx);
int sideTestUp;
int sideTestDown;
int sideTestLeft;
@@ -2435,6 +2454,7 @@ bool LinesManager::PLAN_TEST(int paramX, int paramY, int superRouteIdx, int para
// Test line
int LinesManager::testLine(int paramX, int paramY, int *testValue, int *foundLineIdx, int *foundDataIdx) {
+ debugC(5, kDebugPath, "testLine(%d, %d, testValue, foundLineIdx, foundDataIdx)", paramX, paramY);
int16 *lineData;
int lineDataEndIdx;
int collLineIdx;
@@ -2482,6 +2502,7 @@ int LinesManager::testLine(int paramX, int paramY, int *testValue, int *foundLin
}
int LinesManager::computeYSteps(int idx) {
+ debugC(5, kDebugPath, "computeYSteps(%d)", idx);
int zoomPct = _vm->_globals->_spriteSize[idx];
if (_vm->_globals->_characterType == CHARACTER_HOPKINS_CLONE) {
if (zoomPct < 0)
@@ -2503,6 +2524,7 @@ int LinesManager::computeYSteps(int idx) {
}
void LinesManager::optimizeRoute(RouteItem *route) {
+ debugC(5, kDebugPath, "optimizeRoute(route)");
if (route[0]._x == -1 && route[0]._y == -1)
return;
@@ -2549,6 +2571,7 @@ void LinesManager::optimizeRoute(RouteItem *route) {
}
int LinesManager::getMouseZone() {
+ debugC(9, kDebugPath, "getMouseZone()");
int result;
int xp = _vm->_events->_mousePos.x + _vm->_events->_mouseOffset.x;
@@ -2569,6 +2592,11 @@ int LinesManager::getMouseZone() {
_zone[bobZoneId]._destY = _vm->_objectsMan->_bob[bobId]._oldHeight + _vm->_objectsMan->_bob[bobId]._oldY + 6;
_zone[bobZoneId]._spriteIndex = -1;
}
+
+ // WORKAROUND: Avoid allowing hotspots that should remain non-interactive
+ if (bobZoneId == 24 && _vm->_globals->_curRoomNum == 14)
+ continue;
+
return bobZoneId;
}
}
@@ -2637,6 +2665,7 @@ int LinesManager::getMouseZone() {
}
int LinesManager::checkCollision(int xp, int yp) {
+ debugC(7, kDebugPath, "checkCollision(%d, %d)", xp, yp);
if (_currentSegmentId <= 0)
return -1;
@@ -2686,6 +2715,7 @@ int LinesManager::checkCollision(int xp, int yp) {
// Square Zone
void LinesManager::initSquareZones() {
+ debugC(5, kDebugPath, "initSquareZones()");
for (int idx = 0; idx < 100; ++idx) {
SquareZoneItem *curZone = &_squareZone[idx];
curZone->_enabledFl = false;
@@ -2728,6 +2758,7 @@ void LinesManager::initSquareZones() {
}
void LinesManager::clearAll() {
+ debugC(5, kDebugPath, "clearAll()");
for (int idx = 0; idx < 105; ++idx) {
_zone[idx]._destX = 0;
_zone[idx]._destY = 0;
@@ -2773,6 +2804,7 @@ void LinesManager::clearAll() {
* Clear all zones and reset nextLine
*/
void LinesManager::clearAllZones() {
+ debugC(5, kDebugPath, "clearAllZones()");
for (int idx = 0; idx < MAX_LINES; ++idx)
removeZoneLine(idx);
}
@@ -2781,37 +2813,37 @@ void LinesManager::clearAllZones() {
* Remove Zone Line
*/
void LinesManager::removeZoneLine(int idx) {
+ debugC(5, kDebugPath, "removeZoneLine(%d)", idx);
assert(idx < MAX_LINES + 1);
_zoneLine[idx]._zoneData = (int16 *)_vm->_globals->freeMemory((byte *)_zoneLine[idx]._zoneData);
}
void LinesManager::resetLines() {
+ debugC(5, kDebugPath, "resetLines()");
for (int idx = 0; idx < MAX_LINES; ++idx) {
- removeLine(idx);
+ _lineItem[idx]._lineData = (int16 *)_vm->_globals->freeMemory((byte *)_lineItem[idx]._lineData);
_lineItem[idx]._lineDataEndIdx = 0;
_lineItem[idx]._lineData = NULL;
}
}
-// Remove Line
-void LinesManager::removeLine(int idx) {
- assert(idx < MAX_LINES);
- _lineItem[idx]._lineData = (int16 *)_vm->_globals->freeMemory((byte *)_lineItem[idx]._lineData);
-}
-
void LinesManager::setMaxLineIdx(int idx) {
+ debugC(5, kDebugPath, "setMaxLineIdx(%d)", idx);
_maxLineIdx = idx;
}
void LinesManager::resetLastLine() {
+ debugC(5, kDebugPath, "resetLastLine()");
_lastLine = 0;
}
void LinesManager::resetLinesNumb() {
+ debugC(5, kDebugPath, "resetLinesNumb()");
_linesNumb = 0;
}
void LinesManager::enableZone(int idx) {
+ debugC(5, kDebugPath, "enableZone(%d)", idx);
if (_bobZone[idx]) {
_bobZoneFl[idx] = true;
} else {
@@ -2820,6 +2852,7 @@ void LinesManager::enableZone(int idx) {
}
void LinesManager::disableZone(int idx) {
+ debugC(5, kDebugPath, "disableZone(%d)", idx);
if (_bobZone[idx]) {
_bobZoneFl[idx] = false;
} else {
@@ -2828,6 +2861,7 @@ void LinesManager::disableZone(int idx) {
}
void LinesManager::checkZone() {
+ debugC(9, kDebugPath, "checkZone()");
int mouseX = _vm->_events->getMouseX();
int mouseY = _vm->_events->getMouseY();
int oldMouseY = mouseY;
@@ -2858,6 +2892,10 @@ void LinesManager::checkZone() {
int zoneId;
if (_oldMouseX != mouseX || _oldMouseY != oldMouseY) {
zoneId = getMouseZone();
+
+ // WORKAROUND: Incorrect hotspot zones in the guard's control room
+ if (_vm->_globals->_curRoomNum == 71 && (zoneId == 14 || zoneId == 12 || zoneId == 17))
+ zoneId = _oldMouseZoneId;
} else {
zoneId = _oldMouseZoneId;
}
diff --git a/engines/hopkins/lines.h b/engines/hopkins/lines.h
index 27229aa105..b32dc6e2a5 100644
--- a/engines/hopkins/lines.h
+++ b/engines/hopkins/lines.h
@@ -118,7 +118,6 @@ private:
int _currentSegmentId;
int _maxLineIdx;
int _lastLine;
- int _linesNumb;
int _newLineIdx;
int _newLineDataIdx;
int _newRouteIdx;
@@ -135,7 +134,6 @@ private:
RouteItem *_testRoute0;
RouteItem *_testRoute1;
int16 *_lineBuf;
- LigneItem _lineItem[MAX_LINES];
RouteItem _bestRoute[8001];
int _zoneSkipCount;
int _oldMouseZoneId;
@@ -168,6 +166,8 @@ public:
bool _bobZoneFl[105];
ZoneItem _zone[106];
SquareZoneItem _squareZone[101];
+ LigneItem _lineItem[MAX_LINES];
+ int _linesNumb;
LinesManager(HopkinsEngine *vm);
~LinesManager();
diff --git a/engines/lure/menu.cpp b/engines/lure/menu.cpp
index 93deecdcd6..7d34ac3911 100644
--- a/engines/lure/menu.cpp
+++ b/engines/lure/menu.cpp
@@ -47,6 +47,7 @@ MenuRecord::MenuRecord(const MenuRecordBounds *bounds, int numParams, ...) {
va_start(params, numParams);
for (int index = 0; index < _numEntries; ++index)
_entries[index] = va_arg(params, const char *);
+ va_end(params);
// Store position data
_hsxstart = bounds->left; _hsxend = bounds->right;
diff --git a/engines/made/database.cpp b/engines/made/database.cpp
index bf47164e8f..7c8f9bdd0c 100644
--- a/engines/made/database.cpp
+++ b/engines/made/database.cpp
@@ -665,15 +665,16 @@ bool GameDatabaseV3::getSavegameDescription(const char *filename, Common::String
}
int32 size = in->readUint32LE();
- if (size != in->size() - 64) {
- warning("Unexpected save game size. Expected %d, size is %d (file size - 64)", size, in->size() - 64);
+ int16 saveVersion = in->readUint16LE();
+
+ if (saveVersion != version) {
+ warning("Save game %s was saved with a different version of the game. Game version is %d, save version is %d", filename, version, saveVersion);
delete in;
return false;
}
- int16 saveVersion = in->readUint16LE();
- if (saveVersion != version) {
- warning("Save game %s was saved with a different version of the game. Game version is %d, save version is %d", filename, version, saveVersion);
+ if (size != in->size() - 64) {
+ warning("Unexpected save game size. Expected %d, size is %d (file size - 64)", size, in->size() - 64);
delete in;
return false;
}
@@ -722,15 +723,16 @@ int16 GameDatabaseV3::loadgame(const char *filename, int16 version) {
}
uint32 size = in->readUint32LE();
- if (size != expectedSize) {
- warning("Unexpected save game size. Expected %d, size is %d", expectedSize, size);
+ int16 saveVersion = in->readUint16LE();
+
+ if (saveVersion != version) {
+ warning("Save game %s was saved with a different version of the game. Game version is %d, save version is %d", filename, version, saveVersion);
delete in;
return 1;
}
- int16 saveVersion = in->readUint16LE();
- if (saveVersion != version) {
- warning("Save game %s was saved with a different version of the game. Game version is %d, save version is %d", filename, version, saveVersion);
+ if (size != expectedSize) {
+ warning("Unexpected save game size. Expected %d, size is %d", expectedSize, size);
delete in;
return 1;
}
diff --git a/engines/mohawk/detection_tables.h b/engines/mohawk/detection_tables.h
index 0d5dc1a405..3b925af5a9 100644
--- a/engines/mohawk/detection_tables.h
+++ b/engines/mohawk/detection_tables.h
@@ -1374,7 +1374,7 @@ static const MohawkGameDescription gameDescriptions[] = {
GUIO1(GUIO_NOASPECT)
},
GType_LIVINGBOOKSV1,
- 0,
+ GF_LB_10,
0
},
diff --git a/engines/neverhood/background.cpp b/engines/neverhood/background.cpp
new file mode 100644
index 0000000000..0a80bd8390
--- /dev/null
+++ b/engines/neverhood/background.cpp
@@ -0,0 +1,60 @@
+/* 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/background.h"
+
+namespace Neverhood {
+
+// Background
+
+Background::Background(NeverhoodEngine *vm, int objectPriority)
+ : Entity(vm, objectPriority), _surface(NULL), _spriteResource(vm) {
+ // Empty
+}
+
+Background::Background(NeverhoodEngine *vm, uint32 fileHash, int objectPriority, int surfacePriority)
+ : Entity(vm, objectPriority), _surface(NULL), _spriteResource(vm) {
+
+ _spriteResource.load(fileHash);
+ createSurface(surfacePriority, _spriteResource.getDimensions().width, _spriteResource.getDimensions().height);
+ _surface->drawSpriteResource(_spriteResource);
+
+}
+
+Background::~Background() {
+ delete _surface;
+}
+
+void Background::createSurface(int surfacePriority, int16 width, int16 height) {
+ _surface = new BaseSurface(_vm, surfacePriority, width, height);
+ _surface->setTransparent(false);
+ _spriteResource.getPosition().x = width;
+ _spriteResource.getPosition().y = height;
+}
+
+void Background::load(uint32 fileHash) {
+ _spriteResource.load(fileHash);
+ if (_surface)
+ _surface->drawSpriteResource(_spriteResource);
+}
+
+} // End of namespace Neverhood
diff --git a/engines/neverhood/background.h b/engines/neverhood/background.h
new file mode 100644
index 0000000000..ef88be21c0
--- /dev/null
+++ b/engines/neverhood/background.h
@@ -0,0 +1,49 @@
+/* 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_BACKGROUND_H
+#define NEVERHOOD_BACKGROUND_H
+
+#include "neverhood/neverhood.h"
+#include "neverhood/entity.h"
+#include "neverhood/graphics.h"
+#include "neverhood/resource.h"
+
+namespace Neverhood {
+
+class Background : public Entity {
+public:
+ Background(NeverhoodEngine *vm, int objectPriority);
+ Background(NeverhoodEngine *vm, uint32 fileHash, int objectPriority, int surfacePriority);
+ virtual ~Background();
+ BaseSurface *getSurface() { return _surface; }
+ void createSurface(int surfacePriority, int16 width, int16 height);
+ void load(uint32 fileHash);
+ SpriteResource& getSpriteResource() { return _spriteResource; }
+protected:
+ BaseSurface *_surface;
+ SpriteResource _spriteResource;
+};
+
+} // End of namespace Neverhood
+
+#endif /* NEVERHOOD_BACKGROUND_H */
diff --git a/engines/neverhood/blbarchive.cpp b/engines/neverhood/blbarchive.cpp
new file mode 100644
index 0000000000..9f5f46487c
--- /dev/null
+++ b/engines/neverhood/blbarchive.cpp
@@ -0,0 +1,159 @@
+/* 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 "common/dcl.h"
+#include "neverhood/blbarchive.h"
+
+namespace Neverhood {
+
+/**
+ * A special variant of SafeSeekableSubReadStream which locks a mutex during each read.
+ * This is neccessary because the music is streamed from disk and it could happen
+ * that a sound effect or another music track is played from the same read stream
+ * while the first music track is updated/read.
+ */
+
+class SafeMutexedSeekableSubReadStream : public Common::SafeSeekableSubReadStream {
+public:
+ SafeMutexedSeekableSubReadStream(SeekableReadStream *parentStream, uint32 begin, uint32 end, DisposeAfterUse::Flag disposeParentStream,
+ Common::Mutex &mutex)
+ : SafeSeekableSubReadStream(parentStream, begin, end, disposeParentStream), _mutex(mutex) {
+ }
+ virtual uint32 read(void *dataPtr, uint32 dataSize);
+protected:
+ Common::Mutex &_mutex;
+};
+
+uint32 SafeMutexedSeekableSubReadStream::read(void *dataPtr, uint32 dataSize) {
+ Common::StackLock lock(_mutex);
+ return Common::SafeSeekableSubReadStream::read(dataPtr, dataSize);
+}
+
+BlbArchive::BlbArchive() : _extData(NULL) {
+}
+
+BlbArchive::~BlbArchive() {
+ delete[] _extData;
+}
+
+void BlbArchive::open(const Common::String &filename) {
+ BlbHeader header;
+ uint16 *extDataOffsets;
+
+ _entries.clear();
+
+ if (!_fd.open(filename))
+ error("BlbArchive::open() Could not open %s", filename.c_str());
+
+ header.id1 = _fd.readUint32LE();
+ header.id2 = _fd.readUint16LE();
+ header.extDataSize = _fd.readUint16LE();
+ header.fileSize = _fd.readUint32LE();
+ header.fileCount = _fd.readUint32LE();
+
+ if (header.id1 != 0x2004940 || header.id2 != 7 || header.fileSize != _fd.size())
+ error("BlbArchive::open() %s seems to be corrupt", filename.c_str());
+
+ debug(4, "%s: fileCount = %d", filename.c_str(), header.fileCount);
+
+ _entries.reserve(header.fileCount);
+
+ // Load file hashes
+ for (uint i = 0; i < header.fileCount; i++) {
+ BlbArchiveEntry entry;
+ entry.fileHash = _fd.readUint32LE();
+ _entries.push_back(entry);
+ }
+
+ extDataOffsets = new uint16[header.fileCount];
+
+ // Load file records
+ for (uint i = 0; i < header.fileCount; i++) {
+ BlbArchiveEntry &entry = _entries[i];
+ entry.type = _fd.readByte();
+ entry.comprType = _fd.readByte();
+ entry.extData = NULL;
+ extDataOffsets[i] = _fd.readUint16LE();
+ entry.timeStamp = _fd.readUint32LE();
+ entry.offset = _fd.readUint32LE();
+ entry.diskSize = _fd.readUint32LE();
+ entry.size = _fd.readUint32LE();
+ debug(4, "%08X: %03d, %02X, %04X, %08X, %08X, %08X, %08X",
+ entry.fileHash, entry.type, entry.comprType, extDataOffsets[i], entry.timeStamp,
+ entry.offset, entry.diskSize, entry.size);
+ }
+
+ // Load ext data
+ if (header.extDataSize > 0) {
+ _extData = new byte[header.extDataSize];
+ _fd.read(_extData, header.extDataSize);
+ for (uint i = 0; i < header.fileCount; i++)
+ _entries[i].extData = extDataOffsets[i] > 0 ? _extData + extDataOffsets[i] - 1 : NULL;
+ }
+
+ delete[] extDataOffsets;
+
+}
+
+void BlbArchive::load(uint index, byte *buffer, uint32 size) {
+ load(&_entries[index], buffer, size);
+}
+
+void BlbArchive::load(BlbArchiveEntry *entry, byte *buffer, uint32 size) {
+ Common::StackLock lock(_mutex);
+
+ _fd.seek(entry->offset);
+
+ switch (entry->comprType) {
+ case 1: // Uncompressed
+ if (size == 0)
+ size = entry->diskSize;
+ _fd.read(buffer, size);
+ break;
+ case 3: // DCL-compressed
+ if (!Common::decompressDCL(&_fd, buffer, entry->diskSize, entry->size))
+ error("BlbArchive::load() Error during decompression of %08X", entry->fileHash);
+ break;
+ default:
+ error("BlbArchive::load() Unknown compression type %d", entry->comprType);
+ }
+
+}
+
+byte *BlbArchive::getEntryExtData(uint index) {
+ return getEntryExtData(&_entries[index]);
+}
+
+byte *BlbArchive::getEntryExtData(BlbArchiveEntry *entry) {
+ return entry->extData;
+}
+
+Common::SeekableReadStream *BlbArchive::createStream(uint index) {
+ return createStream(&_entries[index]);
+}
+
+Common::SeekableReadStream *BlbArchive::createStream(BlbArchiveEntry *entry) {
+ return new SafeMutexedSeekableSubReadStream(&_fd, entry->offset, entry->offset + entry->diskSize,
+ DisposeAfterUse::NO, _mutex);
+}
+
+} // End of namespace Neverhood
diff --git a/engines/neverhood/blbarchive.h b/engines/neverhood/blbarchive.h
new file mode 100644
index 0000000000..620b12b8ac
--- /dev/null
+++ b/engines/neverhood/blbarchive.h
@@ -0,0 +1,77 @@
+/* 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_BLBARCHIVE_H
+#define NEVERHOOD_BLBARCHIVE_H
+
+#include "common/array.h"
+#include "common/file.h"
+#include "common/mutex.h"
+#include "common/stream.h"
+#include "common/substream.h"
+#include "neverhood/neverhood.h"
+
+namespace Neverhood {
+
+struct BlbHeader {
+ uint32 id1;
+ uint16 id2;
+ uint16 extDataSize;
+ int32 fileSize;
+ uint32 fileCount;
+};
+
+struct BlbArchiveEntry {
+ uint32 fileHash;
+ byte type;
+ byte comprType;
+ byte *extData;
+ uint32 timeStamp;
+ uint32 offset;
+ uint32 diskSize;
+ uint32 size;
+};
+
+class BlbArchive {
+public:
+ BlbArchive();
+ ~BlbArchive();
+ void open(const Common::String &filename);
+ void load(uint index, byte *buffer, uint32 size);
+ void load(BlbArchiveEntry *entry, byte *buffer, uint32 size);
+ byte *getEntryExtData(uint index);
+ byte *getEntryExtData(BlbArchiveEntry *entry);
+ uint32 getSize(uint index) { return _entries[index].size; }
+ BlbArchiveEntry *getEntry(uint index) { return &_entries[index]; }
+ uint getCount() { return _entries.size(); }
+ Common::SeekableReadStream *createStream(uint index);
+ Common::SeekableReadStream *createStream(BlbArchiveEntry *entry);
+private:
+ Common::File _fd;
+ Common::Mutex _mutex;
+ Common::Array<BlbArchiveEntry> _entries;
+ byte *_extData;
+};
+
+} // End of namespace Neverhood
+
+#endif /* NEVERHOOD_BLBARCHIVE_H */
diff --git a/engines/neverhood/detection.cpp b/engines/neverhood/detection.cpp
new file mode 100644
index 0000000000..5f860f8519
--- /dev/null
+++ b/engines/neverhood/detection.cpp
@@ -0,0 +1,271 @@
+/* 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 "base/plugins.h"
+
+#include "engines/advancedDetector.h"
+#include "common/file.h"
+
+#include "neverhood/neverhood.h"
+
+
+namespace Neverhood {
+
+struct NeverhoodGameDescription {
+ ADGameDescription desc;
+
+ int gameID;
+ int gameType;
+ uint32 features;
+ uint16 version;
+};
+
+const char *NeverhoodEngine::getGameId() const {
+ return _gameDescription->desc.gameid;
+}
+
+uint32 NeverhoodEngine::getFeatures() const {
+ return _gameDescription->features;
+}
+
+Common::Platform NeverhoodEngine::getPlatform() const {
+ return _gameDescription->desc.platform;
+}
+
+uint16 NeverhoodEngine::getVersion() const {
+ return _gameDescription->version;
+}
+
+bool NeverhoodEngine::isDemo() const {
+ return _gameDescription->desc.flags & ADGF_DEMO;
+}
+
+}
+
+static const PlainGameDescriptor neverhoodGames[] = {
+ {"neverhood", "The Neverhood Chronicles"},
+ {0, 0}
+};
+
+namespace Neverhood {
+
+static const NeverhoodGameDescription gameDescriptions[] = {
+
+ {
+ // Neverhood English version
+ {
+ "neverhood",
+ 0,
+ AD_ENTRY1s("hd.blb", "22958d968458c9ff221aee38577bb2b2", 4279716),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO1(GUIO_NONE)
+ },
+ 0,
+ 0,
+ 0,
+ 0,
+ },
+
+ {
+ // Neverhood English demo version
+ {
+ "neverhood",
+ "Demo",
+ AD_ENTRY1s("nevdemo.blb", "05b735cfb1086892bec79b54dca5545b", 22564568),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_DEMO,
+ GUIO1(GUIO_NONE)
+ },
+ 0,
+ 0,
+ 0,
+ 0,
+ },
+
+ {
+ // Neverhood Russian version. Dyadyushka Risech
+ {
+ "neverhood",
+ "DR",
+ AD_ENTRY1s("hd.blb", "787951bf094aad9962291e69a707bdde", 4248635),
+ Common::RU_RUS,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO1(GUIO_NONE)
+ },
+ 0,
+ 0,
+ 0,
+ 0,
+ },
+
+ {
+ // Neverhood Russian version. Fargus
+ {
+ "neverhood",
+ "Fargus",
+ AD_ENTRY1s("hd.blb", "c87c69db423f560d3708e9de78751a7f", 4425816),
+ Common::RU_RUS,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO1(GUIO_NONE)
+ },
+ 0,
+ 0,
+ 0,
+ 0,
+ },
+
+ { AD_TABLE_END_MARKER, 0, 0, 0, 0 }
+};
+
+} // End of namespace Neverhood
+
+class NeverhoodMetaEngine : public AdvancedMetaEngine {
+public:
+ NeverhoodMetaEngine() : AdvancedMetaEngine(Neverhood::gameDescriptions, sizeof(Neverhood::NeverhoodGameDescription), neverhoodGames) {
+ _singleid = "neverhood";
+ _guioptions = GUIO2(GUIO_NOSUBTITLES, GUIO_NOMIDI);
+ }
+
+ virtual const char *getName() const {
+ return "Neverhood Engine";
+ }
+
+ virtual const char *getOriginalCopyright() const {
+ return "Neverhood (C) The Neverhood, Inc.";
+ }
+
+ virtual bool hasFeature(MetaEngineFeature f) const;
+ virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
+
+ SaveStateList listSaves(const char *target) const;
+ virtual int getMaximumSaveSlot() const;
+ void removeSaveState(const char *target, int slot) const;
+ SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
+
+};
+
+bool NeverhoodMetaEngine::hasFeature(MetaEngineFeature f) const {
+ return
+ (f == kSupportsListSaves) ||
+ (f == kSupportsLoadingDuringStartup) ||
+ (f == kSupportsDeleteSave) ||
+ (f == kSavesSupportMetaInfo) ||
+ (f == kSavesSupportThumbnail) ||
+ (f == kSavesSupportCreationDate) ||
+ (f == kSavesSupportPlayTime);
+}
+
+bool Neverhood::NeverhoodEngine::hasFeature(EngineFeature f) const {
+ return
+ (f == kSupportsRTL) ||
+ (f == kSupportsLoadingDuringRuntime) ||
+ (f == kSupportsSavingDuringRuntime);
+}
+
+bool NeverhoodMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
+ const Neverhood::NeverhoodGameDescription *gd = (const Neverhood::NeverhoodGameDescription *)desc;
+ if (gd) {
+ *engine = new Neverhood::NeverhoodEngine(syst, gd);
+ }
+ return gd != 0;
+}
+
+SaveStateList NeverhoodMetaEngine::listSaves(const char *target) const {
+ Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
+ Neverhood::NeverhoodEngine::SaveHeader header;
+ Common::String pattern = target;
+ pattern += ".???";
+
+ Common::StringArray filenames;
+ filenames = saveFileMan->listSavefiles(pattern.c_str());
+ Common::sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..)
+
+ SaveStateList saveList;
+ for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); file++) {
+ // Obtain the last 3 digits of the filename, since they correspond to the save slot
+ int slotNum = atoi(file->c_str() + file->size() - 3);
+ if (slotNum >= 0 && slotNum <= 999) {
+ Common::InSaveFile *in = saveFileMan->openForLoading(file->c_str());
+ if (in) {
+ if (Neverhood::NeverhoodEngine::readSaveHeader(in, false, header) == Neverhood::NeverhoodEngine::kRSHENoError) {
+ saveList.push_back(SaveStateDescriptor(slotNum, header.description));
+ }
+ delete in;
+ }
+ }
+ }
+
+ return saveList;
+}
+
+int NeverhoodMetaEngine::getMaximumSaveSlot() const {
+ return 999;
+}
+
+void NeverhoodMetaEngine::removeSaveState(const char *target, int slot) const {
+ Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
+ Common::String filename = Neverhood::NeverhoodEngine::getSavegameFilename(target, slot);
+ saveFileMan->removeSavefile(filename.c_str());
+}
+
+SaveStateDescriptor NeverhoodMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
+ Common::String filename = Neverhood::NeverhoodEngine::getSavegameFilename(target, slot);
+ Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(filename.c_str());
+
+ if (in) {
+ Neverhood::NeverhoodEngine::SaveHeader header;
+ Neverhood::NeverhoodEngine::kReadSaveHeaderError error;
+
+ error = Neverhood::NeverhoodEngine::readSaveHeader(in, true, header);
+ delete in;
+
+ if (error == Neverhood::NeverhoodEngine::kRSHENoError) {
+ SaveStateDescriptor desc(slot, header.description);
+
+ desc.setDeletableFlag(false);
+ desc.setWriteProtectedFlag(false);
+ desc.setThumbnail(header.thumbnail);
+ int day = (header.saveDate >> 24) & 0xFF;
+ int month = (header.saveDate >> 16) & 0xFF;
+ int year = header.saveDate & 0xFFFF;
+ desc.setSaveDate(year, month, day);
+ int hour = (header.saveTime >> 16) & 0xFF;
+ int minutes = (header.saveTime >> 8) & 0xFF;
+ desc.setSaveTime(hour, minutes);
+ desc.setPlayTime(header.playTime * 1000);
+ return desc;
+ }
+ }
+
+ return SaveStateDescriptor();
+}
+
+#if PLUGIN_ENABLED_DYNAMIC(NEVERHOOD)
+ REGISTER_PLUGIN_DYNAMIC(NEVERHOOD, PLUGIN_TYPE_ENGINE, NeverhoodMetaEngine);
+#else
+ REGISTER_PLUGIN_STATIC(NEVERHOOD, PLUGIN_TYPE_ENGINE, NeverhoodMetaEngine);
+#endif
diff --git a/engines/neverhood/diskplayerscene.cpp b/engines/neverhood/diskplayerscene.cpp
new file mode 100644
index 0000000000..d972943759
--- /dev/null
+++ b/engines/neverhood/diskplayerscene.cpp
@@ -0,0 +1,511 @@
+/* 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/diskplayerscene.h"
+#include "neverhood/mouse.h"
+
+namespace Neverhood {
+
+static const uint32 kDiskplayerPaletteFileHashes[] = {
+ 0x03B78240,
+ 0x34B32B08,
+ 0x4F2569D4,
+ 0x07620590,
+ 0x38422401
+};
+
+static const byte kDiskplayerInitArray[] = {
+ 2, 1, 4, 5, 3, 11, 8, 6, 7, 9, 10, 17, 16, 18, 19, 20, 15, 14, 13, 12
+};
+
+static const uint32 kDiskplayerSmackerFileHashes[] = {
+ 0x010A2810,
+ 0x020A2810,
+ 0x040A2810,
+ 0x080A2810,
+ 0x100A2810,
+ 0x200A2810,
+ 0x400A2810,
+ 0x800A2810,
+ 0x000A2811,
+ 0x010C2810,
+ 0x020C2810,
+ 0x040C2810,
+ 0x080C2810,
+ 0x100C2810,
+ 0x200C2810,
+ 0x400C2810,
+ 0x800C2810,
+ 0x000C2811,
+ 0x000C2812,
+ 0x02002810,
+ 0x04002810
+};
+
+static const uint32 kDiskplayerSlotFileHashes1[] = {
+ 0x81312280,
+ 0x01312281,
+ 0x01312282,
+ 0x01312284,
+ 0x01312288,
+ 0x01312290,
+ 0x013122A0,
+ 0x013122C0,
+ 0x01312200,
+ 0x82312280,
+ 0x02312281,
+ 0x02312282,
+ 0x02312284,
+ 0x02312288,
+ 0x02312290,
+ 0x023122A0,
+ 0x023122C0,
+ 0x02312200,
+ 0x02312380,
+ 0x04312281
+};
+
+static const uint32 kDiskplayerSlotFileHashes2[] = {
+ 0x90443A00,
+ 0x90443A18,
+ 0x90443A28,
+ 0x90443A48,
+ 0x90443A88,
+ 0x90443B08,
+ 0x90443808,
+ 0x90443E08,
+ 0x90443208,
+ 0xA0443A00,
+ 0xA0443A18,
+ 0xA0443A28,
+ 0xA0443A48,
+ 0xA0443A88,
+ 0xA0443B08,
+ 0xA0443808,
+ 0xA0443E08,
+ 0xA0443208,
+ 0xA0442A08,
+ 0xC0443A18
+};
+
+static const uint32 kDiskplayerSlotFileHashes3[] = {
+ 0x10357320,
+ 0x10557320,
+ 0x10957320,
+ 0x11157320,
+ 0x12157320,
+ 0x14157320,
+ 0x18157320,
+ 0x00157320,
+ 0x30157320,
+ 0x1035B320,
+ 0x1055B320,
+ 0x1095B320,
+ 0x1115B320,
+ 0x1215B320,
+ 0x1415B320,
+ 0x1815B320,
+ 0x0015B320,
+ 0x3015B320,
+ 0x5015B320,
+ 0x10543320
+};
+
+static const uint32 kDiskplayerSlotFileHashes4[] = {
+ 0xDC8020E4,
+ 0xDC802164,
+ 0xDC802264,
+ 0xDC802464,
+ 0xDC802864,
+ 0xDC803064,
+ 0xDC800064,
+ 0xDC806064,
+ 0xDC80A064,
+ 0xDC8020E7,
+ 0xDC802167,
+ 0xDC802267,
+ 0xDC802467,
+ 0xDC802867,
+ 0xDC803067,
+ 0xDC800067,
+ 0xDC806067,
+ 0xDC80A067,
+ 0xDC812067,
+ 0xDC802161
+};
+
+AsDiskplayerSceneKey::AsDiskplayerSceneKey(NeverhoodEngine *vm)
+ : AnimatedSprite(vm, 1100) {
+
+ createSurface1(0x100B90B4, 1200);
+ _x = 211;
+ _y = 195;
+ startAnimation(0x100B90B4, 0, -1);
+ _newStickFrameIndex = 0;
+ _needRefresh = true;
+ updatePosition();
+ setVisible(false);
+}
+
+uint32 AsDiskplayerSceneKey::handleMessage(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = Sprite::handleMessage(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x3002:
+ gotoNextState();
+ break;
+ }
+ return messageResult;
+}
+
+void AsDiskplayerSceneKey::stDropKey() {
+ startAnimation(0x100B90B4, 0, -1);
+ SetUpdateHandler(&AnimatedSprite::update);
+ SetMessageHandler(&AsDiskplayerSceneKey::handleMessage);
+ NextState(&AsDiskplayerSceneKey::stDropKeyDone);
+ setVisible(true);
+}
+
+void AsDiskplayerSceneKey::stDropKeyDone() {
+ stopAnimation();
+ SetUpdateHandler(&AnimatedSprite::update);
+ SetMessageHandler(&Sprite::handleMessage);
+ setVisible(false);
+}
+
+DiskplayerPlayButton::DiskplayerPlayButton(NeverhoodEngine *vm, DiskplayerScene *diskplayerScene)
+ : StaticSprite(vm, 1400), _diskplayerScene(diskplayerScene), _isPlaying(false) {
+
+ loadSprite(0x24A4A664, kSLFDefDrawOffset | kSLFDefPosition | kSLFDefCollisionBoundsOffset, 400);
+ setVisible(false);
+ loadSound(0, 0x44043000);
+ loadSound(1, 0x44045000);
+ SetMessageHandler(&DiskplayerPlayButton::handleMessage);
+}
+
+uint32 DiskplayerPlayButton::handleMessage(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = 0;
+ Sprite::handleMessage(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x1011:
+ if (!_diskplayerScene->getDropKey()) {
+ if (_isPlaying) {
+ sendMessage(_diskplayerScene, 0x2001, 0);
+ release();
+ } else {
+ sendMessage(_diskplayerScene, 0x2000, 0);
+ press();
+ }
+ }
+ updatePosition();
+ messageResult = 1;
+ break;
+ }
+ return messageResult;
+}
+
+void DiskplayerPlayButton::press() {
+ if (!_isPlaying) {
+ setVisible(true);
+ updatePosition();
+ playSound(0);
+ _isPlaying = true;
+ }
+}
+
+void DiskplayerPlayButton::release() {
+ if (_isPlaying) {
+ setVisible(false);
+ updatePosition();
+ playSound(1);
+ _isPlaying = false;
+ }
+}
+
+DiskplayerSlot::DiskplayerSlot(NeverhoodEngine *vm, DiskplayerScene *diskplayerScene, int slotIndex, bool isAvailable)
+ : Entity(vm, 0), _diskplayerScene(diskplayerScene), _isLocked(false), _isBlinking(false),
+ _blinkCountdown(0), _initialBlinkCountdown(2), _inactiveSlot(NULL), _appearSlot(NULL), _activeSlot(NULL) {
+
+ if (isAvailable && slotIndex < 20) {
+ _inactiveSlot = _diskplayerScene->addSprite(new StaticSprite(_vm, kDiskplayerSlotFileHashes1[slotIndex], 1100));
+ _appearSlot = _diskplayerScene->addSprite(new StaticSprite(_vm, kDiskplayerSlotFileHashes2[slotIndex], 1000));
+ _activeSlot = _diskplayerScene->addSprite(new StaticSprite(_vm, kDiskplayerSlotFileHashes3[slotIndex], 1100));
+ _inactiveSlot->setVisible(false);
+ _appearSlot->setVisible(false);
+ _activeSlot->setVisible(false);
+ loadSound(0, 0x46210074);
+ setSoundPan(0, slotIndex * 100 / 19);
+ } else if (slotIndex != 20) {
+ _activeSlot = _diskplayerScene->addSprite(new StaticSprite(_vm, kDiskplayerSlotFileHashes4[slotIndex], 1100));
+ _activeSlot->setVisible(false);
+ }
+ SetUpdateHandler(&DiskplayerSlot::update);
+}
+
+void DiskplayerSlot::update() {
+ if (_blinkCountdown != 0 && (--_blinkCountdown == 0)) {
+ if (_isBlinking) {
+ if (_inactiveSlot)
+ _inactiveSlot->setVisible(true);
+ if (_activeSlot)
+ _activeSlot->setVisible(false);
+ _blinkCountdown = _initialBlinkCountdown / 2;
+ } else {
+ if (_inactiveSlot)
+ _inactiveSlot->setVisible(false);
+ if (_activeSlot)
+ _activeSlot->setVisible(true);
+ _blinkCountdown = _initialBlinkCountdown;
+ }
+ _isBlinking = !_isBlinking;
+ }
+}
+
+void DiskplayerSlot::appear() {
+ if (_inactiveSlot)
+ _inactiveSlot->setVisible(true);
+ if (_appearSlot)
+ _appearSlot->setVisible(true);
+ if (_inactiveSlot)
+ playSound(0);
+}
+
+void DiskplayerSlot::play() {
+ if (!_isLocked) {
+ if (_inactiveSlot)
+ _inactiveSlot->setVisible(false);
+ if (_activeSlot)
+ _activeSlot->setVisible(true);
+ _isBlinking = true;
+ _blinkCountdown = 0;
+ }
+}
+
+void DiskplayerSlot::activate() {
+ if (!_isLocked)
+ _blinkCountdown = _initialBlinkCountdown;
+}
+
+void DiskplayerSlot::stop() {
+ if (!_isLocked) {
+ if (_inactiveSlot)
+ _inactiveSlot->setVisible(true);
+ if (_activeSlot)
+ _activeSlot->setVisible(false);
+ _isBlinking = false;
+ _blinkCountdown = 0;
+ }
+}
+
+DiskplayerScene::DiskplayerScene(NeverhoodEngine *vm, Module *parentModule, int paletteIndex)
+ : Scene(vm, parentModule), _diskIndex(0), _appearCountdown(0), _tuneInCountdown(0),
+ _hasAllDisks(false), _dropKey(false), _inputDisabled(true), _updateStatus(kUSStopped) {
+
+ int availableDisksCount = 0;
+
+ setBackground(0x8A000044);
+ setPalette(kDiskplayerPaletteFileHashes[paletteIndex]);
+
+ _ssPlayButton = insertSprite<DiskplayerPlayButton>(this);
+ addCollisionSprite(_ssPlayButton);
+
+ _asKey = insertSprite<AsDiskplayerSceneKey>();
+
+ for (int i = 0; i < 20; i++) {
+ _diskAvailable[i] = false;
+ if (getSubVar(VA_IS_TAPE_INSERTED, i))
+ availableDisksCount++;
+ }
+
+ for (int i = 0; i < availableDisksCount; i++)
+ _diskAvailable[kDiskplayerInitArray[i] - 1] = true;
+
+ for (int slotIndex = 0; slotIndex < 20; slotIndex++) {
+ _diskSlots[slotIndex] = new DiskplayerSlot(_vm, this, slotIndex, _diskAvailable[slotIndex]);
+ addEntity(_diskSlots[slotIndex]);
+ }
+
+ _hasAllDisks = availableDisksCount == 20;
+
+ if (_hasAllDisks && !getGlobalVar(V_HAS_FINAL_KEY))
+ _dropKey = true;
+
+ _finalDiskSlot = new DiskplayerSlot(_vm, this, 20, false);
+ addEntity(_finalDiskSlot);
+
+ insertPuzzleMouse(0x000408A8, 20, 620);
+ showMouse(false);
+
+ _diskSmackerPlayer = new SmackerPlayer(_vm, this, 0x08288103, false, true);
+ addEntity(_diskSmackerPlayer);
+ addSurface(_diskSmackerPlayer->getSurface());
+ _diskSmackerPlayer->setDrawPos(154, 86);
+ _vm->_screen->setSmackerDecoder(_diskSmackerPlayer->getSmackerDecoder());
+
+ _palette->usePalette();
+
+ SetMessageHandler(&DiskplayerScene::handleMessage);
+ SetUpdateHandler(&DiskplayerScene::update);
+ _appearCountdown = 6;
+
+}
+
+void DiskplayerScene::update() {
+ Scene::update();
+
+ if (_updateStatus == kUSTuningIn && _diskSmackerPlayer->isDone()) {
+ if (_diskAvailable[_diskIndex])
+ playDisk();
+ else
+ playStatic();
+ } else if (_updateStatus == kUSPlaying && _diskSmackerPlayer->isDone()) {
+ _diskSlots[_diskIndex]->stop();
+ _diskIndex++;
+ if (_hasAllDisks) {
+ if (_diskIndex != 20) {
+ playDisk();
+ } else if (_dropKey) {
+ playDisk();
+ _updateStatus = kUSPlayingFinal;
+ } else {
+ _diskIndex = 0;
+ stop();
+ }
+ } else if (_diskIndex != 20) {
+ tuneIn();
+ } else {
+ _diskIndex = 0;
+ stop();
+ }
+ } else if (_updateStatus == kUSPlayingFinal) {
+ if (_diskSmackerPlayer->getFrameNumber() == 133) {
+ _asKey->stDropKey();
+ setGlobalVar(V_HAS_FINAL_KEY, 1);
+ } else if (_diskSmackerPlayer->isDone()) {
+ for (int i = 0; i < 20; i++) {
+ _diskSlots[i]->setLocked(false);
+ _diskSlots[i]->stop();
+ }
+ _diskIndex = 0;
+ stop();
+ showMouse(true);
+ _dropKey = false;
+ }
+ }
+
+ if (_appearCountdown != 0 && (--_appearCountdown == 0)) {
+ _diskSlots[_diskIndex]->appear();
+ if (_dropKey) {
+ _diskSlots[_diskIndex]->activate();
+ _diskSlots[_diskIndex]->setLocked(true);
+ }
+ _diskIndex++;
+ while (!_diskAvailable[_diskIndex] && _diskIndex < 19)
+ _diskIndex++;
+ if (_diskIndex < 20) {
+ _appearCountdown = 1;
+ } else {
+ _diskIndex = 0;
+ _inputDisabled = false;
+ if (_dropKey) {
+ _ssPlayButton->press();
+ _tuneInCountdown = 2;
+ } else {
+ showMouse(true);
+ _diskSlots[_diskIndex]->activate();
+ }
+ }
+ }
+
+ if (_tuneInCountdown != 0 && (--_tuneInCountdown == 0))
+ playDisk();
+
+}
+
+uint32 DiskplayerScene::handleMessage(int messageNum, const MessageParam &param, Entity *sender) {
+ Scene::handleMessage(messageNum, param, sender);
+ if (!_inputDisabled) {
+ switch (messageNum) {
+ case 0x0001:
+ if (param.asPoint().x <= 20 || param.asPoint().x >= 620) {
+ sendMessage(_parentModule, 0x1009, 0);
+ } else if (!_dropKey &&
+ param.asPoint().x > 38 && param.asPoint().x < 598 &&
+ param.asPoint().y > 400 && param.asPoint().y < 460) {
+
+ _diskSlots[_diskIndex]->stop();
+ _diskIndex = (param.asPoint().x - 38) / 28;
+ _diskSlots[_diskIndex]->activate();
+ if (_updateStatus == kUSPlaying) {
+ if (_diskAvailable[_diskIndex])
+ playDisk();
+ else
+ playStatic();
+ }
+ }
+ break;
+ case 0x2000:
+ tuneIn();
+ break;
+ case 0x2001:
+ stop();
+ break;
+ }
+ }
+ return 0;
+}
+
+void DiskplayerScene::stop() {
+ _diskSmackerPlayer->open(0x08288103, true);
+ _vm->_screen->setSmackerDecoder(_diskSmackerPlayer->getSmackerDecoder());
+ _palette->usePalette();
+ _ssPlayButton->release();
+ _updateStatus = kUSStopped;
+ _diskSlots[_diskIndex]->activate();
+}
+
+void DiskplayerScene::tuneIn() {
+ _diskSmackerPlayer->open(0x900001C1, false);
+ _vm->_screen->setSmackerDecoder(_diskSmackerPlayer->getSmackerDecoder());
+ _palette->usePalette();
+ _ssPlayButton->release();
+ _updateStatus = kUSTuningIn;
+ _diskSlots[_diskIndex]->activate();
+}
+
+void DiskplayerScene::playDisk() {
+ _diskSmackerPlayer->open(kDiskplayerSmackerFileHashes[_diskIndex], false);
+ _vm->_screen->setSmackerDecoder(_diskSmackerPlayer->getSmackerDecoder());
+ _palette->usePalette();
+ _updateStatus = kUSPlaying;
+ _diskSlots[_diskIndex]->play();
+}
+
+void DiskplayerScene::playStatic() {
+ _diskSmackerPlayer->open(0x90000101, false);
+ _vm->_screen->setSmackerDecoder(_diskSmackerPlayer->getSmackerDecoder());
+ _palette->usePalette();
+ _ssPlayButton->release();
+ _updateStatus = kUSPlaying;
+ _diskSlots[_diskIndex]->activate();
+}
+
+} // End of namespace Neverhood
diff --git a/engines/neverhood/diskplayerscene.h b/engines/neverhood/diskplayerscene.h
new file mode 100644
index 0000000000..f3fd9ea874
--- /dev/null
+++ b/engines/neverhood/diskplayerscene.h
@@ -0,0 +1,110 @@
+/* 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_DISKPLAYERSCENE_H
+#define NEVERHOOD_DISKPLAYERSCENE_H
+
+#include "neverhood/neverhood.h"
+#include "neverhood/resourceman.h"
+#include "neverhood/scene.h"
+#include "neverhood/smackerplayer.h"
+
+namespace Neverhood {
+
+class DiskplayerScene;
+
+class AsDiskplayerSceneKey : public AnimatedSprite {
+public:
+ AsDiskplayerSceneKey(NeverhoodEngine *vm);
+ void stDropKey();
+protected:
+ uint32 handleMessage(int messageNum, const MessageParam &param, Entity *sender);
+ void stDropKeyDone();
+};
+
+class DiskplayerPlayButton : public StaticSprite {
+public:
+ DiskplayerPlayButton(NeverhoodEngine *vm, DiskplayerScene *diskplayerScene);
+ void press();
+ void release();
+protected:
+ DiskplayerScene *_diskplayerScene;
+ bool _isPlaying;
+ uint32 handleMessage(int messageNum, const MessageParam &param, Entity *sender);
+};
+
+class DiskplayerSlot : public Entity {
+public:
+ DiskplayerSlot(NeverhoodEngine *vm, DiskplayerScene *diskplayerScene, int slotIndex, bool isAvailable);
+ void activate();
+ void stop();
+ void appear();
+ void play();
+ void setLocked(bool isLocked) { _isLocked = isLocked; }
+protected:
+ DiskplayerScene *_diskplayerScene;
+ Sprite *_inactiveSlot;
+ Sprite *_appearSlot;
+ Sprite *_activeSlot;
+ int _initialBlinkCountdown;
+ int _blinkCountdown;
+ bool _isLocked;
+ bool _isBlinking;
+ void update();
+};
+
+enum {
+ kUSStopped = 0,
+ kUSTuningIn = 1,
+ kUSPlaying = 2,
+ kUSPlayingFinal = 3
+};
+
+class DiskplayerScene : public Scene {
+public:
+ DiskplayerScene(NeverhoodEngine *vm, Module *parentModule, int paletteIndex);
+ bool getDropKey() const { return _dropKey; }
+protected:
+ SmackerPlayer *_diskSmackerPlayer;
+ DiskplayerPlayButton *_ssPlayButton;
+ AsDiskplayerSceneKey *_asKey;
+ DiskplayerSlot *_diskSlots[20];
+ DiskplayerSlot *_finalDiskSlot;
+ int _updateStatus;
+ bool _diskAvailable[20];
+ int _diskIndex;
+ int _appearCountdown;
+ int _tuneInCountdown;
+ bool _hasAllDisks;
+ bool _inputDisabled;
+ bool _dropKey;
+ void update();
+ uint32 handleMessage(int messageNum, const MessageParam &param, Entity *sender);
+ void stop();
+ void tuneIn();
+ void playDisk();
+ void playStatic();
+};
+
+} // End of namespace Neverhood
+
+#endif /* NEVERHOOD_DISKPLAYERSCENE_H */
diff --git a/engines/neverhood/entity.cpp b/engines/neverhood/entity.cpp
new file mode 100644
index 0000000000..8b1298916c
--- /dev/null
+++ b/engines/neverhood/entity.cpp
@@ -0,0 +1,159 @@
+/* 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/entity.h"
+#include "neverhood/sound.h"
+
+namespace Neverhood {
+
+uint32 MessageParam::asInteger() const {
+ assert(_type == mptInteger);
+ return _integer;
+}
+
+NPoint MessageParam::asPoint() const {
+ assert(_type == mptInteger || _type == mptPoint);
+ if (_type == mptInteger) {
+ NPoint pt;
+ pt.x = _integer & 0xFFFF;
+ pt.y = (_integer >> 16) & 0xFFFF;
+ return pt;
+ }
+ return _point;
+}
+
+Entity *MessageParam::asEntity() const {
+ assert(_type == mptEntity);
+ return _entity;
+}
+
+Entity::Entity(NeverhoodEngine *vm, int priority)
+ : _vm(vm), _updateHandlerCb(NULL), _messageHandlerCb(NULL), _priority(priority), _soundResources(NULL) {
+}
+
+Entity::~Entity() {
+ deleteSoundResources();
+}
+
+void Entity::draw() {
+ // Empty
+}
+
+void Entity::handleUpdate() {
+ debug(5, "handleUpdate() -> [%s]", _updateHandlerCbName.c_str());
+ if (_updateHandlerCb)
+ (this->*_updateHandlerCb)();
+}
+
+uint32 Entity::receiveMessage(int messageNum, const MessageParam &param, Entity *sender) {
+ debug(5, "receiveMessage(%04X) -> [%s]", messageNum, _messageHandlerCbName.c_str());
+ return _messageHandlerCb ? (this->*_messageHandlerCb)(messageNum, param, sender) : 0;
+}
+
+uint32 Entity::sendMessage(Entity *receiver, int messageNum, const MessageParam &param) {
+ return receiver ? receiver->receiveMessage(messageNum, param, this) : 0;
+}
+
+uint32 Entity::sendMessage(Entity *receiver, int messageNum, uint32 param) {
+ return sendMessage(receiver, messageNum, MessageParam(param));
+}
+
+uint32 Entity::sendPointMessage(Entity *receiver, int messageNum, const NPoint &param) {
+ return sendMessage(receiver, messageNum, MessageParam(param));
+}
+
+uint32 Entity::sendEntityMessage(Entity *receiver, int messageNum, Entity *param) {
+ return sendMessage(receiver, messageNum, MessageParam((Entity*)param));
+}
+
+uint32 Entity::getGlobalVar(uint32 nameHash) {
+ return _vm->_gameVars->getGlobalVar(nameHash);
+}
+
+void Entity::setGlobalVar(uint32 nameHash, uint32 value) {
+ _vm->_gameVars->setGlobalVar(nameHash, value);
+}
+
+uint32 Entity::getSubVar(uint32 nameHash, uint32 subNameHash) {
+ return _vm->_gameVars->getSubVar(nameHash, subNameHash);
+}
+
+void Entity::setSubVar(uint32 nameHash, uint32 subNameHash, uint32 value) {
+ _vm->_gameVars->setSubVar(nameHash, subNameHash, value);
+}
+
+void Entity::incGlobalVar(uint32 nameHash, int incrValue) {
+ setGlobalVar(nameHash, getGlobalVar(nameHash) + incrValue);
+}
+
+void Entity::incSubVar(uint32 nameHash, uint32 subNameHash, int incrValue) {
+ setSubVar(nameHash, subNameHash, getSubVar(nameHash, subNameHash) + incrValue);
+}
+
+SoundResource *Entity::getSoundResource(uint index) {
+ assert(index < kMaxSoundResources);
+ if (!_soundResources) {
+ _soundResources = new SoundResource*[kMaxSoundResources];
+ for (uint i = 0; i < kMaxSoundResources; ++i)
+ _soundResources[i] = NULL;
+ }
+ if (!_soundResources[index])
+ _soundResources[index] = new SoundResource(_vm);
+ return _soundResources[index];
+}
+
+void Entity::loadSound(uint index, uint32 fileHash) {
+ getSoundResource(index)->load(fileHash);
+}
+
+void Entity::playSound(uint index, uint32 fileHash) {
+ if (fileHash)
+ getSoundResource(index)->play(fileHash);
+ else
+ getSoundResource(index)->play();
+}
+
+void Entity::stopSound(uint index) {
+ getSoundResource(index)->stop();
+}
+
+bool Entity::isSoundPlaying(uint index) {
+ return getSoundResource(index)->isPlaying();
+}
+
+void Entity::setSoundVolume(uint index, int volume) {
+ getSoundResource(index)->setVolume(volume);
+}
+
+void Entity::setSoundPan(uint index, int pan) {
+ getSoundResource(index)->setPan(pan);
+}
+
+void Entity::deleteSoundResources() {
+ if (_soundResources) {
+ for (uint i = 0; i < kMaxSoundResources; ++i)
+ delete _soundResources[i];
+ delete[] _soundResources;
+ }
+}
+
+} // End of namespace Neverhood
diff --git a/engines/neverhood/entity.h b/engines/neverhood/entity.h
new file mode 100644
index 0000000000..cba1bb9a7f
--- /dev/null
+++ b/engines/neverhood/entity.h
@@ -0,0 +1,120 @@
+/* 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_ENTITY_H
+#define NEVERHOOD_ENTITY_H
+
+#include "common/str.h"
+#include "neverhood/neverhood.h"
+#include "neverhood/gamevars.h"
+#include "neverhood/graphics.h"
+#include "neverhood/sound.h"
+
+namespace Neverhood {
+
+class Entity;
+class SoundResource;
+
+enum MessageParamType {
+ mptInteger,
+ mptPoint,
+ mptEntity
+};
+
+struct MessageParam {
+public:
+ MessageParam(uint32 value) : _type(mptInteger), _integer(value) {}
+ MessageParam(NPoint value) : _type(mptPoint), _point(value) {}
+ MessageParam(Entity *entity) : _type(mptEntity), _entity(entity) {}
+ uint32 asInteger() const;
+ NPoint asPoint() const;
+ Entity *asEntity() const;
+protected:
+ union {
+ uint32 _integer;
+ NPoint _point;
+ Entity *_entity;
+ };
+ MessageParamType _type;
+};
+
+// TODO: Disable heavy debug stuff in release mode
+
+#define SetUpdateHandler(handler) \
+ do { \
+ _updateHandlerCb = static_cast <void (Entity::*)(void)> (handler); \
+ debug(5, "SetUpdateHandler(" #handler ")"); \
+ _updateHandlerCbName = #handler; \
+ } while (0)
+
+#define SetMessageHandler(handler) \
+ do { \
+ _messageHandlerCb = static_cast <uint32 (Entity::*)(int messageNum, const MessageParam &param, Entity *sender)> (handler); \
+ debug(5, "SetMessageHandler(" #handler ")"); \
+ _messageHandlerCbName = #handler; \
+ } while (0)
+
+const uint kMaxSoundResources = 16;
+
+class Entity {
+public:
+ Common::String _updateHandlerCbName;
+ Common::String _messageHandlerCbName;
+ Entity(NeverhoodEngine *vm, int priority);
+ virtual ~Entity();
+ virtual void draw();
+ void handleUpdate();
+ uint32 receiveMessage(int messageNum, const MessageParam &param, Entity *sender);
+ // NOTE: These were overloaded before for the various message parameter types
+ // it caused some problems so each type gets its own sendMessage variant now
+ uint32 sendMessage(Entity *receiver, int messageNum, const MessageParam &param);
+ uint32 sendMessage(Entity *receiver, int messageNum, uint32 param);
+ uint32 sendPointMessage(Entity *receiver, int messageNum, const NPoint &param);
+ uint32 sendEntityMessage(Entity *receiver, int messageNum, Entity *param);
+ // Shortcuts for game variable access
+ uint32 getGlobalVar(uint32 nameHash);
+ void setGlobalVar(uint32 nameHash, uint32 value);
+ uint32 getSubVar(uint32 nameHash, uint32 subNameHash);
+ void setSubVar(uint32 nameHash, uint32 subNameHash, uint32 value);
+ void incGlobalVar(uint32 nameHash, int incrValue);
+ void incSubVar(uint32 nameHash, uint32 subNameHash, int incrValue);
+ int getPriority() const { return _priority; }
+ bool hasMessageHandler() const { return _messageHandlerCb != NULL; }
+protected:
+ void (Entity::*_updateHandlerCb)();
+ uint32 (Entity::*_messageHandlerCb)(int messageNum, const MessageParam &param, Entity *sender);
+ NeverhoodEngine *_vm;
+ int _priority;
+ SoundResource **_soundResources;
+ SoundResource *getSoundResource(uint index);
+ void loadSound(uint index, uint32 fileHash);
+ void playSound(uint index, uint32 fileHash = 0);
+ void stopSound(uint index);
+ bool isSoundPlaying(uint index);
+ void setSoundVolume(uint index, int volume);
+ void setSoundPan(uint index, int pan);
+ void deleteSoundResources();
+};
+
+} // End of namespace Neverhood
+
+#endif /* NEVERHOOD_ENTITY_H */
diff --git a/engines/neverhood/gamemodule.cpp b/engines/neverhood/gamemodule.cpp
new file mode 100644
index 0000000000..49682b0d29
--- /dev/null
+++ b/engines/neverhood/gamemodule.cpp
@@ -0,0 +1,849 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "neverhood/gamemodule.h"
+
+#include "neverhood/graphics.h"
+#include "neverhood/menumodule.h"
+#include "neverhood/modules/module1000.h"
+#include "neverhood/modules/module1100.h"
+#include "neverhood/modules/module1200.h"
+#include "neverhood/modules/module1300.h"
+#include "neverhood/modules/module1400.h"
+#include "neverhood/modules/module1500.h"
+#include "neverhood/modules/module1600.h"
+#include "neverhood/modules/module1700.h"
+#include "neverhood/modules/module1800.h"
+#include "neverhood/modules/module1900.h"
+#include "neverhood/modules/module2000.h"
+#include "neverhood/modules/module2100.h"
+#include "neverhood/modules/module2200.h"
+#include "neverhood/modules/module2300.h"
+#include "neverhood/modules/module2400.h"
+#include "neverhood/modules/module2500.h"
+#include "neverhood/modules/module2600.h"
+#include "neverhood/modules/module2700.h"
+#include "neverhood/modules/module2800.h"
+#include "neverhood/modules/module2900.h"
+#include "neverhood/modules/module3000.h"
+
+namespace Neverhood {
+
+static const uint32 kRadioMusicFileHashes[] = {
+ 0x82B22000,
+ 0x02B22004,
+ 0x42B22000,
+ 0x03322008,
+ 0x02B22001,
+ 0x02B22008,
+ 0x02B22020,
+ 0x03322001,
+ 0x03322002,
+ 0x03322004,
+ 0x03322040,
+ 0x02B22002,
+ 0x02B22010,
+ 0x03322010,
+ 0x02B22040,
+ 0x43322000,
+ 0x83322000,
+ 0x03322020
+};
+
+enum {
+ MENU_MODULE = 9999
+};
+
+GameModule::GameModule(NeverhoodEngine *vm)
+ : Module(vm, NULL), _moduleNum(-1), _prevChildObject(NULL), _prevModuleNum(-1),
+ _restoreGameRequested(false), _restartGameRequested(false), _canRequestMainMenu(true),
+ _mainMenuRequested(false) {
+
+ // Other initializations moved to actual engine class
+ _vm->_soundMan->playSoundThree(0x002D0031, 0x8861079);
+ SetMessageHandler(&GameModule::handleMessage);
+}
+
+GameModule::~GameModule() {
+ _vm->_soundMan->deleteSoundGroup(0x002D0031);
+ delete _childObject;
+ _childObject = NULL;
+}
+
+void GameModule::handleMouseMove(int16 x, int16 y) {
+ if (_childObject) {
+ NPoint mousePos;
+ mousePos.x = x;
+ mousePos.y = y;
+ debug(2, "GameModule::handleMouseMove(%d, %d)", x, y);
+ sendPointMessage(_childObject, 0, mousePos);
+ }
+}
+
+void GameModule::handleMouseDown(int16 x, int16 y) {
+ if (_childObject) {
+ NPoint mousePos;
+ mousePos.x = x;
+ mousePos.y = y;
+ debug(2, "GameModule::handleMouseDown(%d, %d)", x, y);
+ sendPointMessage(_childObject, 0x0001, mousePos);
+ }
+}
+
+void GameModule::handleMouseUp(int16 x, int16 y) {
+ if (_childObject) {
+ NPoint mousePos;
+ mousePos.x = x;
+ mousePos.y = y;
+ debug(2, "GameModule::handleMouseUp(%d, %d)", x, y);
+ sendPointMessage(_childObject, 0x0002, mousePos);
+ }
+}
+
+void GameModule::handleSpaceKey() {
+ if (_childObject) {
+ debug(2, "GameModule::handleSpaceKey()");
+ sendMessage(_childObject, 0x0009, 0);
+ }
+}
+
+void GameModule::handleAsciiKey(char key) {
+ if (_childObject) {
+ debug(2, "GameModule::handleAsciiKey()");
+ sendMessage(_childObject, 0x000A, (uint32)key);
+ }
+}
+
+void GameModule::handleKeyDown(Common::KeyCode keyCode) {
+ if (_childObject) {
+ if (keyCode == Common::KEYCODE_ESCAPE)
+ handleEscapeKey();
+ else if (keyCode == Common::KEYCODE_SPACE)
+ handleSpaceKey();
+ debug(2, "GameModule::handleKeyDown()");
+ sendMessage(_childObject, 0x000B, keyCode);
+ }
+}
+
+void GameModule::handleEscapeKey() {
+ if (_vm->isDemo())
+ _vm->quitGame();
+ else if (!_prevChildObject && _canRequestMainMenu)
+ _mainMenuRequested = true;
+ else if (_childObject)
+ sendMessage(_childObject, 0x000C, 0);
+}
+
+void GameModule::initKeySlotsPuzzle() {
+ if (!getSubVar(VA_IS_PUZZLE_INIT, 0x25400B10)) {
+ NonRepeatingRandomNumbers keySlots(_vm->_rnd, 16);
+ for (uint i = 0; i < 3; i++) {
+ setSubVar(VA_GOOD_KEY_SLOT_NUMBERS, i, keySlots.getNumber());
+ setSubVar(VA_CURR_KEY_SLOT_NUMBERS, i, keySlots.getNumber());
+ }
+ setSubVar(VA_IS_PUZZLE_INIT, 0x25400B10, 1);
+ }
+}
+
+void GameModule::initMemoryPuzzle() {
+ if (!getSubVar(VA_IS_PUZZLE_INIT, 0xC8606803)) {
+ NonRepeatingRandomNumbers diceIndices(_vm->_rnd, 3);
+ NonRepeatingRandomNumbers availableTiles(_vm->_rnd, 48);
+ NonRepeatingRandomNumbers tileSymbols(_vm->_rnd, 10);
+ for (uint32 i = 0; i < 3; i++)
+ setSubVar(VA_CURR_DICE_NUMBERS, i, 1);
+ // Set special symbols
+ // Symbol 5 is always one of the three special symbols
+ setSubVar(VA_DICE_MEMORY_SYMBOLS, diceIndices.getNumber(), 5);
+ tileSymbols.removeNumber(5);
+ for (int i = 0; i < 2; i++)
+ setSubVar(VA_DICE_MEMORY_SYMBOLS, diceIndices.getNumber(), tileSymbols.getNumber());
+ // Insert special symbols tiles
+ for (uint32 i = 0; i < 3; ++i) {
+ int tileSymbolOccurence = _vm->_rnd->getRandomNumber(4 - 1) * 2 + 2;
+ setSubVar(VA_GOOD_DICE_NUMBERS, i, tileSymbolOccurence);
+ while (tileSymbolOccurence--)
+ setSubVar(VA_TILE_SYMBOLS, availableTiles.getNumber(), getSubVar(VA_DICE_MEMORY_SYMBOLS, i));
+ }
+ // Fill the remaining tiles
+ uint32 tileSymbolIndex = 0;
+ while (!availableTiles.empty()) {
+ setSubVar(VA_TILE_SYMBOLS, availableTiles.getNumber(), tileSymbols[tileSymbolIndex]);
+ setSubVar(VA_TILE_SYMBOLS, availableTiles.getNumber(), tileSymbols[tileSymbolIndex]);
+ tileSymbolIndex++;
+ if (tileSymbolIndex >= tileSymbols.size())
+ tileSymbolIndex = 0;
+ }
+ setSubVar(VA_IS_PUZZLE_INIT, 0xC8606803, 1);
+
+ // DEBUG Enable to autosolve all tiles and leave only two matching tiles open
+#if 0
+ for (int i = 0; i < 48; i++)
+ setSubVar(VA_IS_TILE_MATCH, i, 1);
+ int debugIndex = 0;
+ setSubVar(VA_IS_TILE_MATCH, debugIndex, 0);
+ for (int i = 0; i < 48; i++) {
+ if (i != debugIndex && getSubVar(VA_TILE_SYMBOLS, i) == getSubVar(VA_TILE_SYMBOLS, debugIndex)) {
+ setSubVar(VA_IS_TILE_MATCH, i, 0);
+ break;
+ }
+ }
+#endif
+
+ }
+}
+
+void GameModule::initWaterPipesPuzzle() {
+ if (!getSubVar(VA_IS_PUZZLE_INIT, 0x40520234)) {
+ setSubVar(VA_GOOD_WATER_PIPES_LEVEL, 0, 3);
+ setSubVar(VA_GOOD_WATER_PIPES_LEVEL, 1, 1);
+ setSubVar(VA_GOOD_WATER_PIPES_LEVEL, 2, 2);
+ setSubVar(VA_GOOD_WATER_PIPES_LEVEL, 3, 0);
+ setSubVar(VA_GOOD_WATER_PIPES_LEVEL, 4, 4);
+ setSubVar(VA_IS_PUZZLE_INIT, 0x40520234, 1);
+ }
+}
+
+void GameModule::initRadioPuzzle() {
+ if (!getSubVar(VA_IS_PUZZLE_INIT, 0x08C80800)) {
+ int currMusicIndex = _vm->_rnd->getRandomNumber(5 - 1) + 3;
+ setGlobalVar(V_GOOD_RADIO_MUSIC_INDEX, 5 * currMusicIndex);
+ setGlobalVar(V_GOOD_RADIO_MUSIC_NAME, kRadioMusicFileHashes[currMusicIndex]);
+ setGlobalVar(V_RADIO_ROOM_LEFT_DOOR, 1);
+ setGlobalVar(V_RADIO_ROOM_RIGHT_DOOR, 0);
+ setSubVar(VA_IS_PUZZLE_INIT, 0x08C80800, 1);
+ }
+}
+
+void GameModule::initTestTubes1Puzzle() {
+ if (!getSubVar(VA_IS_PUZZLE_INIT, 0x20479010)) {
+ for (uint i = 0; i < 3; i++)
+ setSubVar(VA_GOOD_TEST_TUBES_LEVEL_1, i, _vm->_rnd->getRandomNumber(3 - 1) + 1);
+ setSubVar(VA_IS_PUZZLE_INIT, 0x20479010, 1);
+ }
+}
+
+void GameModule::initTestTubes2Puzzle() {
+ if (!getSubVar(VA_IS_PUZZLE_INIT, 0x66059818)) {
+ for (uint i = 0; i < 3; i++)
+ setSubVar(VA_GOOD_TEST_TUBES_LEVEL_2, i, _vm->_rnd->getRandomNumber(6 - 1) + 1);
+ setSubVar(VA_IS_PUZZLE_INIT, 0x66059818, 1);
+ }
+}
+
+void GameModule::initCannonSymbolsPuzzle() {
+ if (!getSubVar(VA_IS_PUZZLE_INIT, 0x8C9819C2)) {
+ for (int i = 0; i < 3; i++) {
+ setSubVar(VA_GOOD_CANNON_SYMBOLS_1, i, _vm->_rnd->getRandomNumber(12 - 1));
+ setSubVar(VA_GOOD_CANNON_SYMBOLS_2, i, _vm->_rnd->getRandomNumber(12 - 1));
+ }
+ setSubVar(VA_IS_PUZZLE_INIT, 0x8C9819C2, 1);
+ }
+}
+
+void GameModule::initCodeSymbolsPuzzle() {
+ if (!getSubVar(VA_IS_PUZZLE_INIT, 0x0CD09B50)) {
+ for (int i = 0; i < 12; ++i)
+ setSubVar(VA_CODE_SYMBOLS, i, i);
+ for (int i = 0; i < 12; ++i) {
+ uint32 index1 = _vm->_rnd->getRandomNumber(12 - 1);
+ uint32 index2 = _vm->_rnd->getRandomNumber(12 - 1);
+ uint32 temp = getSubVar(VA_CODE_SYMBOLS, index1);
+ setSubVar(VA_CODE_SYMBOLS, index1, getSubVar(VA_CODE_SYMBOLS, index2));
+ setSubVar(VA_CODE_SYMBOLS, index2, temp);
+ }
+ setGlobalVar(V_NOISY_SYMBOL_INDEX, _vm->_rnd->getRandomNumber(11 - 1) + 1);
+ setSubVar(VA_IS_PUZZLE_INIT, 0x0CD09B50, 1);
+ }
+}
+
+void GameModule::initCubeSymbolsPuzzle() {
+ if (!getSubVar(VA_IS_PUZZLE_INIT, 0x60400854)) {
+ NonRepeatingRandomNumbers cubeSymbols(_vm->_rnd, 9);
+ for (uint32 cubePosition = 0; cubePosition < 9; ++cubePosition)
+ setSubVar(VA_CUBE_POSITIONS, cubePosition, (uint32)(cubeSymbols.getNumber() - 1));
+ setSubVar(VA_IS_PUZZLE_INIT, 0x60400854, 1);
+ }
+}
+
+void GameModule::initCrystalColorsPuzzle() {
+ if (!getGlobalVar(V_CRYSTAL_COLORS_INIT)) {
+ TextResource textResource(_vm);
+ const char *textStart, *textEnd;
+ textResource.load(0x46691611);
+ textStart = textResource.getString(0, textEnd);
+ for (uint index = 0; index < 5; index++) {
+ char colorLetter = (byte)textStart[index];
+ byte correctColorNum = 0, misalignedColorNum;
+ switch (colorLetter) {
+ case 'B':
+ correctColorNum = 4;
+ break;
+ case 'G':
+ correctColorNum = 3;
+ break;
+ case 'O':
+ correctColorNum = 1;
+ break;
+ case 'R':
+ correctColorNum = 0;
+ break;
+ case 'V':
+ correctColorNum = 5;
+ break;
+ case 'Y':
+ correctColorNum = 2;
+ break;
+ }
+ do {
+ misalignedColorNum = _vm->_rnd->getRandomNumber(6 - 1);
+ } while (misalignedColorNum == correctColorNum);
+ setSubVar(VA_GOOD_CRYSTAL_COLORS, index, correctColorNum);
+ setSubVar(VA_CURR_CRYSTAL_COLORS, index, misalignedColorNum);
+ }
+ setGlobalVar(V_CRYSTAL_COLORS_INIT, 1);
+ }
+}
+
+uint32 GameModule::getCurrRadioMusicFileHash() {
+ uint musicIndex = getGlobalVar(V_CURR_RADIO_MUSIC_INDEX);
+ return (musicIndex % 5 != 0) ? 0 : kRadioMusicFileHashes[CLIP<uint>(musicIndex / 5, 0, 17)];
+}
+
+
+uint32 GameModule::handleMessage(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = Module::handleMessage(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x0800:
+ _canRequestMainMenu = true;
+ break;
+ case 0x1009:
+ _moduleResult = param.asInteger();
+ _done = true;
+ break;
+ }
+ return messageResult;
+}
+
+void GameModule::startup() {
+#if 1
+ // Logos and intro video // Real game start
+ createModule(1500, 0);
+#else
+ // DEBUG>>>
+ /*
+ setGlobalVar(V_SEEN_MUSIC_BOX, 1);
+ setGlobalVar(V_CREATURE_EXPLODED, 0);
+ setGlobalVar(V_MATCH_STATUS, 0);
+ setGlobalVar(V_PROJECTOR_LOCATION, 2);
+ */
+ //setGlobalVar(V_ENTRANCE_OPEN, 0);
+ //setGlobalVar(V_DOOR_SPIKES_OPEN, 1);
+ //setGlobalVar(V_CREATURE_ANGRY, 1);
+ setGlobalVar(V_RADIO_ENABLED, 1);
+ //setGlobalVar(V_TNT_DUMMY_BUILT, 1);
+ setGlobalVar(V_FLYTRAP_RING_DOOR, 1);
+ setGlobalVar(V_TV_JOKE_TOLD, 1);
+ /*
+ // Give all disks
+ for (int i = 0; i < 20; i++)
+ setSubVar(VA_IS_TAPE_INSERTED, i, 1);
+ */
+ setSubVar(VA_IS_KEY_INSERTED, 0, 1);
+ setSubVar(VA_IS_KEY_INSERTED, 1, 1);
+ setSubVar(VA_IS_KEY_INSERTED, 2, 1);
+ for (uint32 index = 0; index < 9; index++)
+ setSubVar(VA_CUBE_POSITIONS, index, 7 - index);
+ setGlobalVar(V_WALL_BROKEN, 0);
+ setGlobalVar(V_WORLDS_JOINED, 1);
+ setGlobalVar(V_RADIO_MOVE_DISH_VIDEO, 0);
+ // Enable all locations
+ for (int i = 0; i < 6; i++)
+ setSubVar(V_TELEPORTER_DEST_AVAILABLE, i, 1);
+ //setGlobalVar(V_PROJECTOR_LOCATION, 4);
+ setGlobalVar(V_KEYDOOR_UNLOCKED, 1);
+ setGlobalVar(V_LIGHTS_ON, 1);
+ setGlobalVar(V_WATER_RUNNING, 1);
+ setGlobalVar(V_HAS_TEST_TUBE, 1);
+ setSubVar(VA_CURR_WATER_PIPES_LEVEL, 0, 3);
+ setSubVar(VA_CURR_WATER_PIPES_LEVEL, 1, 1);
+ setSubVar(VA_CURR_WATER_PIPES_LEVEL, 2, 2);
+ setSubVar(VA_CURR_WATER_PIPES_LEVEL, 3, 0);
+ setSubVar(VA_CURR_WATER_PIPES_LEVEL, 4, 4);
+ setGlobalVar(V_KLAYMEN_SMALL, 1);
+ setGlobalVar(V_SHRINK_LIGHTS_ON, 0);
+ // <<<DEBUG
+
+#if 1
+ _vm->gameState().which = 0;
+ _vm->gameState().sceneNum = 0;
+ createModule(2400, 0);
+#endif
+
+#endif
+}
+
+void GameModule::requestRestoreGame() {
+ _restoreGameRequested = true;
+}
+
+void GameModule::requestRestartGame(bool requestMainMenu) {
+ _restartGameRequested = true;
+ _mainMenuRequested = requestMainMenu;
+}
+
+void GameModule::redrawPrevChildObject() {
+ if (_prevChildObject) {
+ _prevChildObject->draw();
+ _vm->_screen->update();
+ }
+}
+
+void GameModule::checkRequests() {
+ if (_restartGameRequested) {
+ _restartGameRequested = false;
+ _vm->_gameVars->clear();
+ requestRestoreGame();
+ }
+ if (_restoreGameRequested) {
+ _restoreGameRequested = false;
+ delete _childObject;
+ delete _prevChildObject;
+ _childObject = NULL;
+ _prevChildObject = NULL;
+ _prevModuleNum = 0;
+ createModuleByHash(getGlobalVar(V_MODULE_NAME));
+ }
+ if (_mainMenuRequested)
+ openMainMenu();
+}
+
+void GameModule::createModule(int moduleNum, int which) {
+ debug("GameModule::createModule(%d, %d)", moduleNum, which);
+ _moduleNum = moduleNum;
+ switch (_moduleNum) {
+ case 1000:
+ setGlobalVar(V_MODULE_NAME, 0x03294419);
+ _childObject = new Module1000(_vm, this, which);
+ break;
+ case 1100:
+ setGlobalVar(V_MODULE_NAME, 0x0002C818);
+ _childObject = new Module1100(_vm, this, which);
+ break;
+ case 1200:
+ setGlobalVar(V_MODULE_NAME, 0x00478311);
+ _childObject = new Module1200(_vm, this, which);
+ break;
+ case 1300:
+ setGlobalVar(V_MODULE_NAME, 0x0061C090);
+ _childObject = new Module1300(_vm, this, which);
+ break;
+ case 1400:
+ setGlobalVar(V_MODULE_NAME, 0x00AD0012);
+ _childObject = new Module1400(_vm, this, which);
+ break;
+ case 1500:
+ _canRequestMainMenu = false;
+ setGlobalVar(V_MODULE_NAME, 0x00F10114);
+ _childObject = new Module1500(_vm, this, which);
+ break;
+ case 1600:
+ setGlobalVar(V_MODULE_NAME, 0x01A008D8);
+ _childObject = new Module1600(_vm, this, which);
+ break;
+ case 1700:
+ setGlobalVar(V_MODULE_NAME, 0x04212331);
+ _childObject = new Module1700(_vm, this, which);
+ break;
+ case 1800:
+ setGlobalVar(V_MODULE_NAME, 0x04A14718);
+ _childObject = new Module1800(_vm, this, which);
+ break;
+ case 1900:
+ setGlobalVar(V_MODULE_NAME, 0x04E1C09C);
+ _childObject = new Module1900(_vm, this, which);
+ break;
+ case 2000:
+ setGlobalVar(V_MODULE_NAME, 0x08250000);
+ _childObject = new Module2000(_vm, this, which);
+ break;
+ case 2100:
+ setGlobalVar(V_MODULE_NAME, 0x10A10C14);
+ _childObject = new Module2100(_vm, this, which);
+ break;
+ case 2200:
+ setGlobalVar(V_MODULE_NAME, 0x11391412);
+ _childObject = new Module2200(_vm, this, which);
+ break;
+ case 2300:
+ setGlobalVar(V_MODULE_NAME, 0x1A214010);
+ _childObject = new Module2300(_vm, this, which);
+ break;
+ case 2400:
+ setGlobalVar(V_MODULE_NAME, 0x202D1010);
+ _childObject = new Module2400(_vm, this, which);
+ break;
+ case 2500:
+ setGlobalVar(V_MODULE_NAME, 0x29220120);
+ _childObject = new Module2500(_vm, this, which);
+ break;
+ case 2600:
+ setGlobalVar(V_MODULE_NAME, 0x40271018);
+ _childObject = new Module2600(_vm, this, which);
+ break;
+ case 2700:
+ setGlobalVar(V_MODULE_NAME, 0x42212411);
+ _childObject = new Module2700(_vm, this, which);
+ break;
+ case 2800:
+ setGlobalVar(V_MODULE_NAME, 0x64210814);
+ _childObject = new Module2800(_vm, this, which);
+ break;
+ case 2900:
+ setGlobalVar(V_MODULE_NAME, 0x81100020);
+ if (which >= 0)
+ setGlobalVar(V_TELEPORTER_CURR_LOCATION, which);
+ _childObject = new Module2900(_vm, this, which);
+ break;
+ case 3000:
+ setGlobalVar(V_MODULE_NAME, 0x81293110);
+ _childObject = new Module3000(_vm, this, which);
+ break;
+ case 9999:
+ createDemoScene();
+ break;
+ default:
+ error("GameModule::createModule() Could not create module %d", moduleNum);
+ }
+ SetUpdateHandler(&GameModule::updateModule);
+ _childObject->handleUpdate();
+}
+
+void GameModule::createModuleByHash(uint32 nameHash) {
+ debug("GameModule::createModuleByHash(%08X)", nameHash);
+ switch (nameHash) {
+ case 0x03294419:
+ createModule(1000, -1);
+ break;
+ case 0x0002C818:
+ createModule(1100, -1);
+ break;
+ case 0x00478311:
+ createModule(1200, -1);
+ break;
+ case 0x0061C090:
+ createModule(1300, -1);
+ break;
+ case 0x00AD0012:
+ createModule(1400, -1);
+ break;
+ case 0x00F10114:
+ createModule(1500, -1);
+ break;
+ case 0x01A008D8:
+ createModule(1600, -1);
+ break;
+ case 0x04212331:
+ createModule(1700, -1);
+ break;
+ case 0x04A14718:
+ createModule(1800, -1);
+ break;
+ case 0x04E1C09C:
+ createModule(1900, -1);
+ break;
+ case 0x08250000:
+ createModule(2000, -1);
+ break;
+ case 0x10A10C14:
+ createModule(2100, -1);
+ break;
+ case 0x11391412:
+ createModule(2200, -1);
+ break;
+ case 0x1A214010:
+ createModule(2300, -1);
+ break;
+ case 0x202D1010:
+ createModule(2400, -1);
+ break;
+ case 0x29220120:
+ createModule(2500, -1);
+ break;
+ case 0x40271018:
+ createModule(2600, -1);
+ break;
+ case 0x42212411:
+ createModule(2700, -1);
+ break;
+ case 0x64210814:
+ createModule(2800, -1);
+ break;
+ case 0x81100020:
+ createModule(2900, -1);
+ break;
+ case 0x81293110:
+ createModule(3000, -1);
+ break;
+ default:
+ createModule(1000, 0);
+ break;
+ }
+}
+
+void GameModule::updateModule() {
+ if (!updateChild()) {
+ switch (_moduleNum) {
+ case 1000:
+ createModule(2300, 0);
+ break;
+ case 1200:
+ if (_moduleResult == 1)
+ createModule(2600, 0);
+ else
+ createModule(2300, 2);
+ break;
+ case 1100:
+ if (_moduleResult == 0)
+ createModule(2900, 2);
+ else {
+ setGlobalVar(V_ENTRANCE_OPEN, 1);
+ createModule(1300, 0);
+ }
+ break;
+ case 1300:
+ if (_moduleResult == 1) {
+ // The game was successfully finished
+ requestRestartGame(true);
+ } else
+ createModule(2900, 0);
+ break;
+ case 1400:
+ createModule(1600, 1);
+ break;
+ case 1500:
+ createModule(1000, 0);
+ break;
+ case 1600:
+ if (_moduleResult == 1)
+ createModule(1400, 0);
+ else if (_moduleResult == 2)
+ createModule(1700, 0);
+ else
+ createModule(2100, 0);
+ break;
+ case 1700:
+ if (_moduleResult == 1)
+ createModule(2900, 3);
+ else
+ createModule(1600, 2);
+ break;
+ case 1800:
+ if (_moduleResult == 1) {
+ // Game over, Klaymen jumped into the hole
+ requestRestartGame(true);
+ } else if (_moduleResult == 2)
+ createModule(2700, 0);
+ else if (_moduleResult == 3)
+ createModule(3000, 3);
+ else
+ createModule(2800, 0);
+ break;
+ case 1900:
+ createModule(3000, 1);
+ break;
+ case 2000:
+ createModule(2900, 4);
+ break;
+ case 2100:
+ if (_moduleResult == 1)
+ createModule(2900, 1);
+ else
+ createModule(1600, 0);
+ break;
+ case 2200:
+ createModule(2300, 1);
+ break;
+ case 2300:
+ debug("module 23000 _moduleResult : %d", _moduleResult);
+ if (_moduleResult == 2)
+ createModule(1200, 0);
+ else if (_moduleResult == 0)
+ createModule(1000, 1);
+ else if (_vm->isDemo())
+ createModule(9999, -1);
+ else if (_moduleResult == 1)
+ createModule(2200, 0);
+ else if (_moduleResult == 3)
+ createModule(2400, 0);
+ else if (_moduleResult == 4)
+ createModule(3000, 0);
+ break;
+ case 2400:
+ createModule(2300, 3);
+ break;
+ case 2500:
+ createModule(2600, 1);
+ break;
+ case 2600:
+ if (_moduleResult == 1)
+ createModule(2500, 0);
+ else
+ createModule(1200, 1);
+ break;
+ case 2700:
+ createModule(1800, 2);
+ break;
+ case 2800:
+ if (_moduleResult == 1)
+ createModule(2900, 5);
+ else
+ createModule(1800, 0);
+ break;
+ case 2900:
+ if (_moduleResult != (uint32)-1) {
+ switch (_moduleResult) {
+ case 0:
+ createModule(1300, 5);
+ break;
+ case 1:
+ createModule(2100, 1);
+ break;
+ case 2:
+ createModule(1100, 1);
+ break;
+ case 3:
+ setSubVar(V_TELEPORTER_DEST_AVAILABLE, 2, 1);
+ createModule(1700, 1);
+ break;
+ case 4:
+ createModule(2000, 0);
+ break;
+ case 5:
+ default:
+ createModule(2800, 1);
+ break;
+ }
+ } else {
+ switch (getGlobalVar(V_TELEPORTER_CURR_LOCATION)) {
+ case 0:
+ createModule(1300, 6);
+ break;
+ case 1:
+ createModule(2100, 2);
+ break;
+ case 2:
+ createModule(1100, 2);
+ break;
+ case 3:
+ createModule(1700, 2);
+ break;
+ case 4:
+ createModule(2000, 1);
+ break;
+ case 5:
+ default:
+ createModule(2800, 2);
+ break;
+ }
+ }
+ setGlobalVar(V_TELEPORTER_CURR_LOCATION, 0);
+ break;
+ case 3000:
+ // NOTE _moduleResult 2 never used
+ // NOTE Check if _moduleResult 4 is used
+ if (_moduleResult == 1)
+ createModule(1900, 0);
+ else if (_moduleResult == 3)
+ createModule(1800, 3);
+ else if (_moduleResult == 4)
+ createModule(3000, 0);
+ else
+ createModule(2300, 4);
+ break;
+ case 9999:
+ createModuleByHash(getGlobalVar(V_MODULE_NAME));
+ break;
+ }
+ }
+}
+
+void GameModule::openMainMenu() {
+ if (_childObject) {
+ sendMessage(_childObject, 0x101D, 0);
+ _childObject->draw();
+ } else {
+ // If there's no module, create one so there's something to return to
+ createModule(1000, 0);
+ }
+ _vm->_screen->saveParams();
+ _mainMenuRequested = false;
+ createMenuModule();
+}
+
+void GameModule::createMenuModule() {
+ if (!_prevChildObject) {
+ _prevChildObject = _childObject;
+ _prevModuleNum = _moduleNum;
+ _childObject = new MenuModule(_vm, this, 0);
+ _childObject->handleUpdate();
+ SetUpdateHandler(&GameModule::updateMenuModule);
+ }
+}
+
+void GameModule::updateMenuModule() {
+ if (!updateChild()) {
+ _vm->_screen->restoreParams();
+ _childObject = _prevChildObject;
+ sendMessage(_childObject, 0x101E, 0);
+ _prevChildObject = NULL;
+ _moduleNum = _prevModuleNum;
+ SetUpdateHandler(&GameModule::updateModule);
+ }
+}
+
+NonRepeatingRandomNumbers::NonRepeatingRandomNumbers(Common::RandomSource *rnd, int count)
+ : _rnd(rnd) {
+ for (int i = 0; i < count; i++)
+ push_back(i);
+}
+
+int NonRepeatingRandomNumbers::getNumber() {
+ int number;
+ if (!empty()) {
+ uint index = _rnd->getRandomNumber(size() - 1);
+ number = (*this)[index];
+ remove_at(index);
+ } else
+ number = 0;
+ return number;
+}
+
+void NonRepeatingRandomNumbers::removeNumber(int number) {
+ for (uint i = 0; i < size(); ++i)
+ if ((*this)[i] == number) {
+ remove_at(i);
+ break;
+ }
+}
+
+} // End of namespace Neverhood
diff --git a/engines/neverhood/gamemodule.h b/engines/neverhood/gamemodule.h
new file mode 100644
index 0000000000..8101d38009
--- /dev/null
+++ b/engines/neverhood/gamemodule.h
@@ -0,0 +1,86 @@
+/* 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_GAMEMODULE_H
+#define NEVERHOOD_GAMEMODULE_H
+
+#include "neverhood/neverhood.h"
+#include "neverhood/module.h"
+
+namespace Neverhood {
+
+class GameModule : public Module {
+public:
+ GameModule(NeverhoodEngine *vm);
+ virtual ~GameModule();
+ void startup();
+ void requestRestoreGame();
+ void requestRestartGame(bool requestMainMenu);
+ void redrawPrevChildObject();
+ void checkRequests();
+ void handleMouseMove(int16 x, int16 y);
+ void handleMouseDown(int16 x, int16 y);
+ void handleMouseUp(int16 x, int16 y);
+ void handleSpaceKey();
+ void handleAsciiKey(char key);
+ void handleKeyDown(Common::KeyCode keyCode);
+ void handleEscapeKey();
+ void initKeySlotsPuzzle();
+ void initMemoryPuzzle();
+ void initWaterPipesPuzzle();
+ void initRadioPuzzle();
+ void initTestTubes1Puzzle();
+ void initTestTubes2Puzzle();
+ void initCannonSymbolsPuzzle();
+ void initCodeSymbolsPuzzle();
+ void initCubeSymbolsPuzzle();
+ void initCrystalColorsPuzzle();
+ uint32 getCurrRadioMusicFileHash();
+protected:
+ int _moduleNum;
+ Entity *_prevChildObject;
+ int _prevModuleNum;
+ bool _restoreGameRequested;
+ bool _restartGameRequested;
+ bool _canRequestMainMenu;
+ bool _mainMenuRequested;
+ uint32 handleMessage(int messageNum, const MessageParam &param, Entity *sender);
+ void createModule(int moduleNum, int which);
+ void createModuleByHash(uint32 nameHash);
+ void updateModule();
+ void openMainMenu();
+ void createMenuModule();
+ void updateMenuModule();
+};
+
+class NonRepeatingRandomNumbers : public Common::Array<int> {
+public:
+ NonRepeatingRandomNumbers(Common::RandomSource *rnd, int count);
+ int getNumber();
+ void removeNumber(int number);
+protected:
+ Common::RandomSource *_rnd;
+};
+
+} // End of namespace Neverhood
+
+#endif /* NEVERHOOD_MODULE_H */
diff --git a/engines/neverhood/gamevars.cpp b/engines/neverhood/gamevars.cpp
new file mode 100644
index 0000000000..87f5fe6dd9
--- /dev/null
+++ b/engines/neverhood/gamevars.cpp
@@ -0,0 +1,133 @@
+/* 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/gamevars.h"
+
+namespace Neverhood {
+
+GameVars::GameVars() {
+ addVar(0, 0);
+}
+
+void GameVars::clear() {
+ _vars.clear();
+ addVar(0, 0);
+}
+
+void GameVars::loadState(Common::InSaveFile *in) {
+ uint varCount;
+ _vars.clear();
+ varCount = in->readUint32LE();
+ for (uint i = 0; i < varCount; ++i) {
+ GameVar var;
+ var.nameHash = in->readUint32LE();
+ var.value = in->readUint32LE();
+ var.firstIndex = in->readUint16LE();
+ var.nextIndex = in->readUint16LE();
+ _vars.push_back(var);
+ }
+}
+
+void GameVars::saveState(Common::OutSaveFile *out) {
+ out->writeUint32LE(_vars.size());
+ for (uint i = 0; i < _vars.size(); ++i) {
+ GameVar &var = _vars[i];
+ out->writeUint32LE(var.nameHash);
+ out->writeUint32LE(var.value);
+ out->writeUint16LE(var.firstIndex);
+ out->writeUint16LE(var.nextIndex);
+ }
+}
+
+uint32 GameVars::getGlobalVar(uint32 nameHash) {
+ int16 varIndex = findSubVarIndex(0, nameHash);
+ return varIndex != -1 ? _vars[varIndex].value : 0;
+}
+
+void GameVars::setGlobalVar(uint32 nameHash, uint32 value) {
+ _vars[getSubVarIndex(0, nameHash)].value = value;
+}
+
+uint32 GameVars::getSubVar(uint32 nameHash, uint32 subNameHash) {
+ uint32 value = 0;
+ int16 varIndex = findSubVarIndex(0, nameHash);
+ if (varIndex != -1) {
+ int16 subVarIndex = findSubVarIndex(varIndex, subNameHash);
+ if (subVarIndex != -1)
+ value = _vars[subVarIndex].value;
+ }
+ return value;
+}
+
+void GameVars::setSubVar(uint32 nameHash, uint32 subNameHash, uint32 value) {
+ int16 varIndex = getSubVarIndex(0, nameHash);
+ int16 subVarIndex = getSubVarIndex(varIndex, subNameHash);
+ _vars[subVarIndex].value = value;
+}
+
+int16 GameVars::addVar(uint32 nameHash, uint32 value) {
+ GameVar gameVar;
+ gameVar.nameHash = nameHash;
+ gameVar.value = value;
+ gameVar.firstIndex = -1;
+ gameVar.nextIndex = -1;
+ _vars.push_back(gameVar);
+ return _vars.size() - 1;
+}
+
+int16 GameVars::findSubVarIndex(int16 varIndex, uint32 subNameHash) {
+ for (int16 nextIndex = _vars[varIndex].firstIndex; nextIndex != -1; nextIndex = _vars[nextIndex].nextIndex)
+ if (_vars[nextIndex].nameHash == subNameHash)
+ return nextIndex;
+ return -1;
+}
+
+int16 GameVars::addSubVar(int16 varIndex, uint32 subNameHash, uint32 value) {
+ int16 nextIndex = _vars[varIndex].firstIndex;
+ int16 subVarIndex;
+ if (nextIndex == -1) {
+ subVarIndex = addVar(subNameHash, value);
+ _vars[varIndex].firstIndex = subVarIndex;
+ } else {
+ while (_vars[nextIndex].nextIndex != -1)
+ nextIndex = _vars[nextIndex].nextIndex;
+ subVarIndex = addVar(subNameHash, value);
+ _vars[nextIndex].nextIndex = subVarIndex;
+ }
+ return subVarIndex;
+}
+
+int16 GameVars::getSubVarIndex(int16 varIndex, uint32 subNameHash) {
+ int16 subVarIndex = findSubVarIndex(varIndex, subNameHash);
+ if (subVarIndex == -1)
+ subVarIndex = addSubVar(varIndex, subNameHash, 0);
+ return subVarIndex;
+}
+
+void GameVars::dumpVars() {
+ for (Common::Array<GameVar>::iterator it = _vars.begin(); it != _vars.end(); ++it) {
+ GameVar gameVar = *it;
+ debug("%08X %08X %3d %3d", gameVar.nameHash, gameVar.value, gameVar.firstIndex, gameVar.nextIndex);
+ }
+}
+
+} // End of namespace Neverhood
diff --git a/engines/neverhood/gamevars.h b/engines/neverhood/gamevars.h
new file mode 100644
index 0000000000..5337c13394
--- /dev/null
+++ b/engines/neverhood/gamevars.h
@@ -0,0 +1,193 @@
+/* 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_GAMEVARS_H
+#define NEVERHOOD_GAMEVARS_H
+
+#include "common/array.h"
+#include "common/savefile.h"
+#include "neverhood/neverhood.h"
+
+namespace Neverhood {
+
+enum {
+ // Misc
+ V_MODULE_NAME = 0x91080831, // Currently active module name hash
+ V_CURRENT_SCENE = 0x108A4870, // Current scene in the current module
+ V_CURRENT_SCENE_WHICH = 0x82C80875,
+ V_DEBUG = 0xA4014072, // Original debug-flag, can probably be removed
+ V_SMACKER_CAN_ABORT = 0x06C02850, // Not set anywhere (yet), seems like a debug flag
+ V_KEY3_LOCATION = 0x13382860, // Location of the third key
+ V_TEXT_FLAG1 = 0x8440001F,
+ V_TEXT_INDEX = 0x01830201,
+ V_TEXT_COUNTING_INDEX1 = 0x29408F00,
+ V_TEXT_COUNTING_INDEX2 = 0x8A140C21,
+ V_TALK_COUNTING_INDEX = 0xA0808898,
+ V_FRUIT_COUNTING_INDEX = 0x40040831,
+ V_NOISY_SYMBOL_INDEX = 0x2414C2F2,
+ V_COLUMN_BACK_NAME = 0x4CE79018,
+ V_COLUMN_TEXT_NAME = 0xC8C28808,
+ V_CLICKED_COLUMN_INDEX = 0x48A68852,
+ V_CLICKED_COLUMN_ROW = 0x49C40058,
+ // Klaymen
+ V_KLAYMEN_SMALL = 0x1860C990, // Is Klaymen small?
+ V_KLAYMEN_FRAMEINDEX = 0x18288913,
+ V_KLAYMEN_IS_DELTA_X = 0xC0418A02,
+ V_KLAYMEN_SAVED_X = 0x00D30138,
+ V_CAR_DELTA_X = 0x21E60190,
+ // Flags
+ V_CRYSTAL_COLORS_INIT = 0xDE2EC914,
+ V_TV_JOKE_TOLD = 0x92603A79,
+ V_NOTES_DOOR_UNLOCKED = 0x0045D021,
+ V_WATER_RUNNING = 0x4E0BE910,
+ V_CREATURE_ANGRY = 0x0A310817, // After having played with the music box
+ V_BEEN_SHRINKING_ROOM = 0x1C1B8A9A,
+ V_BEEN_STATUE_ROOM = 0xCB45DE03,
+ V_MOUSE_PUZZLE_SOLVED = 0x70A1189C,
+ V_NOTES_PUZZLE_SOLVED = 0x86615030,
+ V_TILE_PUZZLE_SOLVED = 0x404290D5,
+ V_STAIRS_PUZZLE_SOLVED = 0xA9035F60,
+ V_CODE_SYMBOLS_SOLVED = 0x2C531AF8,
+ V_SPIKES_RETRACTED = 0x18890C91,
+ V_LARGE_DOOR_NUMBER = 0x9A500914, // Number of the currently "large" door
+ V_LIGHTS_ON = 0x4D080E54,
+ V_SHRINK_LIGHTS_ON = 0x190A1D18, // Lights on in the room with the shrinking device
+ V_STAIRS_DOWN_ONCE = 0x2050861A, // Stairs have been down once before
+ V_STAIRS_DOWN = 0x09221A62,
+ V_LADDER_DOWN = 0x0018CA22, // Is the ladder in the statue room down?
+ V_LADDER_DOWN_ACTION = 0x00188211,
+ V_WALL_BROKEN = 0x10938830,
+ V_BOLT_DOOR_OPEN = 0x01BA1A52,
+ V_BOLT_DOOR_UNLOCKED = 0x00040153,
+ V_SEEN_SYMBOLS_NO_LIGHT = 0x81890D14,
+ V_FELL_DOWN_HOLE = 0xE7498218,
+ V_DOOR_PASSED = 0x2090590C, // Auto-closing door was passed
+ V_ENTRANCE_OPEN = 0xD0A14D10, // Is the entrance to Module1300 open (after the robot got his teddy)
+ V_WINDOW_OPEN = 0x03C698DA,
+ V_DOOR_STATUS = 0x52371C95,
+ V_DOOR_BUSTED = 0xD217189D,
+ V_WORLDS_JOINED = 0x98109F12, // Are the worlds joined?
+ V_KEYDOOR_UNLOCKED = 0x80455A41, // Is the keyboard-door unlocked?
+ V_MOUSE_SUCKED_IN = 0x01023818, // Are mouse/cheese in Scene1308?
+ V_BALLOON_POPPED = 0xAC00C0D0, // Has the balloon with the key been popped?
+ V_TNT_DUMMY_BUILT = 0x000CF819, // Are all TNT parts on the dummy?
+ V_TNT_DUMMY_FUSE_LIT = 0x20A0C516,
+ V_RADIO_ENABLED = 0x4DE80AC0,
+ V_SEEN_CREATURE_EXPLODE_VID = 0x2A02C07B,
+ V_CREATURE_EXPLODED = 0x0A18CA33,
+ V_UNUSED = 0x89C669AA, // Seems to be unused, confirmed by checking the exe for this constant value (still left in atm)
+ // Radio
+ V_RADIO_ROOM_LEFT_DOOR = 0x09880D40,
+ V_RADIO_ROOM_RIGHT_DOOR = 0x08180ABC,
+ V_CURR_RADIO_MUSIC_INDEX = 0x08CC0828,
+ V_GOOD_RADIO_MUSIC_INDEX = 0x88880915,
+ V_GOOD_RADIO_MUSIC_NAME = 0x89A82A15,
+ V_RADIO_MOVE_DISH_VIDEO = 0x28D8C940,
+ // Match
+ V_MATCH_STATUS = 0x0112090A,
+ // Venus fly trap
+ V_FLYTRAP_RING_EATEN = 0x2B514304,
+ V_FLYTRAP_RING_DOOR = 0x8306F218,
+ V_FLYTRAP_RING_FENCE = 0x80101B1E,
+ V_FLYTRAP_RING_BRIDGE = 0x13206309,
+ V_FLYTRAP_POSITION_1 = 0x1B144052,
+ V_FLYTRAP_POSITION_2 = 0x86341E88,
+ // Navigation
+ V_NAVIGATION_INDEX = 0x4200189E, // Navigation scene: Current navigation index
+ // Cannon
+ V_CANNON_RAISED = 0x000809C2, // Is the cannon raised?
+ V_CANNON_TURNED = 0x9040018A, // Is the cannon turned?
+ V_ROBOT_HIT = 0x0C0288F4, // Was the robot hit by the cannon?
+ V_ROBOT_TARGET = 0x610210B7, // Is the robot at the cannon target position? (teddy)
+ V_CANNON_SMACKER_NAME = 0xF0402B0A,
+ V_CANNON_TARGET_STATUS = 0x20580A86,
+ // Projector
+ V_PROJECTOR_SLOT = 0x04A10F33, // Projector x slot index
+ V_PROJECTOR_LOCATION = 0x04A105B3, // Projector scene location
+ V_PROJECTOR_ACTIVE = 0x12A10DB3, // Is the projecor projecting?
+ // Teleporter
+ V_TELEPORTER_CURR_LOCATION = 0x0152899A,
+ V_TELEPORTER_WHICH = 0x60826830,
+ V_TELEPORTER_DEST_AVAILABLE = 0x2C145A98,
+ // Inventory
+ V_HAS_NEEDLE = 0x31C63C51, // Has Klaymen the needle?
+ V_HAS_FINAL_KEY = 0xC0780812, // Has Klaymen the key from the diskplayer?
+ V_HAS_TEST_TUBE = 0x45080C38,
+ // Arrays
+ // NOTE "GOOD" means the solution, "CURR" is the current setup of the puzzle variables
+ VA_IS_PUZZLE_INIT = 0x40050052,
+ VA_SMACKER_PLAYED = 0x00800410,
+ VA_CURR_CRYSTAL_COLORS = 0xE11A1929,
+ VA_GOOD_CRYSTAL_COLORS = 0xD4B2089C,
+ VA_GOOD_TEST_TUBES_LEVEL_1 = 0x0C601058,
+ VA_GOOD_TEST_TUBES_LEVEL_2 = 0x40005834,
+ VA_CURR_CANNON_SYMBOLS = 0x00000914,
+ VA_GOOD_CANNON_SYMBOLS_1 = 0x00504B86,
+ VA_GOOD_CANNON_SYMBOLS_2 = 0x0A4C0A9A,
+ VA_CURR_WATER_PIPES_LEVEL = 0x0800547C,
+ VA_GOOD_WATER_PIPES_LEVEL = 0x90405038,
+ VA_CURR_DICE_NUMBERS = 0x61084036,
+ VA_GOOD_DICE_NUMBERS = 0x7500993A,
+ VA_CURR_KEY_SLOT_NUMBERS = 0xA010B810,
+ VA_GOOD_KEY_SLOT_NUMBERS = 0x0C10A000,
+ VA_CUBE_POSITIONS = 0x484498D0,
+ VA_CODE_SYMBOLS = 0x04909A50,
+ VA_TILE_SYMBOLS = 0x0C65F80B,
+ VA_IS_TILE_MATCH = 0xCCE0280F,
+ VA_TNT_POSITIONS = 0x10055D14,
+ VA_DICE_MEMORY_SYMBOLS = 0x13100631,
+ VA_HAS_TAPE = 0x02038314,
+ VA_IS_TAPE_INSERTED = 0x02720344,
+ VA_HAS_KEY = 0x0090EA95,
+ VA_IS_KEY_INSERTED = 0x08D0AB11,
+ VA_LOCKS_DISABLED = 0x14800353,
+ V_END_
+};
+
+struct GameVar {
+ uint32 nameHash;
+ uint32 value;
+ int16 firstIndex, nextIndex;
+};
+
+class GameVars {
+public:
+ GameVars();
+ void clear();
+ void loadState(Common::InSaveFile *in);
+ void saveState(Common::OutSaveFile *out);
+ uint32 getGlobalVar(uint32 nameHash);
+ void setGlobalVar(uint32 nameHash, uint32 value);
+ uint32 getSubVar(uint32 nameHash, uint32 subNameHash);
+ void setSubVar(uint32 nameHash, uint32 subNameHash, uint32 value);
+ void dumpVars();
+protected:
+ Common::Array<GameVar> _vars;
+ int16 addVar(uint32 nameHash, uint32 value);
+ int16 findSubVarIndex(int16 varIndex, uint32 subNameHash);
+ int16 addSubVar(int16 varIndex, uint32 subNameHash, uint32 value);
+ int16 getSubVarIndex(int16 varIndex, uint32 subNameHash);
+};
+
+} // End of namespace Neverhood
+
+#endif /* NEVERHOOD_GAMEVARS_H */
diff --git a/engines/neverhood/graphics.cpp b/engines/neverhood/graphics.cpp
new file mode 100644
index 0000000000..5099c7a00e
--- /dev/null
+++ b/engines/neverhood/graphics.cpp
@@ -0,0 +1,349 @@
+/* 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/graphics.h"
+#include "neverhood/resource.h"
+#include "neverhood/screen.h"
+
+namespace Neverhood {
+
+BaseSurface::BaseSurface(NeverhoodEngine *vm, int priority, int16 width, int16 height)
+ : _vm(vm), _priority(priority), _visible(true), _transparent(true),
+ _clipRects(NULL), _clipRectsCount(0), _version(0) {
+
+ _drawRect.x = 0;
+ _drawRect.y = 0;
+ _drawRect.width = width;
+ _drawRect.height = height;
+ _sysRect.x = 0;
+ _sysRect.y = 0;
+ _sysRect.width = (width + 3) & 0xFFFC; // align by 4 bytes
+ _sysRect.height = height;
+ _clipRect.x1 = 0;
+ _clipRect.y1 = 0;
+ _clipRect.x2 = 640;
+ _clipRect.y2 = 480;
+ _surface = new Graphics::Surface();
+ _surface->create(_sysRect.width, _sysRect.height, Graphics::PixelFormat::createFormatCLUT8());
+}
+
+BaseSurface::~BaseSurface() {
+ delete _surface;
+}
+
+void BaseSurface::draw() {
+ if (_surface && _visible && _drawRect.width > 0 && _drawRect.height > 0) {
+ if (_clipRects && _clipRectsCount) {
+ _vm->_screen->drawSurfaceClipRects(_surface, _drawRect, _clipRects, _clipRectsCount, _transparent, _version);
+ } else if (_sysRect.x == 0 && _sysRect.y == 0) {
+ _vm->_screen->drawSurface2(_surface, _drawRect, _clipRect, _transparent, _version);
+ } else {
+ _vm->_screen->drawUnk(_surface, _drawRect, _sysRect, _clipRect, _transparent, _version);
+ }
+ }
+}
+
+void BaseSurface::clear() {
+ _surface->fillRect(Common::Rect(0, 0, _surface->w, _surface->h), 0);
+ ++_version;
+}
+
+void BaseSurface::drawSpriteResource(SpriteResource &spriteResource) {
+ if (spriteResource.getDimensions().width <= _drawRect.width &&
+ spriteResource.getDimensions().height <= _drawRect.height) {
+ clear();
+ spriteResource.draw(_surface, false, false);
+ ++_version;
+ }
+}
+
+void BaseSurface::drawSpriteResourceEx(SpriteResource &spriteResource, bool flipX, bool flipY, int16 width, int16 height) {
+ if (spriteResource.getDimensions().width <= _sysRect.width &&
+ spriteResource.getDimensions().height <= _sysRect.height) {
+ if (width > 0 && width <= _sysRect.width)
+ _drawRect.width = width;
+ if (height > 0 && height <= _sysRect.height)
+ _drawRect.height = height;
+ if (_surface) {
+ clear();
+ spriteResource.draw(_surface, flipX, flipY);
+ ++_version;
+ }
+ }
+}
+
+void BaseSurface::drawAnimResource(AnimResource &animResource, uint frameIndex, bool flipX, bool flipY, int16 width, int16 height) {
+ if (width > 0 && width <= _sysRect.width)
+ _drawRect.width = width;
+ if (height > 0 && height <= _sysRect.height)
+ _drawRect.height = height;
+ if (_surface) {
+ clear();
+ if (frameIndex < animResource.getFrameCount()) {
+ animResource.draw(frameIndex, _surface, flipX, flipY);
+ ++_version;
+ }
+ }
+}
+
+void BaseSurface::drawMouseCursorResource(MouseCursorResource &mouseCursorResource, int frameNum) {
+ if (frameNum < 3) {
+ mouseCursorResource.draw(frameNum, _surface);
+ ++_version;
+ }
+}
+
+void BaseSurface::copyFrom(Graphics::Surface *sourceSurface, int16 x, int16 y, NDrawRect &sourceRect) {
+ // Copy a rectangle from sourceSurface, no clipping is performed, 0 is the transparent color
+ byte *source = (byte*)sourceSurface->getBasePtr(sourceRect.x, sourceRect.y);
+ byte *dest = (byte*)_surface->getBasePtr(x, y);
+ int height = sourceRect.height;
+ while (height--) {
+ for (int xc = 0; xc < sourceRect.width; xc++)
+ if (source[xc] != 0)
+ dest[xc] = source[xc];
+ source += sourceSurface->pitch;
+ dest += _surface->pitch;
+ }
+ ++_version;
+}
+
+// ShadowSurface
+
+ShadowSurface::ShadowSurface(NeverhoodEngine *vm, int priority, int16 width, int16 height, BaseSurface *shadowSurface)
+ : BaseSurface(vm, priority, width, height), _shadowSurface(shadowSurface) {
+ // Empty
+}
+
+void ShadowSurface::draw() {
+ if (_surface && _visible && _drawRect.width > 0 && _drawRect.height > 0) {
+ _vm->_screen->drawSurface2(_surface, _drawRect, _clipRect, _transparent, _version, _shadowSurface->getSurface());
+ }
+}
+
+// FontSurface
+
+FontSurface::FontSurface(NeverhoodEngine *vm, NPointArray *tracking, uint charsPerRow, uint16 numRows, byte firstChar, uint16 charWidth, uint16 charHeight)
+ : BaseSurface(vm, 0, charWidth * charsPerRow, charHeight * numRows), _charsPerRow(charsPerRow), _numRows(numRows),
+ _firstChar(firstChar), _charWidth(charWidth), _charHeight(charHeight), _tracking(NULL) {
+
+ _tracking = new NPointArray();
+ *_tracking = *tracking;
+
+}
+
+FontSurface::FontSurface(NeverhoodEngine *vm, uint32 fileHash, uint charsPerRow, uint16 numRows, byte firstChar, uint16 charWidth, uint16 charHeight)
+ : BaseSurface(vm, 0, charWidth * charsPerRow, charHeight * numRows), _charsPerRow(charsPerRow), _numRows(numRows),
+ _firstChar(firstChar), _charWidth(charWidth), _charHeight(charHeight), _tracking(NULL) {
+
+ SpriteResource fontSpriteResource(_vm);
+ fontSpriteResource.load(fileHash, true);
+ drawSpriteResourceEx(fontSpriteResource, false, false, 0, 0);
+}
+
+FontSurface::~FontSurface() {
+ delete _tracking;
+}
+
+void FontSurface::drawChar(BaseSurface *destSurface, int16 x, int16 y, byte chr) {
+ NDrawRect sourceRect;
+ chr -= _firstChar;
+ sourceRect.x = (chr % _charsPerRow) * _charWidth;
+ sourceRect.y = (chr / _charsPerRow) * _charHeight;
+ sourceRect.width = _charWidth;
+ sourceRect.height = _charHeight;
+ destSurface->copyFrom(_surface, x, y, sourceRect);
+}
+
+void FontSurface::drawString(BaseSurface *destSurface, int16 x, int16 y, const byte *string, int stringLen) {
+
+ if (stringLen < 0)
+ stringLen = strlen((const char*)string);
+
+ for (; stringLen > 0; --stringLen, ++string) {
+ drawChar(destSurface, x, y, *string);
+ x += _tracking ? (*_tracking)[*string - _firstChar].x : _charWidth;
+ }
+
+}
+
+int16 FontSurface::getStringWidth(const byte *string, int stringLen) {
+ return string ? stringLen * _charWidth : 0;
+}
+
+FontSurface *FontSurface::createFontSurface(NeverhoodEngine *vm, uint32 fileHash) {
+ FontSurface *fontSurface;
+ DataResource fontData(vm);
+ SpriteResource fontSprite(vm);
+ fontData.load(calcHash("asRecFont"));
+ uint16 numRows = fontData.getPoint(calcHash("meNumRows")).x;
+ uint16 firstChar = fontData.getPoint(calcHash("meFirstChar")).x;
+ uint16 charWidth = fontData.getPoint(calcHash("meCharWidth")).x;
+ uint16 charHeight = fontData.getPoint(calcHash("meCharHeight")).x;
+ NPointArray *tracking = fontData.getPointArray(calcHash("meTracking"));
+ fontSprite.load(fileHash, true);
+ fontSurface = new FontSurface(vm, tracking, 16, numRows, firstChar, charWidth, charHeight);
+ fontSurface->drawSpriteResourceEx(fontSprite, false, false, 0, 0);
+ return fontSurface;
+}
+
+// Misc
+
+enum BitmapFlags {
+ BF_RLE = 1,
+ BF_HAS_DIMENSIONS = 2,
+ BF_HAS_POSITION = 4,
+ BF_HAS_PALETTE = 8,
+ BF_HAS_IMAGE = 16
+};
+
+void parseBitmapResource(const byte *sprite, bool *rle, NDimensions *dimensions, NPoint *position, const byte **palette, const byte **pixels) {
+
+ uint16 flags;
+
+ flags = READ_LE_UINT16(sprite);
+ sprite += 2;
+
+ if (rle)
+ *rle = flags & BF_RLE;
+
+ if (flags & BF_HAS_DIMENSIONS) {
+ if (dimensions) {
+ dimensions->width = READ_LE_UINT16(sprite);
+ dimensions->height = READ_LE_UINT16(sprite + 2);
+ }
+ sprite += 4;
+ } else if (dimensions) {
+ dimensions->width = 1;
+ dimensions->height = 1;
+ }
+
+ if (flags & BF_HAS_POSITION) {
+ if (position) {
+ position->x = READ_LE_UINT16(sprite);
+ position->y = READ_LE_UINT16(sprite + 2);
+ }
+ sprite += 4;
+ } else if (position) {
+ position->x = 0;
+ position->y = 0;
+ }
+
+ if (flags & BF_HAS_PALETTE) {
+ if (palette)
+ *palette = sprite;
+ sprite += 1024;
+ } else if (palette)
+ *palette = NULL;
+
+ if (flags & BF_HAS_IMAGE) {
+ if (pixels)
+ *pixels = sprite;
+ } else if (pixels)
+ *pixels = NULL;
+
+}
+
+void unpackSpriteRle(const byte *source, int width, int height, byte *dest, int destPitch, bool flipX, bool flipY, byte oldColor, byte newColor) {
+
+ const bool replaceColors = oldColor != newColor;
+
+ int16 rows, chunks;
+ int16 skip, copy;
+
+ if (flipY) {
+ dest += destPitch * (height - 1);
+ destPitch = -destPitch;
+ }
+
+ rows = READ_LE_UINT16(source);
+ chunks = READ_LE_UINT16(source + 2);
+ source += 4;
+
+ do {
+ if (chunks == 0) {
+ dest += rows * destPitch;
+ } else {
+ while (rows-- > 0) {
+ uint16 rowChunks = chunks;
+ while (rowChunks-- > 0) {
+ skip = READ_LE_UINT16(source);
+ copy = READ_LE_UINT16(source + 2);
+ source += 4;
+ if (!flipX) {
+ memcpy(dest + skip, source, copy);
+ } else {
+ byte *flipDest = dest + width - skip - 1;
+ for (int xc = 0; xc < copy; xc++) {
+ *flipDest-- = source[xc];
+ }
+ }
+ source += copy;
+ }
+ dest += destPitch;
+ if (replaceColors)
+ for (int xc = 0; xc < width; xc++)
+ if (dest[xc] == oldColor)
+ dest[xc] = newColor;
+ }
+ }
+ rows = READ_LE_UINT16(source);
+ chunks = READ_LE_UINT16(source + 2);
+ source += 4;
+ } while (rows > 0);
+
+}
+
+void unpackSpriteNormal(const byte *source, int width, int height, byte *dest, int destPitch, bool flipX, bool flipY) {
+
+ const int sourcePitch = (width + 3) & 0xFFFC;
+
+ if (flipY) {
+ dest += destPitch * (height - 1);
+ destPitch = -destPitch;
+ }
+
+ if (!flipX) {
+ while (height-- > 0) {
+ memcpy(dest, source, width);
+ source += sourcePitch;
+ dest += destPitch;
+ }
+ } else {
+ while (height-- > 0) {
+ dest += width - 1;
+ for (int xc = 0; xc < width; xc++)
+ *dest-- = source[xc];
+ source += sourcePitch;
+ dest += destPitch;
+ }
+ }
+
+}
+
+int calcDistance(int16 x1, int16 y1, int16 x2, int16 y2) {
+ const int16 deltaX = ABS(x1 - x2);
+ const int16 deltaY = ABS(y1 - y2);
+ return sqrt((double)(deltaX * deltaX + deltaY * deltaY));
+}
+
+} // End of namespace Neverhood
diff --git a/engines/neverhood/graphics.h b/engines/neverhood/graphics.h
new file mode 100644
index 0000000000..a0ac1f09d5
--- /dev/null
+++ b/engines/neverhood/graphics.h
@@ -0,0 +1,159 @@
+/* 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_GRAPHICS_H
+#define NEVERHOOD_GRAPHICS_H
+
+#include "common/array.h"
+#include "common/file.h"
+#include "graphics/surface.h"
+#include "neverhood/neverhood.h"
+
+namespace Neverhood {
+
+struct NPoint {
+ int16 x, y;
+};
+
+typedef Common::Array<NPoint> NPointArray;
+
+struct NDimensions {
+ int16 width, height;
+};
+
+struct NRect {
+ int16 x1, y1, x2, y2;
+
+ NRect() : x1(0), y1(0), x2(0), y2(0) {}
+
+ NRect(int16 x01, int16 y01, int16 x02, int16 y02) : x1(x01), y1(y01), x2(x02), y2(y02) {}
+
+ void set(int16 x01, int16 y01, int16 x02, int16 y02) {
+ x1 = x01;
+ y1 = y01;
+ x2 = x02;
+ y2 = y02;
+ }
+
+ bool contains(int16 x, int16 y) const {
+ return x >= x1 && x <= x2 && y >= y1 && y <= y2;
+ }
+
+};
+
+typedef Common::Array<NRect> NRectArray;
+
+// TODO: Use Common::Rect
+struct NDrawRect {
+ int16 x, y, width, height;
+ NDrawRect() : x(0), y(0), width(0), height(0) {}
+ NDrawRect(int16 x0, int16 y0, int16 width0, int16 height0) : x(x0), y(y0), width(width0), height(height0) {}
+ int16 x2() { return x + width; }
+ int16 y2() { return y + height; }
+ void set(int16 x0, int16 y0, int16 width0, int16 height0) {
+ x = x0;
+ y = y0;
+ width = width0;
+ height = height0;
+ }
+};
+
+class AnimResource;
+class SpriteResource;
+class MouseCursorResource;
+
+class BaseSurface {
+public:
+ BaseSurface(NeverhoodEngine *vm, int priority, int16 width, int16 height);
+ virtual ~BaseSurface();
+ virtual void draw();
+ void clear();
+ void drawSpriteResource(SpriteResource &spriteResource);
+ void drawSpriteResourceEx(SpriteResource &spriteResource, bool flipX, bool flipY, int16 width, int16 height);
+ void drawAnimResource(AnimResource &animResource, uint frameIndex, bool flipX, bool flipY, int16 width, int16 height);
+ void drawMouseCursorResource(MouseCursorResource &mouseCursorResource, int frameNum);
+ void copyFrom(Graphics::Surface *sourceSurface, int16 x, int16 y, NDrawRect &sourceRect);
+ int getPriority() const { return _priority; }
+ void setPriority(int priority) { _priority = priority; }
+ NDrawRect& getDrawRect() { return _drawRect; }
+ NDrawRect& getSysRect() { return _sysRect; }
+ NRect& getClipRect() { return _clipRect; }
+ void setClipRect(NRect clipRect) { _clipRect = clipRect; }
+ void setClipRects(NRect *clipRects, uint clipRectsCount) { _clipRects = clipRects; _clipRectsCount = clipRectsCount; }
+ void clearClipRects() { _clipRects = NULL; _clipRectsCount = 0; }
+ bool getVisible() const { return _visible; }
+ void setVisible(bool value) { _visible = value; }
+ void setTransparent(bool value) { _transparent = value; }
+ Graphics::Surface *getSurface() { return _surface; }
+protected:
+ NeverhoodEngine *_vm;
+ int _priority;
+ bool _visible;
+ Graphics::Surface *_surface;
+ NDrawRect _drawRect;
+ NDrawRect _sysRect;
+ NRect _clipRect;
+ NRect *_clipRects;
+ uint _clipRectsCount;
+ bool _transparent;
+ // Version changes each time the pixels are touched in any way
+ byte _version;
+};
+
+class ShadowSurface : public BaseSurface {
+public:
+ ShadowSurface(NeverhoodEngine *vm, int priority, int16 width, int16 height, BaseSurface *shadowSurface);
+ virtual void draw();
+protected:
+ BaseSurface *_shadowSurface;
+};
+
+class FontSurface : public BaseSurface {
+public:
+ FontSurface(NeverhoodEngine *vm, NPointArray *tracking, uint charsPerRow, uint16 numRows, byte firstChar, uint16 charWidth, uint16 charHeight);
+ FontSurface(NeverhoodEngine *vm, uint32 fileHash, uint charsPerRow, uint16 numRows, byte firstChar, uint16 charWidth, uint16 charHeight);
+ virtual ~FontSurface();
+ void drawChar(BaseSurface *destSurface, int16 x, int16 y, byte chr);
+ void drawString(BaseSurface *destSurface, int16 x, int16 y, const byte *string, int stringLen = -1);
+ int16 getStringWidth(const byte *string, int stringLen);
+ uint16 getCharWidth() const { return _charWidth; }
+ uint16 getCharHeight() const { return _charHeight; }
+ static FontSurface *createFontSurface(NeverhoodEngine *vm, uint32 fileHash);
+protected:
+ uint _charsPerRow;
+ uint16 _numRows;
+ byte _firstChar;
+ uint16 _charWidth;
+ uint16 _charHeight;
+ NPointArray *_tracking;
+};
+
+// Misc
+
+void parseBitmapResource(const byte *sprite, bool *rle, NDimensions *dimensions, NPoint *position, const byte **palette, const byte **pixels);
+void unpackSpriteRle(const byte *source, int width, int height, byte *dest, int destPitch, bool flipX, bool flipY, byte oldColor = 0, byte newColor = 0);
+void unpackSpriteNormal(const byte *source, int width, int height, byte *dest, int destPitch, bool flipX, bool flipY);
+int calcDistance(int16 x1, int16 y1, int16 x2, int16 y2);
+
+} // End of namespace Neverhood
+
+#endif /* NEVERHOOD_GRAPHICS_H */
diff --git a/engines/neverhood/klaymen.cpp b/engines/neverhood/klaymen.cpp
new file mode 100644
index 0000000000..06d606e18d
--- /dev/null
+++ b/engines/neverhood/klaymen.cpp
@@ -0,0 +1,6141 @@
+/* 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/klaymen.h"
+#include "neverhood/resourceman.h"
+#include "neverhood/scene.h"
+#include "neverhood/staticdata.h"
+
+namespace Neverhood {
+
+static const KlaymenIdleTableItem klaymenIdleTable1[] = {
+ {1, kIdlePickEar},
+ {1, kIdleSpinHead},
+ {1, kIdleArms},
+ {1, kIdleChest},
+ {1, kIdleHeadOff}
+};
+
+static const KlaymenIdleTableItem klaymenIdleTable2[] = {
+ {1, kIdlePickEar},
+ {1, kIdleSpinHead},
+ {1, kIdleChest},
+ {1, kIdleHeadOff}
+};
+
+static const KlaymenIdleTableItem klaymenIdleTable3[] = {
+ {1, kIdleTeleporterHands},
+ {1, kIdleTeleporterHands2}
+};
+
+static const KlaymenIdleTableItem klaymenIdleTable4[] = {
+ {1, kIdleSpinHead},
+ {1, kIdleChest},
+ {1, kIdleHeadOff},
+};
+
+static const KlaymenIdleTableItem klaymenIdleTable1002[] = {
+ {1, kIdlePickEar},
+ {2, kIdleWonderAbout}
+};
+
+// Klaymen
+
+Klaymen::Klaymen(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y, NRectArray *clipRects)
+ : AnimatedSprite(vm, 1000), _idleCounterMax(0), _idleCounter(0), _isMoveObjectRequested(false), _blinkCounterMax(0),
+ _isWalkingOpenDoorNotified(false), _spitOutCountdown(0), _tapesToInsert(0), _keysToInsert(0), _busyStatus(0), _acceptInput(true),
+ _attachedSprite(NULL), _isWalking(false), _actionStatus(1), _parentScene(parentScene), _isSneaking(false), _isLargeStep(false),
+ _doYHitIncr(false), _isLeverDown(false), _isSittingInTeleporter(false), _actionStatusChanged(false), _ladderStatus(0), _pathPoints(NULL), _soundFlag(false),
+ _idleTableNum(0), _otherSprite(NULL), _moveObjectCountdown(0), _readyToSpit(false), _walkResumeFrameIncr(0) {
+
+ createSurface(1000, 320, 200);
+ _x = x;
+ _y = y;
+ _destX = x;
+ _destY = y;
+ _flags = 2;
+ setKlaymenIdleTable1();
+ stTryStandIdle();
+ SetUpdateHandler(&Klaymen::update);
+}
+
+void Klaymen::xUpdate() {
+ // Empty
+}
+
+uint32 Klaymen::xHandleMessage(int messageNum, const MessageParam &param) {
+ switch (messageNum) {
+ case 0x4001:
+ case 0x4800:
+ startWalkToX(param.asPoint().x, false);
+ break;
+ case 0x4004:
+ GotoState(&Klaymen::stTryStandIdle);
+ break;
+ case 0x4818:
+ startWalkToX(_dataResource.getPoint(param.asInteger()).x, false);
+ break;
+ }
+ return 0;
+}
+
+void Klaymen::update() {
+ AnimatedSprite::update();
+ xUpdate();
+}
+
+void Klaymen::setKlaymenIdleTable(const KlaymenIdleTableItem *table, uint tableCount) {
+ _idleTable = table;
+ _idleTableCount = tableCount;
+ _idleTableTotalWeight = 0;
+ for (uint i = 0; i < tableCount; i++)
+ _idleTableTotalWeight += table[i].weight;
+}
+
+void Klaymen::setKlaymenIdleTable1() {
+ setKlaymenIdleTable(klaymenIdleTable1, ARRAYSIZE(klaymenIdleTable1));
+}
+
+void Klaymen::setKlaymenIdleTable2() {
+ setKlaymenIdleTable(klaymenIdleTable2, ARRAYSIZE(klaymenIdleTable2));
+}
+
+void Klaymen::setKlaymenIdleTable3() {
+ setKlaymenIdleTable(klaymenIdleTable3, ARRAYSIZE(klaymenIdleTable3));
+}
+
+void Klaymen::stIdlePickEar() {
+ _busyStatus = 1;
+ _acceptInput = true;
+ startAnimation(0x5B20C814, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmIdlePickEar);
+ SetSpriteUpdate(NULL);
+ NextState(&Klaymen::stStandAround);
+ FinalizeState(&Klaymen::evIdlePickEarDone);
+}
+
+uint32 Klaymen::hmIdlePickEar(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = hmLowLevelAnimation(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x100D:
+ if (param.asInteger() == 0x04DBC02C) {
+ playSound(0, 0x44528AA1);
+ }
+ break;
+ }
+ return messageResult;
+}
+
+void Klaymen::evIdlePickEarDone() {
+ stopSound(0);
+}
+
+void Klaymen::stIdleSpinHead() {
+ _busyStatus = 1;
+ _acceptInput = true;
+ startAnimation(0xD122C137, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmIdleSpinHead);
+ SetSpriteUpdate(NULL);
+ NextState(&Klaymen::stStandAround);
+}
+
+uint32 Klaymen::hmIdleSpinHead(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = hmLowLevelAnimation(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x100D:
+ if (param.asInteger() == 0x808A0008) {
+ playSound(0, 0xD948A340);
+ }
+ break;
+ }
+ return messageResult;
+}
+
+void Klaymen::stIdleArms() {
+ _busyStatus = 1;
+ _acceptInput = true;
+ startAnimation(0x543CD054, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmIdleArms);
+ SetSpriteUpdate(NULL);
+ NextState(&Klaymen::stStandAround);
+ FinalizeState(&Klaymen::evIdleArmsDone);
+}
+
+void Klaymen::evIdleArmsDone() {
+ stopSound(0);
+}
+
+uint32 Klaymen::hmIdleArms(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = hmLowLevelAnimation(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x100D:
+ if (param.asInteger() == 0x5A0F0104) {
+ playSound(0, 0x7970A100);
+ } else if (param.asInteger() == 0x9A9A0109) {
+ playSound(0, 0xD170CF04);
+ } else if (param.asInteger() == 0x989A2169) {
+ playSound(0, 0xD073CF14);
+ }
+ break;
+ }
+ return messageResult;
+}
+
+void Klaymen::stIdleChest() {
+ _busyStatus = 1;
+ _acceptInput = true;
+ startAnimation(0x40A0C034, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmIdleChest);
+ SetSpriteUpdate(NULL);
+ NextState(&Klaymen::stStandAround);
+}
+
+uint32 Klaymen::hmIdleChest(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = hmLowLevelAnimation(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x100D:
+ if (param.asInteger() == 0x0D2A0288) {
+ playSound(0, 0xD192A368);
+ }
+ break;
+ }
+ return messageResult;
+}
+
+void Klaymen::stIdleHeadOff() {
+ _busyStatus = 1;
+ _acceptInput = true;
+ startAnimation(0x5120E137, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmIdleHeadOff);
+ SetSpriteUpdate(NULL);
+ NextState(&Klaymen::stStandAround);
+}
+
+uint32 Klaymen::hmIdleHeadOff(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = hmLowLevelAnimation(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x100D:
+ if (param.asInteger() == 0xC006000C) {
+ playSound(0, 0x9D406340);
+ } else if (param.asInteger() == 0x2E4A2940) {
+ playSound(0, 0x53A4A1D4);
+ } else if (param.asInteger() == 0xAA0A0860) {
+ playSound(0, 0x5BE0A3C6);
+ } else if (param.asInteger() == 0xC0180260) {
+ playSound(0, 0x5D418366);
+ }
+ break;
+ }
+ return messageResult;
+}
+
+void Klaymen::stIdleWonderAbout() {
+ _busyStatus = 1;
+ _acceptInput = true;
+ startAnimation(0xD820A114, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmLowLevelAnimation);
+ SetSpriteUpdate(NULL);
+ NextState(&Klaymen::stStandAround);
+}
+
+void Klaymen::stSitIdleTeleporter() {
+ _busyStatus = 0;
+ _acceptInput = true;
+ startAnimation(0x582EC138, 0, -1);
+ SetUpdateHandler(&Klaymen::upSitIdleTeleporter);
+ SetMessageHandler(&Klaymen::hmLowLevel);
+ SetSpriteUpdate(NULL);
+ _idleCounter = 0;
+ _blinkCounter = 0;
+ _idleCounterMax = 8;
+ _blinkCounterMax = _vm->_rnd->getRandomNumber(64 - 1) + 24;
+}
+
+void Klaymen::upSitIdleTeleporter() {
+ update();
+ if (++_idleCounter >= _idleCounterMax) {
+ _idleCounter = 0;
+ if (_idleTable) {
+ int idleWeight = _vm->_rnd->getRandomNumber(_idleTableTotalWeight - 1);
+ for (uint i = 0; i < _idleTableCount; i++) {
+ if (idleWeight < _idleTable[i].weight) {
+ enterIdleAnimation(_idleTable[i].idleAnimation);
+ _idleCounterMax = _vm->_rnd->getRandomNumber(128 - 1) + 24;
+ break;
+ }
+ idleWeight -= _idleTable[i].weight;
+ }
+ }
+ } else if (++_blinkCounter >= _blinkCounterMax) {
+ _blinkCounter = 0;
+ _blinkCounterMax = _vm->_rnd->getRandomNumber(64 - 1) + 24;
+ stSitIdleTeleporterBlink();
+ }
+}
+
+void Klaymen::stSitIdleTeleporterBlink() {
+ _busyStatus = 0;
+ _acceptInput = true;
+ startAnimation(0x5C24C018, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmLowLevelAnimation);
+ SetSpriteUpdate(NULL);
+ NextState(&Klaymen::stSitIdleTeleporterBlinkSecond);
+}
+
+void Klaymen::stSitIdleTeleporterBlinkSecond() {
+ _busyStatus = 0;
+ _acceptInput = true;
+ startAnimation(0x5C24C018, 0, -1);
+ SetUpdateHandler(&Klaymen::upSitIdleTeleporter);
+ SetMessageHandler(&Klaymen::hmLowLevel);
+ SetSpriteUpdate(NULL);
+}
+
+void Klaymen::stPickUpNeedle() {
+ setDoDeltaX(_attachedSprite->getX() < _x ? 1 : 0);
+ if (!stStartAction(AnimationCallback(&Klaymen::stPickUpNeedle))) {
+ _busyStatus = 1;
+ _acceptInput = false;
+ startAnimation(0x1449C169, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmPickUpObject);
+ SetSpriteUpdate(NULL);
+ }
+}
+
+void Klaymen::stPickUpTube() {
+ setDoDeltaX(_attachedSprite->getX() < _x ? 1 : 0);
+ if (!stStartAction(AnimationCallback(&Klaymen::stPickUpTube))) {
+ _busyStatus = 1;
+ _acceptInput = false;
+ startAnimation(0x0018C032, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmPickUpTube);
+ SetSpriteUpdate(NULL);
+ }
+}
+
+uint32 Klaymen::hmPickUpTube(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = hmLowLevelAnimation(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x100D:
+ if (param.asInteger() == 0xC1380080) {
+ sendMessage(_attachedSprite, 0x4806, 0);
+ playSound(0, 0xC8004340);
+ } else if (param.asInteger() == 0x02B20220) {
+ playSound(0, 0xC5408620);
+ } else if (param.asInteger() == 0x03020231) {
+ playSound(0, 0xD4C08010);
+ } else if (param.asInteger() == 0x67221A03) {
+ playSound(0, 0x44051000);
+ } else if (param.asInteger() == 0x925A0C1E) {
+ playSound(0, 0x40E5884D);
+ }
+ break;
+ }
+ return messageResult;
+}
+
+void Klaymen::stTurnToUseInTeleporter() {
+ _busyStatus = 0;
+ _acceptInput = false;
+ startAnimation(0xD229823D, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmLowLevelAnimation);
+ SetSpriteUpdate(NULL);
+}
+
+void Klaymen::stReturnFromUseInTeleporter() {
+ _busyStatus = 0;
+ _acceptInput = false;
+ startAnimation(0x9A2801E0, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmLowLevelAnimation);
+ SetSpriteUpdate(NULL);
+}
+
+void Klaymen::stStepOver() {
+ if (!stStartAction(AnimationCallback(&Klaymen::stStepOver))) {
+ _busyStatus = 2;
+ _acceptInput = false;
+ startAnimation(0x004AA310, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmStartWalking);
+ SetSpriteUpdate(&Klaymen::suUpdateDestX);
+ }
+}
+
+void Klaymen::stSitInTeleporter() {
+ if (!stStartAction(AnimationCallback(&Klaymen::stSitInTeleporter))) {
+ _busyStatus = 0;
+ _acceptInput = false;
+ startAnimation(0x392A0330, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmSitInTeleporter);
+ SetSpriteUpdate(&Klaymen::suUpdateDestX);
+ }
+}
+
+uint32 Klaymen::hmSitInTeleporter(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = hmLowLevelAnimation(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x100D:
+ if (param.asInteger() == 0x001A2832) {
+ playSound(0, 0xC0E4884C);
+ }
+ break;
+ }
+ return messageResult;
+}
+
+void Klaymen::stGetUpFromTeleporter() {
+ _busyStatus = 0;
+ _acceptInput = false;
+ startAnimation(0x913AB120, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmLowLevelAnimation);
+ SetSpriteUpdate(&Klaymen::suUpdateDestX);
+}
+
+/////////////////////////////////////////////////////////////////
+
+void Klaymen::stopWalking() {
+ _destX = _x;
+ if (!_isWalking && !_isSneaking && !_isLargeStep) {
+ gotoState(NULL);
+ gotoNextStateExt();
+ }
+}
+
+void Klaymen::startIdleAnimation(uint32 fileHash, AnimationCb callback) {
+ debug("startIdleAnimation(%08X)", fileHash);
+ NextState(callback);
+ SetUpdateHandler(&Klaymen::upIdleAnimation);
+}
+
+void Klaymen::upIdleAnimation() {
+ gotoNextStateExt();
+ update();
+}
+
+bool Klaymen::stStartActionFromIdle(AnimationCb callback) {
+ if (_busyStatus == 2) {
+ _busyStatus = 1;
+ _acceptInput = false;
+ startAnimation(0x9A7020B8, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmStartAction);
+ SetSpriteUpdate(NULL);
+ NextState(callback);
+ return true;
+ }
+ return false;
+}
+
+void Klaymen::gotoNextStateExt() {
+ if (_finalizeStateCb) {
+ AnimationCb cb = _finalizeStateCb;
+ _finalizeStateCb = NULL;
+ (this->*cb)();
+ }
+ if (_nextStateCb) {
+ AnimationCb cb = _nextStateCb;
+ _nextStateCb = NULL;
+ (this->*cb)();
+ } else {
+ // Inform the scene that the current Klaymen animation sequence has finished
+ sendMessage(_parentScene, 0x1006, 0);
+ }
+}
+
+void Klaymen::beginAction() {
+ _actionStatusChanged = false;
+ _actionStatus = 1;
+}
+
+void Klaymen::endAction() {
+ if (_actionStatusChanged)
+ _actionStatus = 0;
+}
+
+void Klaymen::stTryStandIdle() {
+ if (!stStartActionFromIdle(AnimationCallback(&Klaymen::stTryStandIdle))) {
+ _busyStatus = 1;
+ _acceptInput = true;
+ startAnimation(0x5420E254, 0, -1);
+ SetUpdateHandler(&Klaymen::upStandIdle);
+ SetMessageHandler(&Klaymen::hmLowLevel);
+ SetSpriteUpdate(NULL);
+ _idleCounter = 0;
+ _blinkCounter = 0;
+ _blinkCounterMax = _vm->_rnd->getRandomNumber(64) + 24;
+ }
+}
+
+void Klaymen::upStandIdle() {
+ update();
+ if (++_idleCounter >= 720) {
+ _idleCounter = 0;
+ if (_idleTable) {
+ int idleWeight = _vm->_rnd->getRandomNumber(_idleTableTotalWeight - 1);
+ for (uint i = 0; i < _idleTableCount; i++) {
+ if (idleWeight < _idleTable[i].weight) {
+ enterIdleAnimation(_idleTable[i].idleAnimation);
+ break;
+ }
+ idleWeight -= _idleTable[i].weight;
+ }
+ }
+ } else if (++_blinkCounter >= _blinkCounterMax) {
+ _blinkCounter = 0;
+ _blinkCounterMax = _vm->_rnd->getRandomNumber(64 - 1) + 24;
+ stIdleBlink();
+ }
+}
+
+uint32 Klaymen::hmLowLevel(int messageNum, const MessageParam &param, Entity *sender) {
+ Sprite::handleMessage(messageNum, param, sender);
+ uint32 messageResult = xHandleMessage(messageNum, param);
+ switch (messageNum) {
+ case 0x1008:
+ messageResult = _acceptInput;
+ break;
+ case 0x1014:
+ _attachedSprite = (Sprite*)(param.asEntity());
+ break;
+ case 0x1019:
+ gotoNextStateExt();
+ break;
+ case 0x101C:
+ beginAction();
+ break;
+ case 0x1021:
+ endAction();
+ break;
+ case 0x481C:
+ _actionStatus = param.asInteger();
+ _actionStatusChanged = true;
+ messageResult = 1;
+ break;
+ case 0x482C:
+ if (param.asInteger() != 0) {
+ _pathPoints = _dataResource.getPointArray(param.asInteger());
+ } else {
+ _pathPoints = NULL;
+ }
+ break;
+ }
+ return messageResult;
+}
+
+void Klaymen::stIdleBlink() {
+ _busyStatus = 1;
+ _acceptInput = true;
+ startAnimation(0x5900C41E, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmLowLevelAnimation);
+ SetSpriteUpdate(NULL);
+ NextState(&Klaymen::stStandAround);
+}
+
+uint32 Klaymen::hmLowLevelAnimation(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = hmLowLevel(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x3002:
+ gotoNextStateExt();
+ break;
+ }
+ return messageResult;
+}
+
+void Klaymen::stStandAround() {
+ _busyStatus = 1;
+ _acceptInput = true;
+ startAnimation(0x5420E254, 0, -1);
+ SetUpdateHandler(&Klaymen::upStandIdle);
+ SetMessageHandler(&Klaymen::hmLowLevel);
+ SetSpriteUpdate(NULL);
+}
+
+uint32 Klaymen::hmStartAction(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = hmLowLevelAnimation(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x100D:
+ if (param.asInteger() == 0x271AA210) {
+ playSound(0, 0x4924AAC4);
+ } else if (param.asInteger() == 0x2B22AA81) {
+ playSound(0, 0x0A2AA8E0);
+ }
+ break;
+ }
+ return messageResult;
+}
+
+
+void Klaymen::startWalkToX(int16 x, bool walkExt) {
+ int16 xdiff = ABS(x - _x);
+ if (x == _x) {
+ _destX = x;
+ if (!_isWalking && !_isSneaking && !_isLargeStep) {
+ gotoState(NULL);
+ gotoNextStateExt();
+ }
+ } else if (xdiff <= 36 && !_isWalking && !_isSneaking && !_isLargeStep) {
+ _destX = x;
+ gotoState(NULL);
+ gotoNextStateExt();
+ } else if (xdiff <= 42 && _actionStatus != 3) {
+ if (_isSneaking && ((!_doDeltaX && x - _x > 0) || (_doDeltaX && x - _x < 0)) && ABS(_destX - _x) > xdiff) {
+ _destX = x;
+ } else {
+ _destX = x;
+ GotoState(&Klaymen::stSneak);
+ }
+ } else if (_isWalking && ((!_doDeltaX && x - _x > 0) || (_doDeltaX && x - _x < 0))) {
+ _destX = x;
+ } else if (walkExt) {
+ _destX = x;
+ GotoState(&Klaymen::stStartWalkingExt);
+ } else {
+ _destX = x;
+ GotoState(&Klaymen::stStartWalking);
+ }
+}
+
+void Klaymen::stWakeUp() {
+ _busyStatus = 1;
+ _acceptInput = false;
+ startAnimation(0x527AC970, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmLowLevelAnimation);
+ SetSpriteUpdate(NULL);
+}
+
+void Klaymen::stSleeping() {
+ _busyStatus = 0;
+ _acceptInput = true;
+ startAnimation(0x5A38C110, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmSleeping);
+ SetSpriteUpdate(NULL);
+}
+
+uint32 Klaymen::hmSleeping(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = hmLowLevel(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x100D:
+ if (param.asInteger() == 0x03060012) {
+ playSound(0, 0xC0238244);
+ }
+ break;
+ }
+ return messageResult;
+}
+
+bool Klaymen::stStartAction(AnimationCb callback3) {
+ if (_busyStatus == 1) {
+ _busyStatus = 2;
+ _acceptInput = false;
+ startAnimation(0x5C7080D4, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmStartAction);
+ SetSpriteUpdate(&Klaymen::suAction);
+ NextState(callback3);
+ return true;
+ } else {
+ _x = _destX;
+ return false;
+ }
+}
+
+void Klaymen::suAction() {
+
+ int16 xdiff = _destX - _x;
+
+ if (_doDeltaX) {
+ _x -= _deltaX;
+ } else {
+ _x += _deltaX;
+ }
+ _deltaX = 0;
+
+ if (_doDeltaY) {
+ _y -= _deltaY;
+ } else {
+ _y += _deltaY;
+ }
+ _deltaY = 0;
+
+ if (_frameChanged) {
+ if (xdiff > 6)
+ _x += 6;
+ else if (xdiff < -6)
+ _x -= 6;
+ else
+ _x = _destX;
+ }
+
+ updateBounds();
+
+}
+
+void Klaymen::suSneaking() {
+
+ int16 xdiff = _destX - _x;
+
+ if (_currFrameIndex == 9) {
+ if (xdiff > 26)
+ _deltaX += xdiff - 26;
+ else if (xdiff < -26)
+ _deltaX -= xdiff + 26;
+ }
+
+ if (xdiff > _deltaX)
+ xdiff = _deltaX;
+ else if (xdiff < -_deltaX)
+ xdiff = -_deltaX;
+ _deltaX = 0;
+
+ if (_destX != _x) {
+ HitRect *hitRectPrev = _parentScene->findHitRectAtPos(_x, _y);
+ _x += xdiff;
+ if (_pathPoints) {
+ walkAlongPathPoints();
+ } else {
+ HitRect *hitRectNext = _parentScene->findHitRectAtPos(_x, _y);
+ if (hitRectNext->type == 0x5002) {
+ _y = MAX<int16>(hitRectNext->rect.y1, hitRectNext->rect.y2 - (hitRectNext->rect.x2 - _x) / 2);
+ } else if (hitRectNext->type == 0x5003) {
+ _y = MAX<int16>(hitRectNext->rect.y1, hitRectNext->rect.y2 - (_x - hitRectNext->rect.x1) / 2);
+ } else if (hitRectPrev->type == 0x5002) {
+ if (xdiff > 0) {
+ _y = hitRectPrev->rect.y2;
+ } else {
+ _y = hitRectPrev->rect.y1;
+ }
+ } else if (hitRectPrev->type == 0x5003) {
+ if (xdiff < 0) {
+ _y = hitRectPrev->rect.y2;
+ } else {
+ _y = hitRectPrev->rect.y1;
+ }
+ }
+ }
+ updateBounds();
+ }
+
+}
+
+void Klaymen::stSneak() {
+ _busyStatus = 1;
+ _isSneaking = true;
+ _acceptInput = true;
+ setDoDeltaX(_destX < _x ? 1 : 0);
+ startAnimation(0x5C48C506, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmSneaking);
+ SetSpriteUpdate(&Klaymen::suSneaking);
+ FinalizeState(&Klaymen::evSneakingDone);
+}
+
+void Klaymen::evSneakingDone() {
+ _isSneaking = false;
+}
+
+uint32 Klaymen::hmSneaking(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = hmLowLevel(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x100D:
+ if (param.asInteger() == 0x32180101) {
+ playSound(0, 0x4924AAC4);
+ } else if (param.asInteger() == 0x0A2A9098) {
+ playSound(0, 0x0A2AA8E0);
+ } else if (param.asInteger() == 0x32188010) {
+ playSound(0, _soundFlag ? 0x48498E46 : 0x405002D8);
+ } else if (param.asInteger() == 0x02A2909C) {
+ playSound(0, _soundFlag ? 0x50399F64 : 0x0460E2FA);
+ }
+ break;
+ case 0x3002:
+ _x = _destX;
+ gotoNextStateExt();
+ break;
+ }
+ return messageResult;
+}
+
+void Klaymen::stStartWalking() {
+ if (!stStartActionFromIdle(AnimationCallback(&Klaymen::stStartWalking))) {
+ _busyStatus = 0;
+ _isWalking = true;
+ _acceptInput = true;
+ setDoDeltaX(_destX < _x ? 1 : 0);
+ startAnimation(0x242C0198, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmStartWalking);
+ SetSpriteUpdate(&Klaymen::suWalkingTestExit);
+ NextState(&Klaymen::stWalkingFirst);
+ FinalizeState(&Klaymen::evStartWalkingDone);
+ }
+}
+
+void Klaymen::evStartWalkingDone() {
+ _isWalking = false;
+}
+
+uint32 Klaymen::hmStartWalking(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = hmLowLevelAnimation(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x100D:
+ if (param.asInteger() == 0x32180101) {
+ playSound(0, _soundFlag ? 0x48498E46 : 0x405002D8);
+ } else if (param.asInteger() == 0x0A2A9098) {
+ playSound(0, _soundFlag ? 0x50399F64 : 0x0460E2FA);
+ }
+ break;
+ }
+ return messageResult;
+}
+
+void Klaymen::stWalkingFirst() {
+ _busyStatus = 0;
+ _isWalking = true;
+ _acceptInput = true;
+ startAnimation(0x1A249001, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmWalking);
+ SetSpriteUpdate(&Klaymen::suWalkingFirst);
+ NextState(&Klaymen::stUpdateWalkingFirst);
+ FinalizeState(&Klaymen::evStartWalkingDone);
+}
+
+void Klaymen::suWalkingFirst() {
+ SetSpriteUpdate(&Klaymen::suWalkingTestExit);
+ _deltaX = 0;
+}
+
+uint32 Klaymen::hmWalking(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = hmLowLevel(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x100D:
+ if (param.asInteger() == 0x32180101) {
+ playSound(0, _soundFlag ? 0x48498E46 : 0x405002D8);
+ } else if (param.asInteger() == 0x0A2A9098) {
+ playSound(0, _soundFlag ? 0x50399F64 : 0x0460E2FA);
+ }
+ break;
+ }
+ return messageResult;
+}
+
+void Klaymen::stUpdateWalkingFirst() {
+ if (_actionStatus == 2) {
+ gotoNextStateExt();
+ } else if (_actionStatus == 3) {
+ stWalkingOpenDoor();
+ } else {
+ _isSneaking = true;
+ _acceptInput = true;
+ if (ABS(_destX - _x) <= 42 && _currFrameIndex >= 5 && _currFrameIndex <= 11) {
+ if (_actionStatus == 0) {
+ _busyStatus = 1;
+ startAnimation(0xF234EE31, 0, -1);
+ } else {
+ _busyStatus = 2;
+ startAnimation(0xF135CC21, 0, -1);
+ }
+ } else if (ABS(_destX - _x) <= 10 && (_currFrameIndex >= 12 || _currFrameIndex <= 4)) {
+ if (_actionStatus == 0) {
+ _busyStatus = 1;
+ startAnimation(0x8604A152, 0, -1);
+ } else {
+ _busyStatus = 2;
+ startAnimation(0xA246A132, 0, -1);
+ }
+ }
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmSneaking);
+ SetSpriteUpdate(&Klaymen::suSneaking);
+ FinalizeState(&Klaymen::evSneakingDone);
+ }
+}
+
+void Klaymen::suWalkingTestExit() {
+ int16 xdiff = ABS(_destX - _x);
+ int16 xdelta = _destX - _x;
+
+ if (xdelta > _deltaX)
+ xdelta = _deltaX;
+ else if (xdelta < -_deltaX)
+ xdelta = -_deltaX;
+
+ _deltaX = 0;
+
+ if (xdiff == 0 ||
+ (_actionStatus != 2 && _actionStatus != 3 && xdiff <= 42 && _currFrameIndex >= 5 && _currFrameIndex <= 11) ||
+ (_actionStatus != 2 && _actionStatus != 3 && xdiff <= 10 && (_currFrameIndex >= 12 || _currFrameIndex <= 4)) ||
+ (_actionStatus == 3 && xdiff < 30) ||
+ (_actionStatus == 3 && xdiff < 150 && _currFrameIndex >= 6)) {
+ sendMessage(this, 0x1019, 0);
+ } else {
+ HitRect *hitRectPrev = _parentScene->findHitRectAtPos(_x, _y);
+ _x += xdelta;
+ if (_pathPoints) {
+ walkAlongPathPoints();
+ } else {
+ HitRect *hitRectNext = _parentScene->findHitRectAtPos(_x, _y);
+ if (hitRectNext->type == 0x5002) {
+ _y = MAX<int16>(hitRectNext->rect.y1, hitRectNext->rect.y2 - (hitRectNext->rect.x2 - _x) / 2);
+ } else if (hitRectNext->type == 0x5003) {
+ _y = MAX<int16>(hitRectNext->rect.y1, hitRectNext->rect.y2 - (_x - hitRectNext->rect.x1) / 2);
+ } else if (hitRectPrev->type == 0x5002) {
+ _y = xdelta > 0 ? hitRectPrev->rect.y2 : hitRectPrev->rect.y1;
+ } else if (hitRectPrev->type == 0x5003) {
+ _y = xdelta < 0 ? hitRectPrev->rect.y2 : hitRectPrev->rect.y1;
+ } else if (_doYHitIncr && xdelta != 0) {
+ if (hitRectNext->type == 0x5000) {
+ _y++;
+ } else if (hitRectNext->type == 0x5001 && _y > hitRectNext->rect.y1) {
+ _y--;
+ }
+ }
+ }
+ updateBounds();
+ }
+
+}
+
+uint32 Klaymen::hmLever(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = hmLowLevelAnimation(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x100D:
+ if (param.asInteger() == 0x4AB28209) {
+ sendMessage(_attachedSprite, 0x482A, 0);
+ } else if (param.asInteger() == 0x88001184) {
+ sendMessage(_attachedSprite, 0x482B, 0);
+ }
+ break;
+ }
+ return messageResult;
+}
+
+void Klaymen::stPickUpGeneric() {
+ setDoDeltaX(_attachedSprite->getX() < _x ? 1 : 0);
+ if (!stStartAction(AnimationCallback(&Klaymen::stPickUpGeneric))) {
+ _busyStatus = 1;
+ _acceptInput = false;
+ startAnimation(0x1C28C178, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmPickUpObject);
+ SetSpriteUpdate(NULL);
+ }
+}
+
+uint32 Klaymen::hmPickUpObject(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = hmLowLevelAnimation(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x100D:
+ if (param.asInteger() == 0xC1380080) {
+ sendMessage(_attachedSprite, 0x4806, 0);
+ playSound(0, 0x40208200);
+ } else if (param.asInteger() == 0x02B20220) {
+ playSound(0, 0xC5408620);
+ } else if (param.asInteger() == 0x03020231) {
+ playSound(0, 0xD4C08010);
+ } else if (param.asInteger() == 0x67221A03) {
+ playSound(0, 0x44051000);
+ } else if (param.asInteger() == 0x2EAE0303) {
+ playSound(0, 0x03630300);
+ } else if (param.asInteger() == 0x61CE4467) {
+ playSound(0, 0x03630300);
+ }
+ break;
+ }
+ return messageResult;
+
+}
+
+void Klaymen::stPressButton() {
+ if (!stStartAction(AnimationCallback(&Klaymen::stPressButton))) {
+ _busyStatus = 2;
+ _acceptInput = true;
+ startAnimation(0x1C02B03D, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmPressButton);
+ SetSpriteUpdate(NULL);
+ }
+}
+
+uint32 Klaymen::hmPressButton(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = hmLowLevelAnimation(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x100D:
+ if (param.asInteger() == 0x0D01B294) {
+ sendMessage(_attachedSprite, 0x480B, 0);
+ } else if (param.asInteger() == 0x32180101) {
+ playSound(0, 0x4924AAC4);
+ } else if (param.asInteger() == 0x0A2A9098) {
+ playSound(0, 0x0A2AA8E0);
+ }
+ break;
+ }
+ return messageResult;
+}
+
+void Klaymen::stPressFloorButton() {
+ if (!stStartAction(AnimationCallback(&Klaymen::stPressFloorButton))) {
+ _busyStatus = 2;
+ _acceptInput = true;
+ startAnimation(0x1C16B033, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmPressButton);
+ SetSpriteUpdate(NULL);
+ }
+}
+
+void Klaymen::stPressButtonSide() {
+ if (!stStartActionFromIdle(AnimationCallback(&Klaymen::stPressButtonSide))) {
+ _busyStatus = 1;
+ _acceptInput = true;
+ startAnimation(0x1CD89029, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmPressButton);
+ SetSpriteUpdate(&Klaymen::suAction);
+ }
+}
+
+void Klaymen::startSpecialWalkRight(int16 x) {
+ if (_x == x) {
+ _destX = x;
+ gotoState(NULL);
+ gotoNextStateExt();
+ } else if (_x < x) {
+ startWalkToX(x, false);
+ } else if (_x - x <= 105) {
+ startWalkToXExt(x);
+ } else {
+ startWalkToX(x, false);
+ }
+}
+
+void Klaymen::startSpecialWalkLeft(int16 x) {
+ if (x == _x) {
+ _destX = x;
+ gotoState(NULL);
+ gotoNextStateExt();
+ } else if (x < _x) {
+ startWalkToX(x, false);
+ } else if (x - _x <= 105) {
+ startWalkToXExt(x);
+ } else {
+ startWalkToX(x, false);
+ }
+}
+
+void Klaymen::startWalkToXSmall(int16 x) {
+ _actionStatus = 2;
+ if (_x == x) {
+ _destX = x;
+ if (_isWalking) {
+ GotoState(NULL);
+ gotoNextStateExt();
+ }
+ } else if (_isWalking && ((!_doDeltaX && x - _x > 0) || (_doDeltaX && x - _x < 0))) {
+ _destX = x;
+ } else {
+ _destX = x;
+ GotoState(&Klaymen::stStartWalkingSmall);
+ }
+}
+
+void Klaymen::stStartWalkingSmall() {
+ _isWalking = true;
+ _acceptInput = true;
+ _actionStatus = 2;
+ setDoDeltaX(_destX < _x ? 1 : 0);
+ startAnimation(0x3A4CD934, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmWalkingSmall);
+ SetSpriteUpdate(&Klaymen::suWalkingTestExit);
+ FinalizeState(&Klaymen::evStartWalkingDone);
+}
+
+uint32 Klaymen::hmWalkingSmall(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = hmLowLevel(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x100D:
+ if (param.asInteger() == 0x32180101)
+ playSound(0, 0x4924AAC4);
+ else if (param.asInteger() == 0x0A2A9098)
+ playSound(0, 0x0A2AA8E0);
+ }
+ return messageResult;
+}
+
+void Klaymen::stStandIdleSmall() {
+ _busyStatus = 0;
+ _acceptInput = true;
+ startAnimation(0x90D0D1D0, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmLowLevel);
+ SetSpriteUpdate(NULL);
+}
+
+void Klaymen::stWonderAboutAfterSmall() {
+ _busyStatus = 0;
+ _acceptInput = true;
+ startAnimation(0x11C8D156, 30, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmLowLevelAnimation);
+ SetSpriteUpdate(NULL);
+}
+
+void Klaymen::stWonderAboutHalfSmall() {
+ _busyStatus = 0;
+ _acceptInput = true;
+ startAnimation(0x11C8D156, 0, 10);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmLowLevelAnimation);
+ SetSpriteUpdate(NULL);
+}
+
+void Klaymen::stWonderAboutSmall() {
+ _busyStatus = 0;
+ _acceptInput = true;
+ startAnimation(0x11C8D156, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmLowLevelAnimation);
+ SetSpriteUpdate(NULL);
+}
+
+void Klaymen::stWalkToFrontNoStepSmall() {
+ _busyStatus = 0;
+ _acceptInput = false;
+ startAnimationByHash(0x3F9CC394, 0x14884392, 0);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmWalkFrontBackSmall);
+ SetSpriteUpdate(&Klaymen::suUpdateDestX);
+}
+
+uint32 Klaymen::hmWalkFrontBackSmall(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = hmLowLevelAnimation(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x100D:
+ if (param.asInteger() == 0x80C110B5)
+ sendMessage(_parentScene, 0x482A, 0);
+ else if (param.asInteger() == 0x110010D1)
+ sendMessage(_parentScene, 0x482B, 0);
+ else if (param.asInteger() == 0x32180101)
+ playSound(0, 0x4924AAC4);
+ else if (param.asInteger() == 0x0A2A9098)
+ playSound(0, 0x0A2AA8E0);
+ break;
+ }
+ return messageResult;
+}
+
+void Klaymen::stWalkToFront2Small() {
+ _busyStatus = 0;
+ _acceptInput = false;
+ startAnimation(0x2F1C4694, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmWalkFrontBackSmall);
+ SetSpriteUpdate(&Klaymen::suUpdateDestX);
+}
+
+void Klaymen::stWalkToFrontSmall() {
+ _busyStatus = 0;
+ _acceptInput = false;
+ startAnimation(0x3F9CC394, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmWalkFrontBackSmall);
+ SetSpriteUpdate(&Klaymen::suUpdateDestX);
+}
+
+void Klaymen::stTurnToBackHalfSmall() {
+ _busyStatus = 0;
+ _acceptInput = false;
+ startAnimationByHash(0x37ECD436, 0, 0x8520108C);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmWalkFrontBackSmall);
+ SetSpriteUpdate(&Klaymen::suUpdateDestX);
+}
+
+void Klaymen::stTurnToBackWalkSmall() {
+ _busyStatus = 0;
+ _acceptInput = false;
+ startAnimation(0x16EDDE36, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmWalkFrontBackSmall);
+ SetSpriteUpdate(&Klaymen::suUpdateDestX);
+}
+
+void Klaymen::stTurnToBackSmall() {
+ _busyStatus = 0;
+ _acceptInput = false;
+ startAnimation(0x37ECD436, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmWalkFrontBackSmall);
+ SetSpriteUpdate(&Klaymen::suUpdateDestX);
+}
+
+void Klaymen::stPullCord() {
+ if (!stStartAction(AnimationCallback(&Klaymen::stPullCord))) {
+ _busyStatus = 2;
+ _acceptInput = false;
+ startAnimation(0x3F28E094, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmPullReleaseCord);
+ SetSpriteUpdate(NULL);
+ NextState(&Klaymen::stReleaseCord);
+ }
+}
+
+void Klaymen::stReleaseCord() {
+ _acceptInput = false;
+ startAnimation(0x3A28C094, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmPullReleaseCord);
+ SetSpriteUpdate(NULL);
+}
+
+uint32 Klaymen::hmPullReleaseCord(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = hmLowLevelAnimation(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x100D:
+ if (param.asInteger() == 0x4AB28209) {
+ sendMessage(_attachedSprite, 0x482A, 0);
+ sendMessage(_attachedSprite, 0x480F, 0);
+ } else if (param.asInteger() == 0x88001184) {
+ sendMessage(_attachedSprite, 0x482B, 0);
+ }
+ break;
+ }
+ return messageResult;
+}
+
+void Klaymen::stUseTube() {
+ if (!stStartAction(AnimationCallback(&Klaymen::stUseTube))) {
+ _busyStatus = 1;
+ _acceptInput = false;
+ startAnimation(0x1A38A814, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmUseTube);
+ SetSpriteUpdate(NULL);
+ }
+}
+
+uint32 Klaymen::hmUseTube(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = hmLowLevelAnimation(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x100D:
+ if (param.asInteger() == 0x02B20220)
+ playSound(0, 0xC5408620);
+ else if (param.asInteger() == 0x0A720138)
+ playSound(0, 0xD4C08010);
+ else if (param.asInteger() == 0x03020231)
+ playSound(0, 0xD4C08010);
+ else if (param.asInteger() == 0xB613A180)
+ playSound(0, 0x44051000);
+ else if (param.asInteger() == 0x67221A03)
+ playSound(0, 0x44051000);
+ else if (param.asInteger() == 0x038A010B)
+ playSound(0, 0x00018040);
+ else if (param.asInteger() == 0x422B0280)
+ playSound(0, 0x166FC6E0);
+ else if (param.asInteger() == 0x925A0C1E)
+ playSound(0, 0x40E5884D);
+ break;
+ }
+ return messageResult;
+}
+
+void Klaymen::stWalkingFirstExt() {
+ _busyStatus = 0;
+ _isWalking = true;
+ _acceptInput = true;
+ startAnimation(0x5A2CBC00, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmWalking);
+ SetSpriteUpdate(&Klaymen::suWalkingFirst);
+ NextState(&Klaymen::stUpdateWalkingFirst);
+ FinalizeState(&Klaymen::evStartWalkingDone);
+}
+
+void Klaymen::stStartWalkingExt() {
+ if (!stStartActionFromIdle(AnimationCallback(&Klaymen::stStartWalkingExt))) {
+ _busyStatus = 0;
+ _isWalking = true;
+ _acceptInput = true;
+ setDoDeltaX(_destX < _x ? 1 : 0);
+ startAnimation(0x272C1199, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmStartWalking);
+ SetSpriteUpdate(&Klaymen::suWalkingTestExit);
+ NextState(&Klaymen::stWalkingFirstExt);
+ FinalizeState(&Klaymen::evStartWalkingDone);
+ }
+}
+
+void Klaymen::startWalkToXDistance(int16 destX, int16 distance) {
+ if (_x > destX) {
+ if (_x == destX + distance) {
+ _destX = destX + distance;
+ gotoState(NULL);
+ gotoNextStateExt();
+ } else if (_x < destX + distance) {
+ startWalkToXExt(destX + distance);
+ } else {
+ startWalkToX(destX + distance, false);
+ }
+ } else {
+ if (_x == destX - distance) {
+ _destX = destX - distance;
+ gotoState(NULL);
+ gotoNextStateExt();
+ } else if (_x > destX - distance) {
+ startWalkToXExt(destX - distance);
+ } else {
+ startWalkToX(destX - distance, false);
+ }
+ }
+}
+
+void Klaymen::startWalkToXExt(int16 x) {
+ int16 xdiff = ABS(x - _x);
+ if (x == _x) {
+ _destX = x;
+ if (!_isWalking && !_isSneaking && !_isLargeStep) {
+ gotoState(NULL);
+ gotoNextStateExt();
+ }
+ } else if (xdiff <= 36 && !_isWalking && !_isSneaking && !_isLargeStep) {
+ _destX = x;
+ gotoState(NULL);
+ gotoNextStateExt();
+ } else if (xdiff <= 42 && _actionStatus != 3) {
+ if (_isSneaking && ((!_doDeltaX && x - _x > 0) || (_doDeltaX && x - _x < 0)) && ABS(_destX - _x) > xdiff) {
+ _destX = x;
+ } else {
+ _destX = x;
+ GotoState(&Klaymen::stSneak);
+ }
+ } else if (_isLargeStep && ((!_doDeltaX && x - _x > 0) || (_doDeltaX && x - _x < 0))) {
+ _destX = x;
+ } else {
+ _destX = x;
+ GotoState(&Klaymen::stLargeStep);
+ }
+}
+
+void Klaymen::stLargeStep() {
+ _busyStatus = 2;
+ _isLargeStep = true;
+ _acceptInput = true;
+ setDoDeltaX(_destX >= _x ? 1 : 0);
+ startAnimation(0x08B28116, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmLargeStep);
+ SetSpriteUpdate(&Klaymen::suLargeStep);
+ FinalizeState(&Klaymen::evLargeStepDone);
+}
+
+void Klaymen::evLargeStepDone() {
+ _isLargeStep = false;
+}
+
+void Klaymen::suLargeStep() {
+ int16 xdiff = _destX - _x;
+
+ if (_doDeltaX) {
+ _deltaX = -_deltaX;
+ }
+
+ if (_currFrameIndex == 7) {
+ _deltaX = xdiff;
+ }
+
+ if ((xdiff > 0 && xdiff > _deltaX) || (xdiff < 0 && xdiff < _deltaX))
+ xdiff = _deltaX;
+
+ _deltaX = 0;
+
+ if (_x != _destX) {
+ HitRect *hitRectPrev = _parentScene->findHitRectAtPos(_x, _y);
+ _x += xdiff;
+ if (_pathPoints) {
+ walkAlongPathPoints();
+ } else {
+ HitRect *hitRectNext = _parentScene->findHitRectAtPos(_x, _y);
+ if (hitRectNext->type == 0x5002) {
+ _y = MAX<int16>(hitRectNext->rect.y1, hitRectNext->rect.y2 - (hitRectNext->rect.x2 - _x) / 2);
+ } else if (hitRectNext->type == 0x5003) {
+ _y = MAX<int16>(hitRectNext->rect.y1, hitRectNext->rect.y2 - (_x - hitRectNext->rect.x1) / 2);
+ } else if (hitRectPrev->type == 0x5002) {
+ _y = xdiff > 0 ? hitRectPrev->rect.y2 : hitRectPrev->rect.y1;
+ } else if (hitRectPrev->type == 0x5003) {
+ _y = xdiff < 0 ? hitRectPrev->rect.y2 : hitRectPrev->rect.y1;
+ }
+ }
+ updateBounds();
+ }
+}
+
+uint32 Klaymen::hmLargeStep(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = hmLowLevel(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x100D:
+ if (param.asInteger() == 0x32180101) {
+ playSound(0, 0x4924AAC4);
+ } else if (param.asInteger() == 0x0A2A9098) {
+ playSound(0, 0x0A2AA8E0);
+ }
+ break;
+ case 0x3002:
+ _x = _destX;
+ gotoNextStateExt();
+ break;
+ }
+ return messageResult;
+}
+
+void Klaymen::stWonderAboutHalf() {
+ _busyStatus = 0;
+ _acceptInput = true;
+ startAnimation(0xD820A114, 0, 10);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmLowLevelAnimation);
+ SetSpriteUpdate(NULL);
+}
+
+void Klaymen::stWonderAboutAfter() {
+ _busyStatus = 1;
+ _acceptInput = true;
+ startAnimation(0xD820A114, 30, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmLowLevelAnimation);
+ SetSpriteUpdate(NULL);
+}
+
+void Klaymen::stTurnToUseHalf() {
+ _busyStatus = 0;
+ _acceptInput = true;
+ startAnimation(0x9B250AD2, 0, 7);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmTurnToUse);
+ SetSpriteUpdate(NULL);
+}
+
+uint32 Klaymen::hmTurnToUse(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = hmLowLevelAnimation(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x100D:
+ if (param.asInteger() == 0x32180101) {
+ playSound(0, 0x4924AAC4);
+ } else if (param.asInteger() == 0x0A2A9098) {
+ playSound(0, 0x0A2AA8E0);
+ }
+ break;
+ }
+ return messageResult;
+}
+
+void Klaymen::stTurnAwayFromUse() {
+ _busyStatus = 1;
+ _acceptInput = true;
+ startAnimation(0x98F88391, 4, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmTurnToUse);
+ SetSpriteUpdate(NULL);
+}
+
+void Klaymen::stWonderAbout() {
+ _busyStatus = 1;
+ _acceptInput = true;
+ startAnimation(0xD820A114, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmLowLevelAnimation);
+ SetSpriteUpdate(NULL);
+}
+
+void Klaymen::stPeekWall() {
+ _busyStatus = 1;
+ _acceptInput = true;
+ startAnimation(0xAC20C012, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmPeekWall);
+ SetSpriteUpdate(NULL);
+}
+
+uint32 Klaymen::hmPeekWall(int messageNum, const MessageParam &param, Entity *sender) {
+ int16 speedUpFrameIndex;
+ switch (messageNum) {
+ case 0x1008:
+ speedUpFrameIndex = getFrameIndex(kKlaymenSpeedUpHash);
+ if (_currFrameIndex < speedUpFrameIndex)
+ startAnimation(0xAC20C012, speedUpFrameIndex, -1);
+ return 0;
+ case 0x100D:
+ if (param.asInteger() == 0x32180101) {
+ playSound(0, 0x405002D8);
+ } else if (param.asInteger() == 0x0A2A9098) {
+ playSound(0, 0x0460E2FA);
+ }
+ break;
+ }
+ return hmLowLevelAnimation(messageNum, param, sender);
+}
+
+void Klaymen::stJumpToRing1() {
+ if (!stStartAction(AnimationCallback(&Klaymen::stJumpToRing1))) {
+ _busyStatus = 0;
+ startAnimation(0xD82890BA, 0, -1);
+ setupJumpToRing();
+ }
+}
+
+void Klaymen::setupJumpToRing() {
+ _acceptInput = false;
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmJumpToRing);
+ SetSpriteUpdate(&Klaymen::suUpdateDestX);
+ NextState(&Klaymen::stHangOnRing);
+ sendMessage(_attachedSprite, 0x482B, 0);
+}
+
+uint32 Klaymen::hmJumpToRing(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = hmLowLevelAnimation(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x100D:
+ if (param.asInteger() == 0x168050A0) {
+ sendMessage(_attachedSprite, 0x4806, 0);
+ _acceptInput = true;
+ } else if (param.asInteger() == 0x320AC306) {
+ playSound(0, 0x5860C640);
+ } else if (param.asInteger() == 0x4AB28209) {
+ sendMessage(_attachedSprite, 0x482A, 0);
+ } else if (param.asInteger() == 0x88001184) {
+ sendMessage(_attachedSprite, 0x482B, 0);
+ }
+ break;
+ }
+ return messageResult;
+}
+
+void Klaymen::suUpdateDestX() {
+ AnimatedSprite::updateDeltaXY();
+ _destX = _x;
+}
+
+void Klaymen::stHangOnRing() {
+ _busyStatus = 0;
+ _acceptInput = true;
+ startAnimation(0x4829E0B8, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmLowLevel);
+ SetSpriteUpdate(NULL);
+}
+
+void Klaymen::stJumpToRing2() {
+ if (!stStartAction(AnimationCallback(&Klaymen::stJumpToRing2))) {
+ _busyStatus = 0;
+ startAnimation(0x900980B2, 0, -1);
+ setupJumpToRing();
+ }
+}
+
+void Klaymen::stJumpToRing3() {
+ if (!stStartAction(AnimationCallback(&Klaymen::stJumpToRing3))) {
+ _busyStatus = 0;
+ _acceptInput = false;
+ startAnimation(0xBA1910B2, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetSpriteUpdate(&Klaymen::suUpdateDestX);
+ SetMessageHandler(&Klaymen::hmJumpToRing3);
+ NextState(&Klaymen::stHoldRing3);
+ sendMessage(_attachedSprite, 0x482B, 0);
+ }
+}
+
+uint32 Klaymen::hmJumpToRing3(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = hmLowLevelAnimation(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x100D:
+ if (param.asInteger() == 0x168050A0) {
+ sendMessage(_attachedSprite, 0x4806, 0);
+ } else if (param.asInteger() == 0x320AC306) {
+ playSound(0, 0x5860C640);
+ } else if (param.asInteger() == 0x4AB28209) {
+ sendMessage(_attachedSprite, 0x482A, 0);
+ } else if (param.asInteger() == 0x88001184) {
+ sendMessage(_attachedSprite, 0x482B, 0);
+ }
+ break;
+ }
+ return messageResult;
+}
+
+void Klaymen::stHoldRing3() {
+ _busyStatus = 0;
+ _acceptInput = true;
+ startAnimation(0x4A293FB0, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmHoldRing3);
+ SetSpriteUpdate(NULL);
+}
+
+uint32 Klaymen::hmHoldRing3(int messageNum, const MessageParam &param, Entity *sender) {
+ if (messageNum == 0x1008) {
+ stReleaseRing();
+ return 0;
+ }
+ return hmLowLevel(messageNum, param, sender);
+}
+
+void Klaymen::stReleaseRing() {
+ _busyStatus = 1;
+ _acceptInput = false;
+ sendMessage(_attachedSprite, 0x4807, 0);
+ _attachedSprite = NULL;
+ startAnimation(0xB869A4B9, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmLowLevelAnimation);
+ SetSpriteUpdate(NULL);
+}
+
+void Klaymen::stJumpToRing4() {
+ if (!stStartAction(AnimationCallback(&Klaymen::stJumpToRing4))) {
+ _busyStatus = 0;
+ startAnimation(0xB8699832, 0, -1);
+ setupJumpToRing();
+ }
+}
+
+void Klaymen::startWalkToAttachedSpriteXDistance(int16 distance) {
+ startWalkToXDistance(_attachedSprite->getX(), distance);
+}
+
+void Klaymen::stContinueClimbLadderUp() {
+ _busyStatus = 0;
+ _acceptInput = true;
+ _ladderStatus = 3;
+ startAnimationByHash(0x3A292504, 0x01084280, 0);
+ _newStickFrameHash = 0x01084280;
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmLowLevel);
+ SetSpriteUpdate(NULL);
+ gotoNextStateExt();
+}
+
+void Klaymen::stStartClimbLadderDown() {
+ if (!stStartAction(AnimationCallback(&Klaymen::stStartClimbLadderDown))) {
+ _busyStatus = 0;
+ if (_destY < _y) {
+ if (_ladderStatus == 1) {
+ _ladderStatus = 2;
+ stClimbLadderHalf();
+ } else {
+ gotoNextStateExt();
+ }
+ } else if (_ladderStatus == 0) {
+ _ladderStatus = 2;
+ _acceptInput = false;
+ startAnimation(0x122D1505, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmClimbLadderUpDown);
+ SetSpriteUpdate(&Klaymen::suUpdateDestX);
+ } else if (_ladderStatus == 3) {
+ _ladderStatus = 2;
+ _acceptInput = true;
+ startAnimationByHash(0x122D1505, 0x01084280, 0);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmClimbLadderUpDown);
+ SetSpriteUpdate(&Klaymen::suUpdateDestX);
+ } else if (_ladderStatus == 1) {
+ _ladderStatus = 2;
+ _acceptInput = true;
+ startAnimation(0x122D1505, 29 - _currFrameIndex, -1);
+ }
+ }
+}
+
+void Klaymen::stClimbLadderHalf() {
+ _busyStatus = 2;
+ if (_ladderStatus == 1) {
+ _ladderStatus = 0;
+ _acceptInput = false;
+ startAnimationByHash(0x3A292504, 0x02421405, 0);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmClimbLadderHalf);
+ SetSpriteUpdate(&Klaymen::suUpdateDestX);
+ } else if (_ladderStatus == 2) {
+ _ladderStatus = 0;
+ _acceptInput = false;
+ startAnimationByHash(0x122D1505, 0x02421405, 0);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmClimbLadderHalf);
+ SetSpriteUpdate(&Klaymen::suUpdateDestX);
+ } else {
+ gotoNextStateExt();
+ }
+}
+
+uint32 Klaymen::hmClimbLadderHalf(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = hmLowLevelAnimation(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x100D:
+ if (param.asInteger() == 0x489B025C) {
+ playSound(0, 0x52C4C2D7);
+ } else if (param.asInteger() == 0x400A0E64) {
+ playSound(0, 0x50E081D9);
+ } else if (param.asInteger() == 0x32180101) {
+ playSound(0, 0x405002D8);
+ } else if (param.asInteger() == 0x0A2A9098) {
+ playSound(0, 0x0460E2FA);
+ }
+ break;
+ }
+ return messageResult;
+}
+
+uint32 Klaymen::hmClimbLadderUpDown(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = hmLowLevel(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x100D:
+ if (param.asInteger() == 0x01084280) {
+ _acceptInput = true;
+ } else if (param.asInteger() == 0x489B025C) {
+ playSound(0, 0x52C4C2D7);
+ } else if (param.asInteger() == 0x400A0E64) {
+ playSound(0, 0x50E081D9);
+ } else if (param.asInteger() == 0x02421405) {
+ if (_ladderStatus == 1) {
+ startAnimationByHash(0x3A292504, 0x01084280, 0);
+ if (_destY >= _y - 30)
+ sendMessage(this, 0x1019, 0);
+ } else {
+ startAnimationByHash(0x122D1505, 0x01084280, 0);
+ if (_destY <= _y)
+ sendMessage(this, 0x1019, 0);
+ }
+ }
+ break;
+ }
+ return messageResult;
+}
+
+void Klaymen::stStartClimbLadderUp() {
+ if (!stStartAction(AnimationCallback(&Klaymen::stStartClimbLadderUp))) {
+ _busyStatus = 0;
+ if (_destY >= _y - 30) {
+ gotoNextStateExt();
+ } else if (_ladderStatus == 0) {
+ _ladderStatus = 1;
+ _acceptInput = false;
+ startAnimation(0x3A292504, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmClimbLadderUpDown);
+ SetSpriteUpdate(&Klaymen::suUpdateDestX);
+ } else if (_ladderStatus == 3) {
+ _ladderStatus = 1;
+ _acceptInput = true;
+ startAnimationByHash(0x3A292504, 0x01084280, 0);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmClimbLadderUpDown);
+ SetSpriteUpdate(&Klaymen::suUpdateDestX);
+ } else if (_ladderStatus == 2) {
+ _ladderStatus = 1;
+ _acceptInput = true;
+ startAnimation(0x3A292504, 29 - _currFrameIndex, -1);
+ }
+ }
+}
+
+void Klaymen::stWalkToFrontNoStep() {
+ _busyStatus = 2;
+ _acceptInput = false;
+ startAnimationByHash(0xF229C003, 0x14884392, 0);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmWalkToFront);
+ SetSpriteUpdate(&Klaymen::suUpdateDestX);
+}
+
+uint32 Klaymen::hmWalkToFront(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = hmLowLevelAnimation(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x100D:
+ if (param.asInteger() == 0x80C110B5) {
+ sendMessage(_parentScene, 0x482A, 0);
+ } else if (param.asInteger() == 0x110010D1) {
+ sendMessage(_parentScene, 0x482B, 0);
+ } else if (param.asInteger() == 0x32180101) {
+ playSound(0, _soundFlag ? 0x48498E46 : 0x405002D8);
+ } else if (param.asInteger() == 0x0A2A9098) {
+ playSound(0, _soundFlag ? 0x50399F64 : 0x0460E2FA);
+ }
+ break;
+ }
+ return messageResult;
+}
+
+void Klaymen::stWalkToFront() {
+ if (!stStartAction(AnimationCallback(&Klaymen::stWalkToFront))) {
+ _busyStatus = 2;
+ _acceptInput = false;
+ startAnimation(0xF229C003, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmWalkToFront);
+ SetSpriteUpdate(&Klaymen::suUpdateDestX);
+ }
+}
+
+void Klaymen::stTurnToFront() {
+ if (!stStartAction(AnimationCallback(&Klaymen::stTurnToFront))) {
+ _busyStatus = 0;
+ _acceptInput = false;
+ startAnimationByHash(0xCA221107, 0, 0x8520108C);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmWalkToFront);
+ SetSpriteUpdate(&Klaymen::suUpdateDestX);
+ }
+}
+
+void Klaymen::stTurnToBack() {
+ if (!stStartAction(AnimationCallback(&Klaymen::stTurnToBack))) {
+ _busyStatus = 2;
+ _acceptInput = false;
+ startAnimation(0xCA221107, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmWalkToFront);
+ SetSpriteUpdate(&Klaymen::suUpdateDestX);
+ }
+}
+
+void Klaymen::stLandOnFeet() {
+ _busyStatus = 1;
+ _acceptInput = true;
+ startAnimation(0x18118554, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmLandOnFeet);
+ SetSpriteUpdate(NULL);
+}
+
+uint32 Klaymen::hmLandOnFeet(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = hmLowLevelAnimation(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x100D:
+ if (param.asInteger() == 0x320AC306) {
+ playSound(0, 0x5860C640);
+ }
+ break;
+ }
+ return messageResult;
+}
+
+void Klaymen::stTurnToBackToUse() {
+ if (!stStartAction(AnimationCallback(&Klaymen::stTurnToBackToUse))) {
+ _busyStatus = 2;
+ _acceptInput = false;
+ startAnimation(0x91540140, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmTurnToBackToUse);
+ SetSpriteUpdate(&Klaymen::suUpdateDestX);
+ }
+}
+
+uint32 Klaymen::hmTurnToBackToUse(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = hmLowLevelAnimation(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x100D:
+ if (param.asInteger() == 0xC61A0119) {
+ playSound(0, 0x402338C2);
+ } else if (param.asInteger() == 0x32180101) {
+ playSound(0, 0x4924AAC4);
+ } else if (param.asInteger() == 0x0A2A9098) {
+ playSound(0, 0x0A2AA8E0);
+ }
+ break;
+ }
+ return messageResult;
+}
+
+void Klaymen::stClayDoorOpen() {
+ if (!stStartAction(AnimationCallback(&Klaymen::stClayDoorOpen))) {
+ _busyStatus = 2;
+ _acceptInput = false;
+ startAnimation(0x5CCCB330, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmClayDoorOpen);
+ SetSpriteUpdate(&Klaymen::suUpdateDestX);
+ }
+}
+
+uint32 Klaymen::hmClayDoorOpen(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = hmLowLevelAnimation(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x100D:
+ if (param.asInteger() == 0x040D4186) {
+ sendMessage(_attachedSprite, 0x4808, 0);
+ }
+ break;
+ }
+ return messageResult;
+}
+
+void Klaymen::stTurnToUse() {
+ if (!stStartAction(AnimationCallback(&Klaymen::stTurnToUse))) {
+ _busyStatus = 2;
+ _acceptInput = false;
+ startAnimation(0x9B250AD2, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmTurnToUse);
+ SetSpriteUpdate(&Klaymen::suUpdateDestX);
+ }
+}
+
+void Klaymen::stReturnFromUse() {
+ _busyStatus = 2;
+ _acceptInput = false;
+ startAnimation(0x98F88391, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmTurnToUse);
+ SetSpriteUpdate(&Klaymen::suUpdateDestX);
+}
+
+void Klaymen::stWalkingOpenDoor() {
+ _isWalkingOpenDoorNotified = false;
+ _acceptInput = false;
+ startAnimation(0x11A8E012, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmStartWalking);
+ SetSpriteUpdate(&Klaymen::suWalkingOpenDoor);
+}
+
+void Klaymen::suWalkingOpenDoor() {
+ if (!_isWalkingOpenDoorNotified && ABS(_destX - _x) < 80) {
+ sendMessage(_parentScene, 0x4829, 0);
+ _isWalkingOpenDoorNotified = true;
+ }
+ AnimatedSprite::updateDeltaXY();
+}
+
+void Klaymen::stMoveObjectSkipTurnFaceObject() {
+ setDoDeltaX(_attachedSprite->getX() < _x ? 1 : 0);
+ _isMoveObjectRequested = false;
+ _acceptInput = true;
+ startAnimationByHash(0x0C1CA072, 0x01084280, 0);
+ SetUpdateHandler(&Klaymen::update);
+ SetSpriteUpdate(&Klaymen::suUpdateDestX);
+ SetMessageHandler(&Klaymen::hmMoveObjectTurn);
+}
+
+void Klaymen::evMoveObjectTurnDone() {
+ sendMessage(_attachedSprite, 0x4807, 0);
+}
+
+uint32 Klaymen::hmMoveObjectTurn(int messageNum, const MessageParam &param, Entity *sender) {
+ switch (messageNum) {
+ case 0x100D:
+ if (param.asInteger() == 0x01084280) {
+ sendMessage(_attachedSprite, 0x480B, _doDeltaX ? 1 : 0);
+ } else if (param.asInteger() == 0x02421405) {
+ if (_isMoveObjectRequested && sendMessage(_attachedSprite, 0x480C, _doDeltaX ? 1 : 0) != 0) {
+ stMoveObjectSkipTurn();
+ } else {
+ FinalizeState(&Klaymen::evMoveObjectTurnDone);
+ SetMessageHandler(&Klaymen::hmLowLevelAnimation);
+ }
+ } else if (param.asInteger() == 0x32180101) {
+ playSound(0, 0x405002D8);
+ } else if (param.asInteger() == 0x0A2A9098) {
+ playSound(0, 0x0460E2FA);
+ }
+ break;
+ case 0x480A:
+ _isMoveObjectRequested = true;
+ return 0;
+ }
+ return hmLowLevelAnimation(messageNum, param, sender);
+}
+
+void Klaymen::stMoveObjectSkipTurn() {
+ _isMoveObjectRequested = false;
+ _acceptInput = true;
+ startAnimationByHash(0x0C1CA072, 0x01084280, 0);
+ SetUpdateHandler(&Klaymen::update);
+ SetSpriteUpdate(&Klaymen::suUpdateDestX);
+ SetMessageHandler(&Klaymen::hmMoveObjectTurn);
+}
+
+void Klaymen::stMoveObjectFaceObject() {
+ setDoDeltaX(_attachedSprite->getX() < _x ? 1 : 0);
+ if (!stStartAction(AnimationCallback(&Klaymen::stMoveObjectFaceObject))) {
+ _busyStatus = 2;
+ _isMoveObjectRequested = false;
+ _acceptInput = true;
+ startAnimation(0x0C1CA072, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmMoveObjectTurn);
+ SetSpriteUpdate(&Klaymen::suUpdateDestX);
+ }
+}
+
+void Klaymen::stUseLever() {
+ if (!stStartAction(AnimationCallback(&Klaymen::stUseLever))) {
+ _busyStatus = 0;
+ if (_isLeverDown) {
+ stUseLeverRelease();
+ } else {
+ sendMessage(_attachedSprite, 0x482B, 0);
+ startAnimation(0x0C303040, 0, -1);
+ SetSpriteUpdate(&Klaymen::suUpdateDestX);
+ SetMessageHandler(&Klaymen::hmLever);
+ SetUpdateHandler(&Klaymen::update);
+ NextState(&Klaymen::stPullLeverDown);
+ _acceptInput = false;
+ }
+ }
+}
+
+// Exactly the same code as sub420DA0 which was removed
+void Klaymen::stPullLeverDown() {
+ startAnimation(0x0D318140, 0, -1);
+ sendMessage(_attachedSprite, 0x480F, 0);
+ NextState(&Klaymen::stHoldLeverDown);
+}
+
+void Klaymen::stHoldLeverDown() {
+ startAnimation(0x4464A440, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmLowLevel);
+ SetSpriteUpdate(&Klaymen::suUpdateDestX);
+ _isLeverDown = true;
+ _acceptInput = true;
+}
+
+void Klaymen::stUseLeverRelease() {
+ startAnimation(0x09018068, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmLever);
+ SetSpriteUpdate(&Klaymen::suUpdateDestX);
+ sendMessage(_attachedSprite, 0x4807, 0);
+ NextState(&Klaymen::stPullLeverDown);
+ _acceptInput = false;
+}
+
+void Klaymen::stReleaseLever() {
+ if (_isLeverDown) {
+ _busyStatus = 2;
+ startAnimation(0x09018068, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmLever);
+ SetSpriteUpdate(&Klaymen::suUpdateDestX);
+ sendMessage(_attachedSprite, 0x4807, 0);
+ NextState(&Klaymen::stLetGoOfLever);
+ _acceptInput = false;
+ _isLeverDown = false;
+ } else {
+ gotoNextStateExt();
+ }
+}
+
+void Klaymen::stLetGoOfLever() {
+ startAnimation(0x0928C048, 0, -1);
+ FinalizeState(&Klaymen::evLeverReleasedEvent);
+}
+
+void Klaymen::evLeverReleasedEvent() {
+ sendMessage(_attachedSprite, 0x482A, 0);
+}
+
+void Klaymen::stInsertDisk() {
+ if (!stStartActionFromIdle(AnimationCallback(&Klaymen::stInsertDisk))) {
+ _busyStatus = 2;
+ _tapesToInsert = 0;
+ for (uint32 i = 0; i < 20; i++) {
+ if (getSubVar(VA_HAS_TAPE, i)) {
+ setSubVar(VA_IS_TAPE_INSERTED, i, 1);
+ setSubVar(VA_HAS_TAPE, i, 0);
+ _tapesToInsert++;
+ }
+ }
+ if (_tapesToInsert == 0) {
+ GotoState(NULL);
+ gotoNextStateExt();
+ } else {
+ startAnimation(0xD8C8D100, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmInsertDisk);
+ SetSpriteUpdate(&Klaymen::suAction);
+ _acceptInput = false;
+ _tapesToInsert--;
+ }
+ }
+}
+
+uint32 Klaymen::hmInsertDisk(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = Klaymen::hmLowLevelAnimation(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x100D:
+ if (_tapesToInsert == 0 && param.asInteger() == 0x06040580) {
+ nextAnimationByHash(0xD8C8D100, calcHash("GoToStartLoop/Finish"), 0);
+ } else if (_tapesToInsert != 0 && param.asInteger() == calcHash("GoToStartLoop/Finish")) {
+ _tapesToInsert--;
+ startAnimationByHash(0xD8C8D100, 0x01084280, 0);
+ } else if (param.asInteger() == 0x062A1510) {
+ playSound(0, 0x41688704);
+ } else if (param.asInteger() == 0x02B20220) {
+ playSound(0, 0xC5408620);
+ } else if (param.asInteger() == 0x0A720138) {
+ playSound(0, 0xD4C08010);
+ } else if (param.asInteger() == 0xB613A180) {
+ playSound(0, 0x44051000);
+ } else if (param.asInteger() == 0x0E040501) {
+ playSound(1, 0xC6A129C1);
+ }
+ }
+ return messageResult;
+}
+
+void Klaymen::walkAlongPathPoints() {
+ if (_x <= (*_pathPoints)[0].x)
+ _y = (*_pathPoints)[0].y;
+ else if (_x >= (*_pathPoints)[_pathPoints->size() - 1].x)
+ _y = (*_pathPoints)[_pathPoints->size() - 1].y;
+ else {
+ int16 deltaX = _x - (*_pathPoints)[0].x, deltaXIncr = 0;
+ uint index = 0;
+ while (deltaX > 0) {
+ NPoint pt2 = (*_pathPoints)[index];
+ NPoint pt1 = index + 1 >= _pathPoints->size() ? (*_pathPoints)[0] : (*_pathPoints)[index + 1];
+ int16 xd = ABS(pt1.x - pt2.x);
+ int16 yd = ABS(pt1.y - pt2.y);
+ if (deltaX + deltaXIncr >= xd) {
+ deltaX -= xd;
+ deltaX += deltaXIncr;
+ ++index;
+ if (index >= _pathPoints->size())
+ index = 0;
+ _y = (*_pathPoints)[index].y;
+ } else {
+ deltaXIncr += deltaX;
+ if (pt1.y >= pt2.y) {
+ _y = pt2.y + (yd * deltaXIncr) / xd;
+ } else {
+ _y = pt2.y - (yd * deltaXIncr) / xd;
+ }
+ deltaX = 0;
+ }
+ }
+ }
+}
+
+void Klaymen::enterIdleAnimation(uint idleAnimation) {
+ switch (idleAnimation) {
+ case kIdlePickEar:
+ startIdleAnimation(0x5B20C814, AnimationCallback(&Klaymen::stIdlePickEar));
+ break;
+ case kIdleSpinHead:
+ startIdleAnimation(0xD122C137, AnimationCallback(&Klaymen::stIdleSpinHead));
+ break;
+ case kIdleArms:
+ startIdleAnimation(0x543CD054, AnimationCallback(&Klaymen::stIdleArms));
+ break;
+ case kIdleChest:
+ startIdleAnimation(0x40A0C034, AnimationCallback(&Klaymen::stIdleChest));
+ break;
+ case kIdleHeadOff:
+ startIdleAnimation(0x5120E137, AnimationCallback(&Klaymen::stIdleHeadOff));
+ break;
+ case kIdleTeleporterHands:
+ startIdleAnimation(0x90EF8D38, AnimationCallback(&Klaymen::stIdleTeleporterHands));
+ break;
+ case kIdleTeleporterHands2:
+ startIdleAnimation(0x900F0930, AnimationCallback(&Klaymen::stIdleTeleporterHands2));
+ break;
+ case kIdleWonderAbout:
+ stIdleWonderAbout();
+ break;
+ }
+}
+
+void Klaymen::stJumpToGrab() {
+ _busyStatus = 0;
+ _acceptInput = false;
+ startAnimationByHash(0x00AB8C10, 0x01084280, 0);
+ SetUpdateHandler(&Klaymen::update);
+ SetSpriteUpdate(&Klaymen::suJumpToGrab);
+ SetMessageHandler(&Klaymen::hmJumpToGrab);
+}
+
+void Klaymen::suJumpToGrab() {
+ updateDeltaXY();
+ if (_y >= _destY) {
+ _y = _destY;
+ updateBounds();
+ gotoNextStateExt();
+ }
+}
+
+uint32 Klaymen::hmJumpToGrab(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = hmLowLevel(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x100D:
+ if (param.asInteger() == 0x168050A0)
+ sendMessage(_attachedSprite, 0x4806, 0);
+ else if (param.asInteger() == 0x320AC306)
+ startAnimationByHash(0x00AB8C10, 0x01084280, 0);
+ else if (param.asInteger() == 0x4AB28209)
+ sendMessage(_attachedSprite, 0x482A, 0);
+ else if (param.asInteger() == 0x88001184)
+ sendMessage(_attachedSprite, 0x482B, 0);
+ break;
+ }
+ return messageResult;
+}
+
+void Klaymen::stFinishGrow() {
+ _busyStatus = 2;
+ _acceptInput = false;
+ startAnimation(0x38445000, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetSpriteUpdate(NULL);
+ SetMessageHandler(&Klaymen::hmFinishGrow);
+}
+
+uint32 Klaymen::hmFinishGrow(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = hmLowLevelAnimation(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x100D:
+ if (param.asInteger() == 0x040C4C01)
+ playSound(0, 0x01E11140);
+ break;
+ }
+ return messageResult;
+}
+
+void Klaymen::stTurnToUseExt() {
+ if (!stStartAction(AnimationCallback(&Klaymen::stTurnToUseExt))) {
+ _busyStatus = 2;
+ _acceptInput = false;
+ startAnimation(0x1B3D8216, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmTurnToUse);
+ SetSpriteUpdate(&Klaymen::suUpdateDestX);
+ }
+}
+
+void Klaymen::stJumpToGrabFall() {
+ if (!stStartAction(AnimationCallback(&Klaymen::stJumpToGrabFall))) {
+ _busyStatus = 0;
+ _acceptInput = false;
+ startAnimation(0x00AB8C10, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmJumpToGrab);
+ SetSpriteUpdate(&Klaymen::suJumpToGrab);
+ sendMessage(_attachedSprite, 0x482B, 0);
+ }
+}
+
+void Klaymen::stJumpToGrabRelease() {
+ _busyStatus = 1;
+ _acceptInput = false;
+ startAnimationByHash(0x00AB8C10, 0x320AC306, 0);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmJumpToGrabRelease);
+ SetSpriteUpdate(NULL);
+ NextState(&Klaymen::stReleaseRing);
+}
+
+uint32 Klaymen::hmJumpToGrabRelease(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = hmLowLevelAnimation(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x100D:
+ if (param.asInteger() == 0x320AC306)
+ playSound(0, 0x5860C640);
+ break;
+ }
+ return messageResult;
+}
+
+void Klaymen::stIdleTeleporterHands() {
+ _busyStatus = 0;
+ _acceptInput = true;
+ startAnimation(0x90EF8D38, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmLowLevelAnimation);
+ SetSpriteUpdate(NULL);
+ NextState(&Klaymen::stSitIdleTeleporterBlinkSecond);
+}
+
+void Klaymen::stIdleTeleporterHands2() {
+ _busyStatus = 0;
+ _acceptInput = true;
+ startAnimation(0x900F0930, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmLowLevelAnimation);
+ SetSpriteUpdate(NULL);
+ NextState(&Klaymen::stSitIdleTeleporterBlinkSecond);
+}
+
+void Klaymen::teleporterAppear(uint32 fileHash) {
+ _busyStatus = 0;
+ _acceptInput = false;
+ startAnimation(fileHash, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmTeleporterAppearDisappear);
+ SetSpriteUpdate(NULL);
+}
+
+void Klaymen::teleporterDisappear(uint32 fileHash) {
+ _busyStatus = 0;
+ _acceptInput = false;
+ startAnimation(fileHash, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmTeleporterAppearDisappear);
+ SetSpriteUpdate(NULL);
+}
+
+uint32 Klaymen::hmTeleporterAppearDisappear(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = hmLowLevelAnimation(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x100D:
+ if (param.asInteger() == 0x4E0A2C24) {
+ playSound(0, 0x85B10BB8);
+ } else if (param.asInteger() == 0x4E6A0CA0) {
+ playSound(0, 0xC5B709B0);
+ }
+ break;
+ }
+ return messageResult;
+}
+
+uint32 Klaymen::hmShrink(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = hmLowLevelAnimation(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x100D:
+ if (param.asInteger() == 0x80C110B5)
+ sendMessage(_parentScene, 0x482A, 0);
+ else if (param.asInteger() == 0x33288344)
+ playSound(2, 0x10688664);
+ break;
+ }
+ return messageResult;
+}
+
+void Klaymen::stShrink() {
+ _busyStatus = 0;
+ _acceptInput = false;
+ playSound(0, 0x4C69EA53);
+ startAnimation(0x1AE88904, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmShrink);
+ SetSpriteUpdate(&AnimatedSprite::updateDeltaXY);
+}
+
+void Klaymen::stStandWonderAbout() {
+ if (_x > 260)
+ setDoDeltaX(1);
+ _busyStatus = 0;
+ _acceptInput = true;
+ startAnimation(0xD820A114, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmLowLevel);
+ SetSpriteUpdate(NULL);
+ _newStickFrameIndex = 10;
+}
+
+uint32 Klaymen::hmDrinkPotion(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = hmLowLevelAnimation(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x1008:
+ if (_potionFlag1) {
+ startAnimationByHash(0x1C388C04, 0x004A2148, 0);
+ messageResult = 0;
+ } else
+ _potionFlag2 = true;
+ break;
+ case 0x100D:
+ if (param.asInteger() == 0x0002418E)
+ sendMessage(_parentScene, 0x2000, 0);
+ else if (param.asInteger() == 0x924090C2) {
+ _potionFlag1 = true;
+ if (_potionFlag2) {
+ startAnimationByHash(0x1C388C04, 0x004A2148, 0);
+ messageResult = 0;
+ }
+ } else if (param.asInteger() == 0x004A2148)
+ _potionFlag1 = false;
+ else if (param.asInteger() == 0x02B20220)
+ playSound(0, 0xC5408620);
+ else if (param.asInteger() == 0x0A720138)
+ playSound(0, 0xD4C08010);
+ else if (param.asInteger() == 0x03020231)
+ playSound(0, 0xD4C08010);
+ else if (param.asInteger() == 0xB613A180)
+ playSound(0, 0x44051000);
+ else if (param.asInteger() == 0x67221A03)
+ playSound(0, 0x44051000);
+ else if (param.asInteger() == 0x038A010B)
+ playSound(0, 0x00018040);
+ else if (param.asInteger() == 0x422B0280)
+ playSound(0, 0x166FC6E0);
+ else if (param.asInteger() == 0x925A0C1E)
+ playSound(0, 0x40E5884D);
+ else if (param.asInteger() == 0x000F0082)
+ playSound(0, 0x546CDCC1);
+ else if (param.asInteger() == 0x00020814)
+ playSound(0, 0x786CC6D0);
+ else if (param.asInteger() == 0x06020500)
+ playSound(0, 0x1069C0E1);
+ else if (param.asInteger() == 0x02128C00)
+ playSound(0, 0x5068C4C3);
+ else if (param.asInteger() == 0x82022030)
+ playSound(0, 0x5C48C0E8);
+ break;
+ }
+ return messageResult;
+}
+
+uint32 Klaymen::hmGrow(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = hmLowLevelAnimation(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x100D:
+ if (param.asInteger() == 0x32180101)
+ playSound(0, 0x405002D8);
+ else if (param.asInteger() == 0x0A2A9098)
+ playSound(0, 0x0460E2FA);
+ else if (param.asInteger() == 0xD00A0C0C)
+ playSound(3);
+ else if (param.asInteger() == 0x04121920)
+ playSound(4);
+ else if (param.asInteger() == 0x030B4480)
+ playSound(5);
+ else if (param.asInteger() == 0x422B0280)
+ playSound(6);
+ else if (param.asInteger() == 0x038A010B)
+ playSound(7);
+ else if (param.asInteger() == 0x67221A03)
+ playSound(0, 0x44051000);
+ else if (param.asInteger() == 0x02B20220)
+ playSound(0, 0xC5408620);
+ else if (param.asInteger() == 0x925A0C1E)
+ playSound(0, 0x40E5884D);
+ else if (param.asInteger() == 0x03020231)
+ playSound(0, 0xD4C08010);
+ else if (param.asInteger() == 0x08040840)
+ setDoDeltaX(2);
+ break;
+ }
+ return messageResult;
+}
+
+void Klaymen::stGrow() {
+ _busyStatus = 0;
+ _acceptInput = false;
+ startAnimation(0x2838C010, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmGrow);
+ SetSpriteUpdate(&AnimatedSprite::updateDeltaXY);
+}
+
+void Klaymen::stDrinkPotion() {
+ _busyStatus = 1;
+ _acceptInput = false;
+ _potionFlag1 = false;
+ _potionFlag2 = false;
+ startAnimation(0x1C388C04, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmDrinkPotion);
+ SetSpriteUpdate(&AnimatedSprite::updateDeltaXY);
+}
+
+uint32 Klaymen::hmInsertKey(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = Klaymen::hmLowLevelAnimation(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x100D:
+ if (_keysToInsert == 0 && param.asInteger() == 0x06040580) {
+ nextAnimationByHash(0xDC409440, 0x46431401, 0);
+ } else if (_keysToInsert != 0 && param.asInteger() == 0x46431401) {
+ _keysToInsert--;
+ startAnimationByHash(0xDC409440, 0x01084280, 0);
+ } else if (param.asInteger() == 0x062A1510) {
+ playSound(0, 0x41688704);
+ } else if (param.asInteger() == 0x02B20220) {
+ playSound(0, 0xC5408620);
+ } else if (param.asInteger() == 0x0A720138) {
+ playSound(0, 0xD4C08010);
+ } else if (param.asInteger() == 0xB613A180) {
+ playSound(0, 0x44051000);
+ } else if (param.asInteger() == 0x0E4C8141) {
+ playSound(0, 0xDC4A1280);
+ }
+ break;
+ }
+ return messageResult;
+}
+
+void Klaymen::stInsertKey() {
+ if (!stStartActionFromIdle(AnimationCallback(&Klaymen::stInsertKey))) {
+ _busyStatus = 2;
+ _keysToInsert = 0;
+ for (uint32 i = 0; i < 3; i++) {
+ if (getSubVar(VA_HAS_KEY, i)) {
+ bool more;
+ setSubVar(VA_IS_KEY_INSERTED, i, 1);
+ setSubVar(VA_HAS_KEY, i, 0);
+ do {
+ more = false;
+ setSubVar(VA_CURR_KEY_SLOT_NUMBERS, i, _vm->_rnd->getRandomNumber(16 - 1));
+ for (uint j = 0; j < i && !more; j++) {
+ if (getSubVar(VA_IS_KEY_INSERTED, j) && getSubVar(VA_CURR_KEY_SLOT_NUMBERS, j) == getSubVar(VA_CURR_KEY_SLOT_NUMBERS, i))
+ more = true;
+ }
+ if (getSubVar(VA_CURR_KEY_SLOT_NUMBERS, i) == getSubVar(VA_GOOD_KEY_SLOT_NUMBERS, i))
+ more = true;
+ } while (more);
+ _keysToInsert++;
+ }
+ }
+ if (_keysToInsert == 0) {
+ GotoState(NULL);
+ gotoNextStateExt();
+ } else {
+ _acceptInput = false;
+ startAnimation(0xDC409440, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmInsertKey);
+ SetSpriteUpdate(&Klaymen::suAction);
+ _keysToInsert--;
+ }
+ }
+}
+
+uint32 Klaymen::hmReadNote(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = hmLowLevelAnimation(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x100D:
+ if (param.asInteger() == 0x04684052) {
+ _acceptInput = true;
+ sendMessage(_parentScene, 0x2002, 0);
+ }
+ break;
+ }
+ return messageResult;
+}
+
+void Klaymen::stReadNote() {
+ _busyStatus = 2;
+ _acceptInput = false;
+ startAnimation(0x123E9C9F, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmReadNote);
+ SetSpriteUpdate(&AnimatedSprite::updateDeltaXY);
+}
+
+uint32 Klaymen::hmHitByDoor(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = hmLowLevelAnimation(messageNum, param, sender);
+ int16 speedUpFrameIndex;
+ switch (messageNum) {
+ case 0x1008:
+ speedUpFrameIndex = getFrameIndex(kKlaymenSpeedUpHash);
+ if (_currFrameIndex < speedUpFrameIndex) {
+ startAnimation(0x35AA8059, speedUpFrameIndex, -1);
+ _y = 438;
+ }
+ messageResult = 0;
+ break;
+ case 0x100D:
+ if (param.asInteger() == 0x1A1A0785) {
+ playSound(0, 0x40F0A342);
+ } else if (param.asInteger() == 0x60428026) {
+ playSound(0, 0x40608A59);
+ }
+ break;
+ }
+ return messageResult;
+}
+
+void Klaymen::stHitByDoor() {
+ _busyStatus = 1;
+ _acceptInput = false;
+ startAnimation(0x35AA8059, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmHitByDoor);
+ SetSpriteUpdate(&AnimatedSprite::updateDeltaXY);
+ playSound(0, 0x402E82D4);
+}
+
+uint32 Klaymen::hmPeekWallReturn(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = hmLowLevelAnimation(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x100D:
+ if (param.asInteger() == calcHash("PopBalloon")) {
+ sendMessage(_parentScene, 0x2000, 0);
+ } else if (param.asInteger() == 0x02B20220) {
+ playSound(0, 0xC5408620);
+ } else if (param.asInteger() == 0x0A720138) {
+ playSound(0, 0xD4C08010);
+ } else if (param.asInteger() == 0xB613A180) {
+ playSound(0, 0x44051000);
+ }
+ break;
+ }
+ return messageResult;
+}
+
+void Klaymen::upPeekWallBlink() {
+ Klaymen::update();
+ _blinkCounter++;
+ if (_blinkCounter >= _blinkCounterMax)
+ stPeekWallBlink();
+}
+
+void Klaymen::stPeekWall1() {
+ _busyStatus = 0;
+ _acceptInput = true;
+ startAnimation(0xAC20C012, 8, 37);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmLowLevelAnimation);
+ SetSpriteUpdate(NULL);
+ NextState(&Klaymen::stPeekWallBlink);
+}
+
+void Klaymen::stPeekWall2() {
+ _busyStatus = 1;
+ _acceptInput = false;
+ startAnimation(0xAC20C012, 43, 49);
+ SetUpdateHandler(&Klaymen::update);
+ SetSpriteUpdate(NULL);
+ SetMessageHandler(&Klaymen::hmLowLevelAnimation);
+}
+
+void Klaymen::stPeekWallBlink() {
+ _blinkCounter = 0;
+ _busyStatus = 0;
+ _acceptInput = true;
+ _blinkCounterMax = _vm->_rnd->getRandomNumber(64) + 24;
+ startAnimation(0xAC20C012, 38, 42);
+ SetUpdateHandler(&Klaymen::upPeekWallBlink);
+ SetSpriteUpdate(NULL);
+ SetMessageHandler(&Klaymen::hmLowLevel);
+ _newStickFrameIndex = 42;
+}
+
+void Klaymen::stPeekWallReturn() {
+ _busyStatus = 0;
+ _acceptInput = false;
+ startAnimation(0x2426932E, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmPeekWallReturn);
+ SetSpriteUpdate(NULL);
+}
+
+void Klaymen::stPullHammerLever() {
+ if (!stStartAction(AnimationCallback(&Klaymen::stPullHammerLever))) {
+ _busyStatus = 2;
+ _acceptInput = false;
+ startAnimation(0x00648953, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmPullHammerLever);
+ SetSpriteUpdate(&AnimatedSprite::updateDeltaXY);
+ }
+}
+
+uint32 Klaymen::hmPullHammerLever(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = Klaymen::hmLever(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x100D:
+ if (param.asInteger() == 0x4AB28209)
+ sendMessage(_attachedSprite, 0x480F, 0);
+ break;
+ }
+ return messageResult;
+}
+
+void Klaymen::suRidePlatformDown() {
+ _platformDeltaY++;
+ _y += _platformDeltaY;
+ if (_y > 600)
+ sendMessage(this, 0x1019, 0);
+}
+
+void Klaymen::stRidePlatformDown() {
+ if (!stStartActionFromIdle(AnimationCallback(&Klaymen::stRidePlatformDown))) {
+ _busyStatus = 1;
+ sendMessage(_parentScene, 0x4803, 0);
+ _acceptInput = false;
+ _platformDeltaY = 0;
+ startAnimation(0x5420E254, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmLowLevel);
+ SetSpriteUpdate(&Klaymen::suRidePlatformDown);
+ _vm->_soundMan->playSoundLooping(0xD3B02847);
+ }
+}
+
+void Klaymen::stCrashDown() {
+ playSound(0, 0x41648271);
+ _busyStatus = 1;
+ _acceptInput = false;
+ startAnimationByHash(0x000BAB02, 0x88003000, 0);
+ SetUpdateHandler(&Klaymen::update);
+ SetSpriteUpdate(NULL);
+ SetMessageHandler(&Klaymen::hmLowLevelAnimation);
+ NextState(&Klaymen::stCrashDownFinished);
+}
+
+void Klaymen::stCrashDownFinished() {
+ setDoDeltaX(2);
+ stTryStandIdle();
+}
+
+void Klaymen::upSpitOutFall() {
+ Klaymen::update();
+ if (_spitOutCountdown != 0 && (--_spitOutCountdown == 0)) {
+ _surface->setVisible(true);
+ SetUpdateHandler(&Klaymen::update);
+ }
+}
+
+uint32 Klaymen::hmJumpToRingVenusFlyTrap(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = hmLowLevelAnimation(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x100D:
+ if (param.asInteger() == 0x168050A0) {
+ sendMessage(_attachedSprite, 0x480F, 0);
+ } else if (param.asInteger() == 0x586B0300) {
+ sendMessage(_otherSprite, 0x480E, 1);
+ } else if (param.asInteger() == 0x4AB28209) {
+ sendMessage(_attachedSprite, 0x482A, 0);
+ } else if (param.asInteger() == 0x88001184) {
+ sendMessage(_attachedSprite, 0x482B, 0);
+ }
+ break;
+ }
+ return messageResult;
+}
+
+uint32 Klaymen::hmStandIdleSpecial(int messageNum, const MessageParam &param, Entity *sender) {
+ switch (messageNum) {
+ case 0x4811:
+ playSound(0, 0x5252A0E4);
+ setDoDeltaX(((Sprite*)sender)->isDoDeltaX() ? 1 : 0);
+ if (_doDeltaX) {
+ _x = ((Sprite*)sender)->getX() - 75;
+ } else {
+ _x = ((Sprite*)sender)->getX() + 75;
+ }
+ _y = ((Sprite*)sender)->getY() - 200;
+ if (param.asInteger() == 0) {
+ stSpitOutFall0();
+ } else if (param.asInteger() == 1) {
+ // NOTE This is never used and the code was removed
+ // Also the animations used here in the original don't exist...
+ } else if (param.asInteger() == 2) {
+ stSpitOutFall2();
+ }
+ break;
+ }
+ return 0;
+}
+
+uint32 Klaymen::hmPressDoorButton(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = hmLowLevelAnimation(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x100D:
+ if (param.asInteger() == 0x942D2081) {
+ _acceptInput = false;
+ sendMessage(_attachedSprite, 0x2003, 0);
+ } else if (param.asInteger() == 0xDA600012) {
+ stHitByBoxingGlove();
+ } else if (param.asInteger() == 0x0D01B294) {
+ _acceptInput = false;
+ sendMessage(_attachedSprite, 0x480B, 0);
+ }
+ break;
+ }
+ return messageResult;
+}
+
+uint32 Klaymen::hmMoveVenusFlyTrap(int messageNum, const MessageParam &param, Entity *sender) {
+ switch (messageNum) {
+ case 0x100D:
+ if (param.asInteger() == 0x01084280) {
+ sendMessage(_attachedSprite, 0x480B, (uint32)_doDeltaX);
+ } else if (param.asInteger() == 0x02421405) {
+ if (_isMoveObjectRequested) {
+ if (sendMessage(_attachedSprite, 0x480C, (uint32)_doDeltaX) != 0)
+ stContinueMovingVenusFlyTrap();
+ } else {
+ SetMessageHandler(&Klaymen::hmFirstMoveVenusFlyTrap);
+ }
+ } else if (param.asInteger() == 0x4AB28209) {
+ sendMessage(_attachedSprite, 0x482A, 0);
+ } else if (param.asInteger() == 0x88001184) {
+ sendMessage(_attachedSprite, 0x482B, 0);
+ } else if (param.asInteger() == 0x32180101) {
+ playSound(0, 0x405002D8);
+ } else if (param.asInteger() == 0x0A2A9098) {
+ playSound(0, 0x0460E2FA);
+ }
+ break;
+ case 0x480A:
+ _isMoveObjectRequested = true;
+ return 0;
+ }
+ return hmLowLevelAnimation(messageNum, param, sender);
+}
+
+uint32 Klaymen::hmFirstMoveVenusFlyTrap(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = hmLowLevelAnimation(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x100D:
+ if (param.asInteger() == 0x4AB28209) {
+ sendMessage(_attachedSprite, 0x482A, 0);
+ } else if (param.asInteger() == 0x88001184) {
+ sendMessage(_attachedSprite, 0x482B, 0);
+ } else if (param.asInteger() == 0x32180101) {
+ playSound(0, 0x405002D8);
+ } else if (param.asInteger() == 0x0A2A9098) {
+ playSound(0, 0x0460E2FA);
+ }
+ break;
+ }
+ return messageResult;
+}
+
+uint32 Klaymen::hmHitByBoxingGlove(int messageNum, const MessageParam &param, Entity *sender) {
+ int16 speedUpFrameIndex;
+ uint32 messageResult = hmLowLevelAnimation(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x1008:
+ speedUpFrameIndex = getFrameIndex(kKlaymenSpeedUpHash);
+ if (_currFrameIndex < speedUpFrameIndex) {
+ startAnimation(0x35AA8059, speedUpFrameIndex, -1);
+ _y = 435;
+ }
+ messageResult = 0;
+ break;
+ case 0x100D:
+ if (param.asInteger() == 0x1A1A0785) {
+ playSound(0, 0x40F0A342);
+ } else if (param.asInteger() == 0x60428026) {
+ playSound(0, 0x40608A59);
+ }
+ break;
+ }
+ return messageResult;
+}
+
+uint32 Klaymen::hmJumpAndFall(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = hmLowLevel(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x100D:
+ if (param.asInteger() == 0x1307050A) {
+ playSound(0, 0x40428A09);
+ }
+ break;
+ }
+ return messageResult;
+}
+
+void Klaymen::suFallDown() {
+ AnimatedSprite::updateDeltaXY();
+ HitRect *hitRect = _parentScene->findHitRectAtPos(_x, _y + 10);
+ if (hitRect->type == 0x5001) {
+ _y = hitRect->rect.y1;
+ updateBounds();
+ sendMessage(this, 0x1019, 0);
+ }
+ _parentScene->checkCollision(this, 0xFFFF, 0x4810, 0);
+}
+
+void Klaymen::stJumpToRingVenusFlyTrap() {
+ if (!stStartAction(AnimationCallback(&Klaymen::stJumpToRingVenusFlyTrap))) {
+ _busyStatus = 2;
+ _acceptInput = false;
+ startAnimation(0x584984B4, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmJumpToRingVenusFlyTrap);
+ SetSpriteUpdate(&AnimatedSprite::updateDeltaXY);
+ NextState(&Klaymen::stLandOnFeet);
+ sendMessage(_attachedSprite, 0x482B, 0);
+ }
+}
+
+void Klaymen::stStandIdleSpecial() {
+ playSound(0, 0x56548280);
+ _busyStatus = 0;
+ _acceptInput = false;
+ _surface->setVisible(false);
+ startAnimation(0x5420E254, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmStandIdleSpecial);
+ SetSpriteUpdate(NULL);
+}
+
+void Klaymen::stSpitOutFall0() {
+ _spitOutCountdown = 1;
+ _busyStatus = 0;
+ _acceptInput = false;
+ startAnimation(0x000BAB02, 0, -1);
+ SetUpdateHandler(&Klaymen::upSpitOutFall);
+ SetMessageHandler(&Klaymen::hmLowLevel);
+ SetSpriteUpdate(&Klaymen::suFallDown);
+ NextState(&Klaymen::stFalling);
+ sendMessage(_parentScene, 0x8000, 0);
+}
+
+void Klaymen::stSpitOutFall2() {
+ _spitOutCountdown = 1;
+ _busyStatus = 0;
+ _acceptInput = false;
+ startAnimation(0x9308C132, 0, -1);
+ SetUpdateHandler(&Klaymen::upSpitOutFall);
+ SetMessageHandler(&Klaymen::hmLowLevelAnimation);
+ SetSpriteUpdate(&Klaymen::suFallDown);
+ NextState(&Klaymen::stFalling);
+ sendMessage(_parentScene, 0x8000, 0);
+}
+
+void Klaymen::stFalling() {
+ sendMessage(_parentScene, 0x1024, 1);
+ playSound(0, 0x41648271);
+ _busyStatus = 1;
+ _acceptInput = false;
+ _isWalking = false;
+ startAnimationByHash(0x000BAB02, 0x88003000, 0);
+ SetUpdateHandler(&Klaymen::update);
+ SetSpriteUpdate(NULL);
+ SetMessageHandler(&Klaymen::hmLowLevelAnimation);
+ NextState(&Klaymen::stFallTouchdown);
+ sendMessage(_parentScene, 0x2002, 0);
+ _attachedSprite = NULL;
+ sendMessage(_parentScene, 0x8001, 0);
+}
+
+void Klaymen::stFallTouchdown() {
+ setDoDeltaX(2);
+ stTryStandIdle();
+}
+
+void Klaymen::stJumpAndFall() {
+ if (!stStartAction(AnimationCallback(&Klaymen::stJumpAndFall))) {
+ sendMessage(_parentScene, 0x1024, 3);
+ _busyStatus = 2;
+ _acceptInput = false;
+ startAnimation(0xB93AB151, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmJumpAndFall);
+ SetSpriteUpdate(&Klaymen::suFallDown);
+ NextState(&Klaymen::stLandOnFeet);
+ }
+}
+
+void Klaymen::stDropFromRing() {
+ if (_attachedSprite) {
+ _x = _attachedSprite->getX();
+ sendMessage(_attachedSprite, 0x4807, 0);
+ _attachedSprite = NULL;
+ }
+ _busyStatus = 2;
+ _acceptInput = false;
+ startAnimation(0x586984B1, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmLowLevel);
+ SetSpriteUpdate(&Klaymen::suFallDown);
+ NextState(&Klaymen::stLandOnFeet);
+}
+
+void Klaymen::stPressDoorButton() {
+ _busyStatus = 2;
+ _acceptInput = true;
+ setDoDeltaX(0);
+ startAnimation(0x1CD89029, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmPressDoorButton);
+ SetSpriteUpdate(&Klaymen::suAction);
+}
+
+void Klaymen::stHitByBoxingGlove() {
+ _busyStatus = 1;
+ _acceptInput = false;
+ startAnimation(0x35AA8059, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmHitByBoxingGlove);
+ SetSpriteUpdate(&AnimatedSprite::updateDeltaXY);
+ FinalizeState(&Klaymen::evHitByBoxingGloveDone);
+}
+
+void Klaymen::evHitByBoxingGloveDone() {
+ sendMessage(_parentScene, 0x1024, 1);
+}
+
+void Klaymen::stMoveVenusFlyTrap() {
+ if (!stStartAction(AnimationCallback(&Klaymen::stMoveVenusFlyTrap))) {
+ _busyStatus = 2;
+ _isMoveObjectRequested = false;
+ _acceptInput = true;
+ setDoDeltaX(_attachedSprite->getX() < _x ? 1 : 0);
+ startAnimation(0x5C01A870, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmMoveVenusFlyTrap);
+ SetSpriteUpdate(&AnimatedSprite::updateDeltaXY);
+ FinalizeState(&Klaymen::evMoveVenusFlyTrapDone);
+ }
+}
+
+void Klaymen::stContinueMovingVenusFlyTrap() {
+ _isMoveObjectRequested = false;
+ _acceptInput = true;
+ startAnimationByHash(0x5C01A870, 0x01084280, 0);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmMoveVenusFlyTrap);
+ SetSpriteUpdate(&AnimatedSprite::updateDeltaXY);
+ FinalizeState(&Klaymen::evMoveVenusFlyTrapDone);
+}
+
+void Klaymen::evMoveVenusFlyTrapDone() {
+ sendMessage(_attachedSprite, 0x482A, 0);
+}
+
+void Klaymen::suFallSkipJump() {
+ updateDeltaXY();
+ HitRect *hitRect = _parentScene->findHitRectAtPos(_x, _y + 10);
+ if (hitRect->type == 0x5001) {
+ _y = hitRect->rect.y1;
+ updateBounds();
+ sendMessage(this, 0x1019, 0);
+ }
+}
+
+void Klaymen::stFallSkipJump() {
+ _busyStatus = 2;
+ _acceptInput = false;
+ startAnimationByHash(0xB93AB151, 0x40A100F8, 0);
+ SetUpdateHandler(&Klaymen::update);
+ SetSpriteUpdate(&Klaymen::suFallSkipJump);
+ SetMessageHandler(&Klaymen::hmLowLevel);
+ NextState(&Klaymen::stLandOnFeet);
+}
+
+void Klaymen::upMoveObject() {
+ if (_x >= 380)
+ gotoNextStateExt();
+ Klaymen::update();
+}
+
+uint32 Klaymen::hmMatch(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = Klaymen::hmLowLevelAnimation(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x100D:
+ if (param.asInteger() == 0x51281850) {
+ setGlobalVar(V_TNT_DUMMY_FUSE_LIT, 1);
+ } else if (param.asInteger() == 0x43000538) {
+ playSound(0, 0x21043059);
+ } else if (param.asInteger() == 0x02B20220) {
+ playSound(0, 0xC5408620);
+ } else if (param.asInteger() == 0x0A720138) {
+ playSound(0, 0xD4C08010);
+ } else if (param.asInteger() == 0xB613A180) {
+ playSound(0, 0x44051000);
+ }
+ break;
+ }
+ return messageResult;
+}
+
+void Klaymen::stFetchMatch() {
+ if (!stStartAction(AnimationCallback(&Klaymen::stFetchMatch))) {
+ _busyStatus = 0;
+ _acceptInput = false;
+ setDoDeltaX(_attachedSprite->getX() < _x ? 1 : 0);
+ startAnimation(0x9CAA0218, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmMatch);
+ SetSpriteUpdate(NULL);
+ NextState(&Klaymen::stLightMatch);
+ }
+}
+
+void Klaymen::stLightMatch() {
+ _busyStatus = 1;
+ _acceptInput = false;
+ setDoDeltaX(_attachedSprite->getX() < _x ? 1 : 0);
+ startAnimation(0x1222A513, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmMatch);
+ SetSpriteUpdate(NULL);
+}
+
+uint32 Klaymen::hmMoveObject(int messageNum, const MessageParam &param, Entity *sender) {
+ switch (messageNum) {
+ case 0x100D:
+ if (param.asInteger() == 0x01084280) {
+ playSound(0, 0x405002D8);
+ sendMessage(_attachedSprite, 0x480B, 0);
+ } else if (param.asInteger() == 0x02421405) {
+ if (_moveObjectCountdown != 0) {
+ _moveObjectCountdown--;
+ stContinueMoveObject();
+ } else {
+ SetMessageHandler(&Klaymen::hmLowLevelAnimation);
+ }
+ }
+ break;
+ }
+ return Klaymen::hmLowLevelAnimation(messageNum, param, sender);
+}
+
+uint32 Klaymen::hmTumbleHeadless(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = Klaymen::hmLowLevelAnimation(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x100D:
+ if (param.asInteger() == 0x000F0082) {
+ playSound(0, 0x74E2810F);
+ }
+ break;
+ }
+ return messageResult;
+}
+
+void Klaymen::stMoveObject() {
+ if (!stStartAction(AnimationCallback(&Klaymen::stMoveObject))) {
+ _busyStatus = 2;
+ _acceptInput = false;
+ _moveObjectCountdown = 8;
+ setDoDeltaX(0);
+ startAnimation(0x0C1CA072, 0, -1);
+ SetUpdateHandler(&Klaymen::upMoveObject);
+ SetMessageHandler(&Klaymen::hmMoveObject);
+ SetSpriteUpdate(&AnimatedSprite::updateDeltaXY);
+ }
+}
+
+void Klaymen::stContinueMoveObject() {
+ _acceptInput = false;
+ startAnimationByHash(0x0C1CA072, 0x01084280, 0);
+ SetUpdateHandler(&Klaymen::upMoveObject);
+ SetSpriteUpdate(&AnimatedSprite::updateDeltaXY);
+ SetMessageHandler(&Klaymen::hmMoveObject);
+}
+
+void Klaymen::stTumbleHeadless() {
+ if (!stStartActionFromIdle(AnimationCallback(&Klaymen::stTumbleHeadless))) {
+ _busyStatus = 1;
+ _acceptInput = false;
+ setDoDeltaX(0);
+ startAnimation(0x2821C590, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmTumbleHeadless);
+ SetSpriteUpdate(&AnimatedSprite::updateDeltaXY);
+ NextState(&Klaymen::stTryStandIdle);
+ sendMessage(_parentScene, 0x8000, 0);
+ playSound(0, 0x62E0A356);
+ }
+}
+
+void Klaymen::stCloseEyes() {
+ if (!stStartActionFromIdle(AnimationCallback(&Klaymen::stCloseEyes))) {
+ _busyStatus = 1;
+ _acceptInput = false;
+ startAnimation(0x5420E254, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmLowLevel);
+ SetSpriteUpdate(NULL);
+ }
+}
+
+uint32 Klaymen::hmSpit(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = Klaymen::hmLowLevelAnimation(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x100D:
+ if (param.asInteger() == 0x16401CA6) {
+ _canSpitPipe = true;
+ if (_contSpitPipe)
+ spitIntoPipe();
+ } else if (param.asInteger() == 0xC11C0008) {
+ _canSpitPipe = false;
+ _acceptInput = false;
+ _readyToSpit = false;
+ } else if (param.asInteger() == 0x018A0001) {
+ sendMessage(_parentScene, 0x2001, _spitDestPipeIndex);
+ }
+ break;
+ }
+ return messageResult;
+}
+
+void Klaymen::stTrySpitIntoPipe() {
+ if (_readyToSpit) {
+ _contSpitPipe = true;
+ _spitContDestPipeIndex = _spitPipeIndex;
+ if (_canSpitPipe)
+ spitIntoPipe();
+ } else if (!stStartAction(AnimationCallback(&Klaymen::stTrySpitIntoPipe))) {
+ _busyStatus = 2;
+ _acceptInput = true;
+ _spitDestPipeIndex = _spitPipeIndex;
+ _readyToSpit = true;
+ _canSpitPipe = false;
+ _contSpitPipe = false;
+ startAnimation(0x1808B150, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmSpit);
+ SetSpriteUpdate(NULL);
+ }
+}
+
+void Klaymen::spitIntoPipe() {
+ _contSpitPipe = false;
+ _spitDestPipeIndex = _spitContDestPipeIndex;
+ _canSpitPipe = false;
+ _acceptInput = false;
+ startAnimation(0x1B08B553, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmSpit);
+ SetSpriteUpdate(NULL);
+ NextState(&Klaymen::stContSpitIntoPipe);
+}
+
+void Klaymen::stContSpitIntoPipe() {
+ _canSpitPipe = true;
+ _acceptInput = true;
+ startAnimationByHash(0x1808B150, 0x16401CA6, 0);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmSpit);
+ SetSpriteUpdate(NULL);
+}
+
+void Klaymen::suRidePlatform() {
+ _x = _attachedSprite->getX() - 20;
+ _y = _attachedSprite->getY() + 46;
+ updateBounds();
+}
+
+void Klaymen::stRidePlatform() {
+ if (!stStartActionFromIdle(AnimationCallback(&Klaymen::stRidePlatform))) {
+ _busyStatus = 1;
+ _acceptInput = true;
+ startAnimation(0x5420E254, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmLowLevel);
+ SetSpriteUpdate(&Klaymen::suRidePlatform);
+ }
+}
+
+void Klaymen::stInteractLever() {
+ if (!stStartAction(AnimationCallback(&Klaymen::stInteractLever))) {
+ _busyStatus = 0;
+ if (_isLeverDown) {
+ stUseLeverRelease();
+ } else {
+ _acceptInput = false;
+ startAnimation(0x0C303040, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmLever);
+ SetSpriteUpdate(&Klaymen::suUpdateDestX);
+ NextState(&Klaymen::stPullLever);
+ }
+ }
+}
+
+void Klaymen::stPullLever() {
+ startAnimation(0x0D318140, 0, -1);
+ NextState(&Klaymen::stLookLeverDown);
+ sendMessage(_attachedSprite, 0x480F, 0);
+}
+
+void Klaymen::stLookLeverDown() {
+ _acceptInput = true;
+ _isLeverDown = true;
+ startAnimation(0x1564A2C0, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetSpriteUpdate(&Klaymen::suUpdateDestX);
+ NextState(&Klaymen::stWaitLeverDown);
+}
+
+void Klaymen::stWaitLeverDown() {
+ _acceptInput = true;
+ _isLeverDown = true;
+ startAnimation(0x4464A440, 0, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmLowLevel);
+ SetSpriteUpdate(&Klaymen::suUpdateDestX);
+}
+
+void Klaymen::stStartWalkingResume() {
+ int16 frameIndex = getGlobalVar(V_KLAYMEN_FRAMEINDEX) + _walkResumeFrameIncr;
+ if (frameIndex < 0 || frameIndex > 13)
+ frameIndex = 0;
+ _busyStatus = 0;
+ _isWalking = true;
+ _acceptInput = true;
+ startAnimation(0x1A249001, frameIndex, -1);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmWalking);
+ SetSpriteUpdate(&Klaymen::suWalkingFirst);
+ NextState(&Klaymen::stUpdateWalkingFirst);
+ FinalizeState(&Klaymen::evStartWalkingDone);
+}
+
+void Klaymen::upPeekInsideBlink() {
+ update();
+ ++_blinkCounter;
+ if (_blinkCounter >= _blinkCounterMax)
+ stPeekInsideBlink();
+}
+
+void Klaymen::stPeekInside() {
+ _busyStatus = 0;
+ _acceptInput = true;
+ startAnimation(0xAC20C012, 8, 37);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmLowLevelAnimation);
+ SetSpriteUpdate(NULL);
+ NextState(&Klaymen::stPeekInsideBlink);
+}
+
+void Klaymen::stPeekInsideReturn() {
+ _busyStatus = 1;
+ _acceptInput = false;
+ startAnimation(0xAC20C012, 43, 49);
+ SetUpdateHandler(&Klaymen::update);
+ SetMessageHandler(&Klaymen::hmLowLevelAnimation);
+ SetSpriteUpdate(NULL);
+}
+
+void Klaymen::stPeekInsideBlink() {
+ _busyStatus = 0;
+ _acceptInput = true;
+ startAnimation(0xAC20C012, 38, 42);
+ _newStickFrameIndex = 42;
+ SetUpdateHandler(&Klaymen::upPeekInsideBlink);
+ SetMessageHandler(&Klaymen::hmLowLevel);
+ SetSpriteUpdate(NULL);
+ _blinkCounter = 0;
+ _blinkCounterMax = _vm->_rnd->getRandomNumber(64 - 1) + 24;
+}
+
+// KmScene1001
+
+KmScene1001::KmScene1001(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y)
+ : Klaymen(vm, parentScene, x, y) {
+}
+
+uint32 KmScene1001::xHandleMessage(int messageNum, const MessageParam &param) {
+ switch (messageNum) {
+ case 0x4001:
+ case 0x4800:
+ startWalkToX(param.asPoint().x, false);
+ break;
+ case 0x4004:
+ GotoState(&Klaymen::stTryStandIdle);
+ break;
+ case 0x4804:
+ if (param.asInteger() == 2)
+ GotoState(&Klaymen::stSleeping);
+ break;
+ case 0x480D:
+ GotoState(&Klaymen::stPullHammerLever);
+ break;
+ case 0x4812:
+ GotoState(&Klaymen::stPickUpGeneric);
+ break;
+ case 0x4816:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stPressButton);
+ else if (param.asInteger() == 2)
+ GotoState(&Klaymen::stPressFloorButton);
+ else
+ GotoState(&Klaymen::stPressButtonSide);
+ break;
+ case 0x4817:
+ setDoDeltaX(param.asInteger());
+ gotoNextStateExt();
+ break;
+ case 0x481B:
+ if (param.asPoint().y != 0)
+ startWalkToXDistance(param.asPoint().y, param.asPoint().x);
+ else
+ startWalkToAttachedSpriteXDistance(param.asPoint().x);
+ break;
+ case 0x481F:
+ if (param.asInteger() == 0)
+ GotoState(&Klaymen::stWonderAboutHalf);
+ else if (param.asInteger() == 1)
+ GotoState(&Klaymen::stWonderAboutAfter);
+ else if (param.asInteger() == 3)
+ GotoState(&Klaymen::stTurnToUseHalf);
+ else if (param.asInteger() == 4)
+ GotoState(&Klaymen::stTurnAwayFromUse);
+ else
+ GotoState(&Klaymen::stWonderAbout);
+ break;
+ case 0x482D:
+ setDoDeltaX(_x > (int16)param.asInteger() ? 1 : 0);
+ gotoNextStateExt();
+ break;
+ case 0x4836:
+ if (param.asInteger() == 1) {
+ sendMessage(_parentScene, 0x2002, 0);
+ GotoState(&Klaymen::stWakeUp);
+ }
+ break;
+ case 0x483F:
+ startSpecialWalkRight(param.asInteger());
+ break;
+ case 0x4840:
+ startSpecialWalkLeft(param.asInteger());
+ break;
+ }
+ return 0;
+}
+
+// KmScene1002
+
+KmScene1002::KmScene1002(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y)
+ : Klaymen(vm, parentScene, x, y) {
+
+ setKlaymenIdleTable1();
+}
+
+void KmScene1002::xUpdate() {
+ if (_x >= 250 && _x <= 435 && _y >= 420) {
+ if (_idleTableNum == 0) {
+ setKlaymenIdleTable(klaymenIdleTable1002, ARRAYSIZE(klaymenIdleTable1002));
+ _idleTableNum = 1;
+ }
+ } else if (_idleTableNum == 1) {
+ setKlaymenIdleTable1();
+ _idleTableNum = 0;
+ }
+}
+
+uint32 KmScene1002::xHandleMessage(int messageNum, const MessageParam &param) {
+ switch (messageNum) {
+ case 0x2001:
+ GotoState(&Klaymen::stStandIdleSpecial);
+ break;
+ case 0x2007:
+ _otherSprite = (Sprite*)param.asEntity();
+ break;
+ case 0x4001:
+ case 0x4800:
+ startWalkToX(param.asPoint().x, false);
+ break;
+ case 0x4004:
+ GotoState(&Klaymen::stTryStandIdle);
+ break;
+ case 0x4803:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stJumpAndFall);
+ else if (param.asInteger() == 2)
+ GotoState(&Klaymen::stDropFromRing);
+ break;
+ case 0x4804:
+ GotoState(&Klaymen::stPeekWall);
+ break;
+ case 0x4805:
+ switch (param.asInteger()) {
+ case 1:
+ GotoState(&Klaymen::stJumpToRing1);
+ break;
+ case 2:
+ GotoState(&Klaymen::stJumpToRing2);
+ break;
+ case 3:
+ GotoState(&Klaymen::stJumpToRing3);
+ break;
+ case 4:
+ GotoState(&Klaymen::stJumpToRing4);
+ break;
+ }
+ break;
+ case 0x480A:
+ GotoState(&Klaymen::stMoveVenusFlyTrap);
+ break;
+ case 0x480D:
+ GotoState(&Klaymen::stJumpToRingVenusFlyTrap);
+ break;
+ case 0x4816:
+ if (param.asInteger() == 0)
+ GotoState(&Klaymen::stPressDoorButton);
+ break;
+ case 0x4817:
+ setDoDeltaX(param.asInteger());
+ gotoNextStateExt();
+ break;
+ case 0x481B:
+ startWalkToAttachedSpriteXDistance(param.asInteger());
+ break;
+ case 0x4820:
+ sendMessage(_parentScene, 0x2005, 0);
+ GotoState(&Klaymen::stContinueClimbLadderUp);
+ break;
+ case 0x4821:
+ sendMessage(_parentScene, 0x2005, 0);
+ _destY = param.asInteger();
+ GotoState(&Klaymen::stStartClimbLadderDown);
+ break;
+ case 0x4822:
+ sendMessage(_parentScene, 0x2005, 0);
+ _destY = param.asInteger();
+ GotoState(&Klaymen::stStartClimbLadderUp);
+ break;
+ case 0x4823:
+ sendMessage(_parentScene, 0x2006, 0);
+ GotoState(&Klaymen::stClimbLadderHalf);
+ break;
+ case 0x482E:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stWalkToFrontNoStep);
+ else
+ GotoState(&Klaymen::stWalkToFront);
+ break;
+ case 0x482F:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stTurnToFront);
+ else
+ GotoState(&Klaymen::stTurnToBack);
+ break;
+ case 0x483F:
+ startSpecialWalkRight(param.asInteger());
+ break;
+ case 0x4840:
+ startSpecialWalkLeft(param.asInteger());
+ break;
+ }
+ return 0;
+}
+
+// KmScene1004
+
+KmScene1004::KmScene1004(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y)
+ : Klaymen(vm, parentScene, x, y) {
+
+ _dataResource.load(0x01900A04);
+}
+
+uint32 KmScene1004::xHandleMessage(int messageNum, const MessageParam &param) {
+ switch (messageNum) {
+ case 0x4001:
+ case 0x4800:
+ startWalkToX(param.asPoint().x, false);
+ break;
+ case 0x4004:
+ GotoState(&Klaymen::stTryStandIdle);
+ break;
+ case 0x4817:
+ setDoDeltaX(param.asInteger());
+ gotoNextStateExt();
+ break;
+ case 0x4818:
+ startWalkToX(_dataResource.getPoint(param.asInteger()).x, false);
+ break;
+ case 0x481E:
+ GotoState(&Klaymen::stReadNote);
+ break;
+ case 0x4820:
+ sendMessage(_parentScene, 0x2000, 0);
+ GotoState(&Klaymen::stContinueClimbLadderUp);
+ break;
+ case 0x4821:
+ sendMessage(_parentScene, 0x2000, 0);
+ _destY = param.asInteger();
+ GotoState(&Klaymen::stStartClimbLadderDown);
+ break;
+ case 0x4822:
+ sendMessage(_parentScene, 0x2000, 0);
+ _destY = param.asInteger();
+ GotoState(&Klaymen::stStartClimbLadderUp);
+ break;
+ case 0x4823:
+ sendMessage(_parentScene, 0x2001, 0);
+ GotoState(&Klaymen::stClimbLadderHalf);
+ break;
+ case 0x4824:
+ sendMessage(_parentScene, 0x2000, 0);
+ _destY = _dataResource.getPoint(param.asInteger()).y;
+ GotoState(&Klaymen::stStartClimbLadderDown);
+ break;
+ case 0x4825:
+ sendMessage(_parentScene, 0x2000, 0);
+ _destY = _dataResource.getPoint(param.asInteger()).y;
+ GotoState(&Klaymen::stStartClimbLadderUp);
+ break;
+ case 0x4828:
+ GotoState(&Klaymen::stTurnToBackToUse);
+ break;
+ case 0x483F:
+ startSpecialWalkRight(param.asInteger());
+ break;
+ case 0x4840:
+ startSpecialWalkLeft(param.asInteger());
+ break;
+ }
+ return 0;
+}
+
+KmScene1109::KmScene1109(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y)
+ : Klaymen(vm, parentScene, x, y) {
+
+ // Empty
+}
+
+uint32 KmScene1109::xHandleMessage(int messageNum, const MessageParam &param) {
+ uint32 messageResult = 0;
+ switch (messageNum) {
+ case 0x2000:
+ _isSittingInTeleporter = param.asInteger() != 0;
+ messageResult = 1;
+ break;
+ case 0x4001:
+ case 0x4800:
+ startWalkToX(param.asPoint().x, false);
+ break;
+ case 0x4004:
+ if (_isSittingInTeleporter)
+ GotoState(&Klaymen::stSitIdleTeleporter);
+ else
+ GotoState(&Klaymen::stTryStandIdle);
+ break;
+ case 0x4804:
+ if (param.asInteger() != 0) {
+ _destX = param.asInteger();
+ GotoState(&Klaymen::stWalkingFirst);
+ } else
+ GotoState(&Klaymen::stPeekWall);
+ break;
+ case 0x4817:
+ setDoDeltaX(param.asInteger());
+ gotoNextStateExt();
+ break;
+ case 0x481D:
+ if (_isSittingInTeleporter)
+ GotoState(&Klaymen::stTurnToUseInTeleporter);
+ break;
+ case 0x481E:
+ if (_isSittingInTeleporter)
+ GotoState(&Klaymen::stReturnFromUseInTeleporter);
+ break;
+ case 0x4834:
+ GotoState(&Klaymen::stStepOver);
+ break;
+ case 0x4835:
+ sendMessage(_parentScene, 0x2000, 1);
+ _isSittingInTeleporter = true;
+ GotoState(&Klaymen::stSitInTeleporter);
+ break;
+ case 0x4836:
+ sendMessage(_parentScene, 0x2000, 0);
+ _isSittingInTeleporter = false;
+ GotoState(&Klaymen::stGetUpFromTeleporter);
+ break;
+ case 0x483D:
+ teleporterAppear(0x2C2A4A1C);
+ break;
+ case 0x483E:
+ teleporterDisappear(0x3C2E4245);
+ break;
+ }
+ return messageResult;
+}
+
+// KmScene1201
+
+KmScene1201::KmScene1201(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y)
+ : Klaymen(vm, parentScene, x, y) {
+
+ setKlaymenIdleTable(klaymenIdleTable4, ARRAYSIZE(klaymenIdleTable4));
+ _doYHitIncr = true;
+}
+
+uint32 KmScene1201::xHandleMessage(int messageNum, const MessageParam &param) {
+ switch (messageNum) {
+ case 0x4001:
+ case 0x4800:
+ startWalkToX(param.asPoint().x, false);
+ break;
+ case 0x4004:
+ GotoState(&Klaymen::stTryStandIdle);
+ break;
+ case 0x480A:
+ GotoState(&Klaymen::stMoveObject);
+ break;
+ case 0x4812:
+ GotoState(&Klaymen::stPickUpGeneric);
+ break;
+ case 0x4813:
+ GotoState(&Klaymen::stFetchMatch);
+ break;
+ case 0x4814:
+ GotoState(&Klaymen::stTumbleHeadless);
+ break;
+ case 0x4815:
+ GotoState(&Klaymen::stCloseEyes);
+ break;
+ case 0x4816:
+ if (param.asInteger() == 0)
+ GotoState(&Klaymen::stPressButtonSide);
+ break;
+ case 0x4817:
+ setDoDeltaX(param.asInteger());
+ gotoNextStateExt();
+ break;
+ case 0x481B:
+ if (param.asPoint().y != 0)
+ startWalkToXDistance(param.asPoint().y, param.asPoint().x);
+ else
+ startWalkToAttachedSpriteXDistance(param.asPoint().x);
+ break;
+ case 0x481D:
+ GotoState(&Klaymen::stTurnToUse);
+ break;
+ case 0x481E:
+ GotoState(&Klaymen::stReturnFromUse);
+ break;
+ case 0x481F:
+ GotoState(&Klaymen::stWonderAbout);
+ break;
+ case 0x482D:
+ setDoDeltaX(_x > (int16)param.asInteger() ? 1 : 0);
+ gotoNextStateExt();
+ break;
+ case 0x483F:
+ startSpecialWalkRight(param.asInteger());
+ break;
+ case 0x4840:
+ startSpecialWalkLeft(param.asInteger());
+ break;
+ }
+ return 0;
+}
+
+KmScene1303::KmScene1303(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y)
+ : Klaymen(vm, parentScene, x, y) {
+
+ // Empty
+}
+
+uint32 KmScene1303::xHandleMessage(int messageNum, const MessageParam &param) {
+ switch (messageNum) {
+ case 0x4804:
+ GotoState(&Klaymen::stPeekWall1);
+ break;
+ case 0x483B:
+ GotoState(&Klaymen::stPeekWallReturn);
+ break;
+ case 0x483C:
+ GotoState(&Klaymen::stPeekWall2);
+ break;
+ }
+ return 0;
+}
+
+KmScene1304::KmScene1304(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y)
+ : Klaymen(vm, parentScene, x, y) {
+
+ // Empty
+}
+
+uint32 KmScene1304::xHandleMessage(int messageNum, const MessageParam &param) {
+ switch (messageNum) {
+ case 0x4001:
+ case 0x4800:
+ startWalkToX(param.asPoint().x, false);
+ break;
+ case 0x4004:
+ GotoState(&Klaymen::stTryStandIdle);
+ break;
+ case 0x4812:
+ if (param.asInteger() == 2)
+ GotoState(&Klaymen::stPickUpNeedle);
+ else if (param.asInteger() == 1)
+ GotoState(&Klaymen::stPickUpTube);
+ else
+ GotoState(&Klaymen::stPickUpGeneric);
+ break;
+ case 0x4817:
+ setDoDeltaX(param.asInteger());
+ gotoNextStateExt();
+ break;
+ case 0x481B:
+ if (param.asPoint().y != 0)
+ startWalkToXDistance(param.asPoint().y, param.asPoint().x);
+ else
+ startWalkToAttachedSpriteXDistance(param.asPoint().x);
+ break;
+ case 0x481F:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stTurnAwayFromUse);
+ else if (param.asInteger() == 0)
+ GotoState(&Klaymen::stTurnToUseHalf);
+ else
+ GotoState(&Klaymen::stWonderAbout);
+ break;
+ case 0x483F:
+ startSpecialWalkRight(param.asInteger());
+ break;
+ case 0x4840:
+ startSpecialWalkLeft(param.asInteger());
+ break;
+ }
+ return 0;
+}
+
+KmScene1305::KmScene1305(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y)
+ : Klaymen(vm, parentScene, x, y) {
+
+ // Empty
+}
+
+uint32 KmScene1305::xHandleMessage(int messageNum, const MessageParam &param) {
+ switch (messageNum) {
+ case 0x4001:
+ case 0x4800:
+ startWalkToX(param.asPoint().x, false);
+ break;
+ case 0x4004:
+ GotoState(&Klaymen::stTryStandIdle);
+ break;
+ case 0x4804:
+ GotoState(&Klaymen::stCrashDown);
+ break;
+ case 0x4817:
+ setDoDeltaX(param.asInteger());
+ gotoNextStateExt();
+ break;
+ }
+ return 0;
+}
+
+KmScene1306::KmScene1306(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y)
+ : Klaymen(vm, parentScene, x, y) {
+
+ // Empty
+}
+
+uint32 KmScene1306::xHandleMessage(int messageNum, const MessageParam &param) {
+ uint32 messageResult = 0;
+ switch (messageNum) {
+ case 0x2000:
+ _isSittingInTeleporter = param.asInteger() != 0;
+ messageResult = 1;
+ break;
+ case 0x4001:
+ case 0x4800:
+ startWalkToX(param.asPoint().x, false);
+ break;
+ case 0x4004:
+ if (_isSittingInTeleporter)
+ GotoState(&Klaymen::stSitIdleTeleporter);
+ else
+ GotoState(&Klaymen::stTryStandIdle);
+ break;
+ case 0x4812:
+ if (param.asInteger() == 2)
+ GotoState(&Klaymen::stPickUpNeedle);
+ else if (param.asInteger() == 1)
+ GotoState(&Klaymen::stPickUpTube);
+ else
+ GotoState(&Klaymen::stPickUpGeneric);
+ break;
+ case 0x4816:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stPressButton);
+ else if (param.asInteger() == 2)
+ GotoState(&Klaymen::stPressFloorButton);
+ else
+ GotoState(&Klaymen::stPressButtonSide);
+ break;
+ case 0x4817:
+ setDoDeltaX(param.asInteger());
+ gotoNextStateExt();
+ break;
+ case 0x481A:
+ GotoState(&Klaymen::stInsertDisk);
+ break;
+ case 0x481B:
+ if (param.asPoint().y != 0)
+ startWalkToXDistance(param.asPoint().y, param.asPoint().x);
+ else
+ startWalkToAttachedSpriteXDistance(param.asPoint().x);
+ break;
+ case 0x481D:
+ if (_isSittingInTeleporter)
+ GotoState(&Klaymen::stTurnToUseInTeleporter);
+ else
+ GotoState(&Klaymen::stTurnToUse);
+ break;
+ case 0x481E:
+ if (_isSittingInTeleporter)
+ GotoState(&Klaymen::stReturnFromUseInTeleporter);
+ else
+ GotoState(&Klaymen::stReturnFromUse);
+ break;
+ case 0x481F:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stWonderAboutAfter);
+ else if (param.asInteger() == 0)
+ GotoState(&Klaymen::stWonderAboutHalf);
+ else if (param.asInteger() == 4)
+ GotoState(&Klaymen::stTurnAwayFromUse);
+ else if (param.asInteger() == 3)
+ GotoState(&Klaymen::stTurnToUseHalf);
+ else
+ GotoState(&Klaymen::stWonderAbout);
+ break;
+ case 0x482D:
+ setDoDeltaX(_x > (int16)param.asInteger() ? 1 : 0);
+ gotoNextStateExt();
+ break;
+ case 0x482E:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stWalkToFrontNoStep);
+ else
+ GotoState(&Klaymen::stWalkToFront);
+ break;
+ case 0x482F:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stTurnToFront);
+ else
+ GotoState(&Klaymen::stTurnToBack);
+ break;
+ case 0x4834:
+ GotoState(&Klaymen::stStepOver);
+ break;
+ case 0x4835:
+ sendMessage(_parentScene, 0x2000, 1);
+ _isSittingInTeleporter = true;
+ GotoState(&Klaymen::stSitInTeleporter);
+ break;
+ case 0x4836:
+ sendMessage(_parentScene, 0x2000, 0);
+ _isSittingInTeleporter = false;
+ GotoState(&Klaymen::stGetUpFromTeleporter);
+ break;
+ case 0x483D:
+ teleporterAppear(0xEE084A04);
+ break;
+ case 0x483E:
+ teleporterDisappear(0xB86A4274);
+ break;
+ case 0x483F:
+ startSpecialWalkRight(param.asInteger());
+ break;
+ case 0x4840:
+ startSpecialWalkLeft(param.asInteger());
+ break;
+ }
+ return messageResult;
+}
+
+KmScene1308::KmScene1308(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y)
+ : Klaymen(vm, parentScene, x, y) {
+
+ // Empty
+}
+
+uint32 KmScene1308::xHandleMessage(int messageNum, const MessageParam &param) {
+ switch (messageNum) {
+ case 0x4001:
+ case 0x4800:
+ startWalkToX(param.asPoint().x, false);
+ break;
+ case 0x4004:
+ GotoState(&Klaymen::stTryStandIdle);
+ break;
+ case 0x480A:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stMoveObjectSkipTurnFaceObject);
+ else
+ GotoState(&Klaymen::stMoveObjectFaceObject);
+ break;
+ case 0x480D:
+ GotoState(&Klaymen::stUseLever);
+ break;
+ case 0x4812:
+ if (param.asInteger() == 2)
+ GotoState(&Klaymen::stPickUpNeedle);
+ else if (param.asInteger() == 1)
+ GotoState(&Klaymen::stPickUpTube);
+ else
+ GotoState(&Klaymen::stPickUpGeneric);
+ break;
+ case 0x4817:
+ setDoDeltaX(param.asInteger());
+ gotoNextStateExt();
+ break;
+ case 0x481A:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stInsertKey);
+ else
+ GotoState(&Klaymen::stInsertDisk);
+ break;
+ case 0x481B:
+ if (param.asPoint().y != 0)
+ startWalkToXDistance(param.asPoint().y, param.asPoint().x);
+ else
+ startWalkToAttachedSpriteXDistance(param.asPoint().x);
+ break;
+ case 0x481D:
+ GotoState(&Klaymen::stTurnToUse);
+ break;
+ case 0x481E:
+ GotoState(&Klaymen::stReturnFromUse);
+ break;
+ case 0x4827:
+ GotoState(&Klaymen::stReleaseLever);
+ break;
+ case 0x4834:
+ GotoState(&Klaymen::stStepOver);
+ break;
+ case 0x483F:
+ startSpecialWalkRight(param.asInteger());
+ break;
+ case 0x4840:
+ startSpecialWalkLeft(param.asInteger());
+ break;
+ }
+ return 0;
+}
+
+// KmScene1401
+
+KmScene1401::KmScene1401(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y)
+ : Klaymen(vm, parentScene, x, y) {
+
+ // Empty
+}
+
+uint32 KmScene1401::xHandleMessage(int messageNum, const MessageParam &param) {
+ switch (messageNum) {
+ case 0x4001:
+ case 0x4800:
+ startWalkToX(param.asPoint().x, false);
+ break;
+ case 0x4004:
+ GotoState(&Klaymen::stTryStandIdle);
+ break;
+ case 0x480A:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stMoveObjectSkipTurnFaceObject);
+ else
+ GotoState(&Klaymen::stMoveObjectFaceObject);
+ break;
+ case 0x4816:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stPressButton);
+ else if (param.asInteger() == 2)
+ GotoState(&Klaymen::stPressFloorButton);
+ else
+ GotoState(&Klaymen::stPressButtonSide);
+ break;
+ case 0x4817:
+ setDoDeltaX(param.asInteger());
+ gotoNextStateExt();
+ break;
+ case 0x481B:
+ if (param.asPoint().y != 0)
+ startWalkToXDistance(param.asPoint().y, param.asPoint().x);
+ else
+ startWalkToAttachedSpriteXDistance(param.asPoint().x);
+ break;
+ case 0x481F:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stTurnAwayFromUse);
+ else if (param.asInteger() == 0)
+ GotoState(&Klaymen::stTurnToUseHalf);
+ else
+ GotoState(&Klaymen::stWonderAbout);
+ break;
+ case 0x482D:
+ setDoDeltaX(_x > (int16)param.asInteger() ? 1 : 0);
+ gotoNextStateExt();
+ break;
+ case 0x482E:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stWalkToFrontNoStep);
+ else
+ GotoState(&Klaymen::stWalkToFront);
+ break;
+ case 0x482F:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stTurnToFront);
+ else
+ GotoState(&Klaymen::stTurnToBack);
+ break;
+ }
+ return 0;
+}
+
+// KmScene1402
+
+KmScene1402::KmScene1402(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y)
+ : Klaymen(vm, parentScene, x, y) {
+
+ SetFilterY(&Sprite::defFilterY);
+}
+
+uint32 KmScene1402::xHandleMessage(int messageNum, const MessageParam &param) {
+ switch (messageNum) {
+ case 0x4001:
+ case 0x4800:
+ startWalkToX(param.asPoint().x, false);
+ break;
+ case 0x4004:
+ GotoState(&Klaymen::stTryStandIdle);
+ break;
+ case 0x480A:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stMoveObjectSkipTurnFaceObject);
+ else
+ GotoState(&Klaymen::stMoveObjectFaceObject);
+ break;
+ case 0x4817:
+ setDoDeltaX(param.asInteger());
+ gotoNextStateExt();
+ break;
+ case 0x481B:
+ if (param.asPoint().y != 0)
+ startWalkToXDistance(param.asPoint().y, param.asPoint().x);
+ else
+ startWalkToAttachedSpriteXDistance(param.asPoint().x);
+ break;
+ case 0x481D:
+ GotoState(&Klaymen::stTurnToUse);
+ break;
+ case 0x481E:
+ GotoState(&Klaymen::stReturnFromUse);
+ break;
+ }
+ return 0;
+}
+
+// KmScene1403
+
+KmScene1403::KmScene1403(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y)
+ : Klaymen(vm, parentScene, x, y) {
+
+ setKlaymenIdleTable(klaymenIdleTable4, ARRAYSIZE(klaymenIdleTable4));
+}
+
+uint32 KmScene1403::xHandleMessage(int messageNum, const MessageParam &param) {
+ switch (messageNum) {
+ case 0x4001:
+ case 0x4800:
+ startWalkToX(param.asPoint().x, false);
+ break;
+ case 0x4004:
+ GotoState(&Klaymen::stTryStandIdle);
+ break;
+ case 0x480A:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stMoveObjectSkipTurnFaceObject);
+ else
+ GotoState(&Klaymen::stMoveObjectFaceObject);
+ break;
+ case 0x480D:
+ GotoState(&Klaymen::stUseLever);
+ break;
+ case 0x4812:
+ if (param.asInteger() == 2)
+ GotoState(&Klaymen::stPickUpNeedle);
+ else if (param.asInteger() == 1)
+ GotoState(&Klaymen::stPickUpTube);
+ else
+ GotoState(&Klaymen::stPickUpGeneric);
+ break;
+ case 0x4817:
+ setDoDeltaX(param.asInteger());
+ gotoNextStateExt();
+ break;
+ case 0x481B:
+ if (param.asPoint().y != 0)
+ startWalkToXDistance(param.asPoint().y, param.asPoint().x);
+ else
+ startWalkToAttachedSpriteXDistance(param.asPoint().x);
+ break;
+ case 0x4827:
+ GotoState(&Klaymen::stReleaseLever);
+ break;
+ case 0x483F:
+ startSpecialWalkRight(param.asInteger());
+ break;
+ case 0x4840:
+ startSpecialWalkLeft(param.asInteger());
+ break;
+ }
+ return 0;
+}
+
+// KmScene1404
+
+KmScene1404::KmScene1404(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y)
+ : Klaymen(vm, parentScene, x, y) {
+
+ // Empty
+}
+
+uint32 KmScene1404::xHandleMessage(int messageNum, const MessageParam &param) {
+ switch (messageNum) {
+ case 0x4001:
+ case 0x4800:
+ startWalkToX(param.asPoint().x, false);
+ break;
+ case 0x4004:
+ GotoState(&Klaymen::stTryStandIdle);
+ break;
+ case 0x480A:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stMoveObjectSkipTurnFaceObject);
+ else
+ GotoState(&Klaymen::stMoveObjectFaceObject);
+ break;
+ case 0x4812:
+ if (param.asInteger() == 2)
+ GotoState(&Klaymen::stPickUpNeedle);
+ else if (param.asInteger() == 1)
+ GotoState(&Klaymen::stPickUpTube);
+ else
+ GotoState(&Klaymen::stPickUpGeneric);
+ break;
+ case 0x4817:
+ setDoDeltaX(param.asInteger());
+ gotoNextStateExt();
+ break;
+ case 0x481A:
+ GotoState(&Klaymen::stInsertDisk);
+ break;
+ case 0x481B:
+ if (param.asPoint().y != 0)
+ startWalkToXDistance(param.asPoint().y, param.asPoint().x);
+ else
+ startWalkToAttachedSpriteXDistance(param.asPoint().x);
+ break;
+ case 0x481D:
+ GotoState(&Klaymen::stTurnToUse);
+ break;
+ case 0x481E:
+ GotoState(&Klaymen::stReturnFromUse);
+ break;
+ case 0x481F:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stWonderAboutAfter);
+ else if (param.asInteger() == 0)
+ GotoState(&Klaymen::stWonderAboutHalf);
+ else if (param.asInteger() == 4)
+ GotoState(&Klaymen::stTurnAwayFromUse);
+ else if (param.asInteger() == 3)
+ GotoState(&Klaymen::stTurnToUseHalf);
+ else
+ GotoState(&Klaymen::stWonderAbout);
+ break;
+ case 0x482D:
+ setDoDeltaX(_x > (int16)param.asInteger() ? 1 : 0);
+ gotoNextStateExt();
+ break;
+ case 0x483F:
+ startSpecialWalkRight(param.asInteger());
+ break;
+ case 0x4840:
+ startSpecialWalkLeft(param.asInteger());
+ break;
+ }
+ return 0;
+}
+
+KmScene1608::KmScene1608(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y)
+ : Klaymen(vm, parentScene, x, y) {
+
+ // Empty
+}
+
+uint32 KmScene1608::xHandleMessage(int messageNum, const MessageParam &param) {
+ uint32 messageResult = 0;
+ switch (messageNum) {
+ case 0x2032:
+ _isSittingInTeleporter = param.asInteger() != 0;
+ messageResult = 1;
+ break;
+ case 0x4001:
+ case 0x4800:
+ startWalkToX(param.asPoint().x, false);
+ break;
+ case 0x4004:
+ if (_isSittingInTeleporter)
+ GotoState(&Klaymen::stSitIdleTeleporter);
+ else
+ GotoState(&Klaymen::stTryStandIdle);
+ break;
+ case 0x4812:
+ if (param.asInteger() == 2)
+ GotoState(&Klaymen::stPickUpNeedle);
+ else if (param.asInteger() == 1)
+ GotoState(&Klaymen::stPickUpTube);
+ else
+ GotoState(&Klaymen::stPickUpGeneric);
+ break;
+ case 0x4817:
+ setDoDeltaX(param.asInteger());
+ gotoNextStateExt();
+ break;
+ case 0x481B:
+ if (param.asPoint().y != 0)
+ startWalkToXDistance(param.asPoint().y, param.asPoint().x);
+ else
+ startWalkToAttachedSpriteXDistance(param.asPoint().x);
+ break;
+ case 0x481D:
+ if (_isSittingInTeleporter)
+ GotoState(&Klaymen::stTurnToUseInTeleporter);
+ break;
+ case 0x481E:
+ if (_isSittingInTeleporter)
+ GotoState(&Klaymen::stReturnFromUseInTeleporter);
+ break;
+ case 0x481F:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stWonderAboutAfter);
+ else if (param.asInteger() == 0)
+ GotoState(&Klaymen::stWonderAboutHalf);
+ else if (param.asInteger() == 4)
+ GotoState(&Klaymen::stTurnAwayFromUse);
+ else if (param.asInteger() == 3)
+ GotoState(&Klaymen::stTurnToUseHalf);
+ else
+ GotoState(&Klaymen::stWonderAbout);
+ break;
+ case 0x482D:
+ setDoDeltaX(_x > (int16)param.asInteger() ? 1 : 0);
+ gotoNextStateExt();
+ break;
+ case 0x4834:
+ GotoState(&Klaymen::stStepOver);
+ break;
+ case 0x4835:
+ sendMessage(_parentScene, 0x2032, 1);
+ _isSittingInTeleporter = true;
+ GotoState(&Klaymen::stSitInTeleporter);
+ break;
+ case 0x4836:
+ sendMessage(_parentScene, 0x2032, 0);
+ _isSittingInTeleporter = false;
+ GotoState(&Klaymen::stGetUpFromTeleporter);
+ break;
+ case 0x483F:
+ startSpecialWalkRight(param.asInteger());
+ break;
+ case 0x4840:
+ startSpecialWalkLeft(param.asInteger());
+ break;
+ }
+ return messageResult;
+}
+
+// KmScene1705
+
+KmScene1705::KmScene1705(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y)
+ : Klaymen(vm, parentScene, x, y) {
+
+ // Empty
+}
+
+uint32 KmScene1705::xHandleMessage(int messageNum, const MessageParam &param) {
+ uint32 messageResult = 0;
+ switch (messageNum) {
+ case 0x2000:
+ _isSittingInTeleporter = param.asInteger() != 0;
+ messageResult = 1;
+ break;
+ case 0x4001:
+ case 0x4800:
+ startWalkToX(param.asPoint().x, false);
+ break;
+ case 0x4004:
+ if (_isSittingInTeleporter)
+ GotoState(&Klaymen::stSitIdleTeleporter);
+ else
+ GotoState(&Klaymen::stTryStandIdle);
+ break;
+ case 0x4803:
+ GotoState(&Klaymen::stFallSkipJump);
+ break;
+ case 0x4812:
+ if (param.asInteger() == 2)
+ GotoState(&Klaymen::stPickUpNeedle);
+ else if (param.asInteger() == 1)
+ GotoState(&Klaymen::stPickUpTube);
+ else
+ GotoState(&Klaymen::stPickUpGeneric);
+ break;
+ case 0x4817:
+ setDoDeltaX(param.asInteger());
+ gotoNextStateExt();
+ break;
+ case 0x481B:
+ if (param.asPoint().y != 0)
+ startWalkToXDistance(param.asPoint().y, param.asPoint().x);
+ else
+ startWalkToAttachedSpriteXDistance(param.asPoint().x);
+ break;
+ case 0x481D:
+ if (_isSittingInTeleporter) {
+ GotoState(&Klaymen::stTurnToUseInTeleporter);
+ }
+ break;
+ case 0x481E:
+ if (_isSittingInTeleporter)
+ GotoState(&Klaymen::stReturnFromUseInTeleporter);
+ break;
+ case 0x481F:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stWonderAboutAfter);
+ else if (param.asInteger() == 0)
+ GotoState(&Klaymen::stWonderAboutHalf);
+ else if (param.asInteger() == 4)
+ GotoState(&Klaymen::stTurnAwayFromUse);
+ else if (param.asInteger() == 3)
+ GotoState(&Klaymen::stTurnToUseHalf);
+ else
+ GotoState(&Klaymen::stWonderAbout);
+ break;
+ case 0x4834:
+ GotoState(&Klaymen::stStepOver);
+ break;
+ case 0x4835:
+ sendMessage(_parentScene, 0x2000, 1);
+ _isSittingInTeleporter = true;
+ GotoState(&Klaymen::stSitInTeleporter);
+ break;
+ case 0x4836:
+ sendMessage(_parentScene, 0x2000, 0);
+ _isSittingInTeleporter = false;
+ GotoState(&Klaymen::stGetUpFromTeleporter);
+ break;
+ case 0x483D:
+ teleporterAppear(0x5E0A4905);
+ break;
+ case 0x483E:
+ teleporterDisappear(0xD86E4477);
+ break;
+ }
+ return messageResult;
+}
+
+KmScene1901::KmScene1901(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y)
+ : Klaymen(vm, parentScene, x, y) {
+
+ // Empty
+}
+
+uint32 KmScene1901::xHandleMessage(int messageNum, const MessageParam &param) {
+ switch (messageNum) {
+ case 0x4001:
+ case 0x4800:
+ startWalkToX(param.asPoint().x, false);
+ break;
+ case 0x4004:
+ GotoState(&Klaymen::stTryStandIdle);
+ break;
+ case 0x4817:
+ setDoDeltaX(param.asInteger());
+ gotoNextStateExt();
+ break;
+ case 0x481D:
+ GotoState(&Klaymen::stTurnToUse);
+ break;
+ case 0x481E:
+ GotoState(&Klaymen::stReturnFromUse);
+ break;
+ case 0x482D:
+ setDoDeltaX(_x > (int16)param.asInteger() ? 1 : 0);
+ gotoNextStateExt();
+ break;
+ case 0x483F:
+ startSpecialWalkRight(param.asInteger());
+ break;
+ case 0x4840:
+ startSpecialWalkLeft(param.asInteger());
+ break;
+ }
+ return 0;
+}
+
+KmScene2001::KmScene2001(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y)
+ : Klaymen(vm, parentScene, x, y) {
+
+ // Empty
+}
+
+uint32 KmScene2001::xHandleMessage(int messageNum, const MessageParam &param) {
+ uint32 messageResult = 0;
+ switch (messageNum) {
+ case 0x2000:
+ _isSittingInTeleporter = param.asInteger() != 0;
+ messageResult = 1;
+ break;
+ case 0x4001:
+ case 0x4800:
+ startWalkToX(param.asPoint().x, false);
+ break;
+ case 0x4004:
+ if (_isSittingInTeleporter)
+ GotoState(&Klaymen::stSitIdleTeleporter);
+ else
+ GotoState(&Klaymen::stTryStandIdle);
+ break;
+ case 0x4804:
+ if (param.asInteger() != 0) {
+ _destX = param.asInteger();
+ GotoState(&Klaymen::stWalkingFirst);
+ } else
+ GotoState(&Klaymen::stPeekWall);
+ break;
+ case 0x4817:
+ setDoDeltaX(param.asInteger());
+ gotoNextStateExt();
+ break;
+ case 0x481D:
+ if (_isSittingInTeleporter)
+ GotoState(&Klaymen::stTurnToUseInTeleporter);
+ break;
+ case 0x481E:
+ if (_isSittingInTeleporter)
+ GotoState(&Klaymen::stReturnFromUseInTeleporter);
+ break;
+ case 0x4834:
+ GotoState(&Klaymen::stStepOver);
+ break;
+ case 0x4835:
+ sendMessage(_parentScene, 0x2000, 1);
+ _isSittingInTeleporter = true;
+ GotoState(&Klaymen::stSitInTeleporter);
+ break;
+ case 0x4836:
+ sendMessage(_parentScene, 0x2000, 0);
+ _isSittingInTeleporter = false;
+ GotoState(&Klaymen::stGetUpFromTeleporter);
+ break;
+ case 0x483D:
+ teleporterAppear(0xBE68CC54);
+ break;
+ case 0x483E:
+ teleporterDisappear(0x18AB4ED4);
+ break;
+ }
+ return messageResult;
+}
+
+KmScene2101::KmScene2101(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y)
+ : Klaymen(vm, parentScene, x, y) {
+
+ // Empty
+}
+
+uint32 KmScene2101::xHandleMessage(int messageNum, const MessageParam &param) {
+ uint32 messageResult = 0;
+ switch (messageNum) {
+ case 0x2000:
+ _isSittingInTeleporter = param.asInteger() != 0;
+ messageResult = 1;
+ break;
+ case 0x4001:
+ case 0x4800:
+ startWalkToX(param.asPoint().x, false);
+ break;
+ case 0x4004:
+ if (_isSittingInTeleporter)
+ GotoState(&Klaymen::stSitIdleTeleporter);
+ else
+ GotoState(&Klaymen::stTryStandIdle);
+ break;
+ case 0x4811:
+ GotoState(&Klaymen::stHitByDoor);
+ break;
+ case 0x4812:
+ if (param.asInteger() == 2)
+ GotoState(&Klaymen::stPickUpNeedle);
+ else if (param.asInteger() == 1)
+ GotoState(&Klaymen::stPickUpTube);
+ else
+ GotoState(&Klaymen::stPickUpGeneric);
+ break;
+ case 0x4816:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stPressButton);
+ else if (param.asInteger() == 2)
+ GotoState(&Klaymen::stPressFloorButton);
+ else
+ GotoState(&Klaymen::stPressButtonSide);
+ break;
+ case 0x4817:
+ setDoDeltaX(param.asInteger());
+ gotoNextStateExt();
+ break;
+ case 0x481B:
+ if (param.asPoint().y != 0)
+ startWalkToXDistance(param.asPoint().y, param.asPoint().x);
+ else
+ startWalkToAttachedSpriteXDistance(param.asPoint().x);
+ break;
+ case 0x481D:
+ if (_isSittingInTeleporter)
+ GotoState(&Klaymen::stTurnToUseInTeleporter);
+ break;
+ case 0x481E:
+ if (_isSittingInTeleporter)
+ GotoState(&Klaymen::stReturnFromUseInTeleporter);
+ break;
+ case 0x4834:
+ GotoState(&Klaymen::stStepOver);
+ break;
+ case 0x4835:
+ sendMessage(_parentScene, 0x2000, 1);
+ _isSittingInTeleporter = true;
+ GotoState(&Klaymen::stSitInTeleporter);
+ break;
+ case 0x4836:
+ sendMessage(_parentScene, 0x2000, 0);
+ _isSittingInTeleporter = false;
+ GotoState(&Klaymen::stGetUpFromTeleporter);
+ break;
+ case 0x483D:
+ teleporterAppear(0xFF290E30);
+ break;
+ case 0x483E:
+ teleporterDisappear(0x9A28CA1C);
+ break;
+ }
+ return messageResult;
+}
+
+KmScene2201::KmScene2201(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y, NRect *clipRects, int clipRectsCount)
+ : Klaymen(vm, parentScene, x, y) {
+
+ _surface->setClipRects(clipRects, clipRectsCount);
+ _dataResource.load(0x04104242);
+}
+
+uint32 KmScene2201::xHandleMessage(int messageNum, const MessageParam &param) {
+ switch (messageNum) {
+ case 0x4001:
+ case 0x4800:
+ startWalkToX(param.asPoint().x, false);
+ break;
+ case 0x4004:
+ GotoState(&Klaymen::stTryStandIdle);
+ break;
+ case 0x4812:
+ GotoState(&Klaymen::stPickUpGeneric);
+ break;
+ case 0x4816:
+ if (param.asInteger() == 0)
+ GotoState(&Klaymen::stPressButtonSide);
+ break;
+ case 0x4817:
+ setDoDeltaX(param.asInteger() ? 1 : 0);
+ gotoNextStateExt();
+ break;
+ case 0x4818:
+ startWalkToX(_dataResource.getPoint(param.asInteger()).x, false);
+ break;
+ case 0x481B:
+ if (param.asPoint().y != 0)
+ startWalkToXDistance(param.asPoint().y, param.asPoint().x);
+ else
+ startWalkToAttachedSpriteXDistance(param.asPoint().x);
+ break;
+ case 0x481D:
+ GotoState(&Klaymen::stTurnToUse);
+ break;
+ case 0x481E:
+ GotoState(&Klaymen::stReturnFromUse);
+ break;
+ case 0x482D:
+ setDoDeltaX(_x > (int16)param.asInteger() ? 1 : 0);
+ gotoNextStateExt();
+ break;
+ case 0x482E:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stWalkToFrontNoStep);
+ else
+ GotoState(&Klaymen::stWalkToFront);
+ break;
+ case 0x482F:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stTurnToFront);
+ else
+ GotoState(&Klaymen::stTurnToBack);
+ break;
+ case 0x483F:
+ startSpecialWalkRight(param.asInteger());
+ break;
+ case 0x4840:
+ startSpecialWalkLeft(param.asInteger());
+ break;
+ }
+ return 0;
+}
+
+KmScene2203::KmScene2203(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y)
+ : Klaymen(vm, parentScene, x, y) {
+
+ // Empty
+}
+
+uint32 KmScene2203::xHandleMessage(int messageNum, const MessageParam &param) {
+ switch (messageNum) {
+ case 0x4001:
+ case 0x4800:
+ startWalkToX(param.asPoint().x, false);
+ break;
+ case 0x4004:
+ GotoState(&Klaymen::stTryStandIdle);
+ break;
+ case 0x4812:
+ if (param.asInteger() == 2)
+ GotoState(&Klaymen::stPickUpNeedle);
+ else if (param.asInteger() == 1)
+ GotoState(&Klaymen::stPickUpTube);
+ else
+ GotoState(&Klaymen::stPickUpGeneric);
+ break;
+ case 0x4816:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stPressButton);
+ else if (param.asInteger() == 2)
+ GotoState(&Klaymen::stPressFloorButton);
+ else
+ GotoState(&Klaymen::stPressButtonSide);
+ break;
+ case 0x4817:
+ setDoDeltaX(param.asInteger());
+ gotoNextStateExt();
+ break;
+ case 0x4818:
+ startWalkToX(_dataResource.getPoint(param.asInteger()).x, false);
+ break;
+ case 0x4819:
+ GotoState(&Klaymen::stClayDoorOpen);
+ break;
+ case 0x481A:
+ GotoState(&Klaymen::stInsertDisk);
+ break;
+ case 0x481B:
+ if (param.asPoint().y != 0)
+ startWalkToXDistance(param.asPoint().y, param.asPoint().x);
+ else
+ startWalkToAttachedSpriteXDistance(param.asPoint().x);
+ break;
+ case 0x481D:
+ GotoState(&Klaymen::stTurnToUse);
+ break;
+ case 0x481E:
+ GotoState(&Klaymen::stReturnFromUse);
+ break;
+ case 0x482D:
+ setDoDeltaX(_x > (int16)param.asInteger() ? 1 : 0);
+ gotoNextStateExt();
+ break;
+ case 0x483F:
+ startSpecialWalkRight(param.asInteger());
+ break;
+ case 0x4840:
+ startSpecialWalkLeft(param.asInteger());
+ break;
+ }
+ return 0;
+}
+
+KmScene2205::KmScene2205(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y)
+ : Klaymen(vm, parentScene, x, y) {
+
+ // Empty
+}
+
+void KmScene2205::xUpdate() {
+ setGlobalVar(V_KLAYMEN_FRAMEINDEX, _currFrameIndex);
+}
+
+uint32 KmScene2205::xHandleMessage(int messageNum, const MessageParam &param) {
+ switch (messageNum) {
+ case 0x4001:
+ case 0x4800:
+ startWalkToX(param.asPoint().x, false);
+ break;
+ case 0x4004:
+ GotoState(&Klaymen::stTryStandIdle);
+ break;
+ case 0x4804:
+ if (param.asInteger() != 0) {
+ _destX = param.asInteger();
+ GotoState(&Klaymen::stStartWalkingResume);
+ } else
+ GotoState(&Klaymen::stPeekWall);
+ break;
+ case 0x4816:
+ if (param.asInteger() == 0)
+ GotoState(&Klaymen::stPressButtonSide);
+ break;
+ case 0x4817:
+ setDoDeltaX(param.asInteger());
+ gotoNextStateExt();
+ break;
+ case 0x4818:
+ startWalkToX(_dataResource.getPoint(param.asInteger()).x, false);
+ break;
+ case 0x483F:
+ startSpecialWalkRight(param.asInteger());
+ break;
+ case 0x4840:
+ startSpecialWalkLeft(param.asInteger());
+ break;
+ }
+ return 0;
+}
+
+KmScene2206::KmScene2206(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y)
+ : Klaymen(vm, parentScene, x, y) {
+
+ _walkResumeFrameIncr = 1;
+ _vm->_soundMan->addSound(0x80101800, 0xD3B02847);
+}
+
+KmScene2206::~KmScene2206() {
+ _vm->_soundMan->deleteSoundGroup(0x80101800);
+}
+
+void KmScene2206::xUpdate() {
+ setGlobalVar(V_KLAYMEN_FRAMEINDEX, _currFrameIndex);
+}
+
+uint32 KmScene2206::xHandleMessage(int messageNum, const MessageParam &param) {
+ switch (messageNum) {
+ case 0x4001:
+ case 0x4800:
+ startWalkToX(param.asPoint().x, false);
+ break;
+ case 0x4004:
+ GotoState(&Klaymen::stTryStandIdle);
+ break;
+ case 0x4803:
+ GotoState(&Klaymen::stRidePlatformDown);
+ break;
+ case 0x4804:
+ if (param.asInteger() != 0) {
+ _destX = param.asInteger();
+ GotoState(&Klaymen::stStartWalkingResume);
+ } else
+ GotoState(&Klaymen::stPeekWall);
+ break;
+ case 0x4812:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stPickUpTube);
+ else
+ GotoState(&Klaymen::stPickUpGeneric);
+ break;
+ case 0x4816:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stPressButton);
+ else if (param.asInteger() == 2)
+ GotoState(&Klaymen::stPressFloorButton);
+ else
+ GotoState(&Klaymen::stPressButtonSide);
+ break;
+ case 0x4817:
+ setDoDeltaX(param.asInteger());
+ gotoNextStateExt();
+ break;
+ case 0x481B:
+ if (param.asPoint().y != 0)
+ startWalkToXDistance(param.asPoint().y, param.asPoint().x);
+ else
+ startWalkToAttachedSpriteXDistance(param.asPoint().x);
+ break;
+ case 0x481F:
+ if (param.asInteger() == 0)
+ GotoState(&Klaymen::stWonderAboutHalf);
+ else if (param.asInteger() == 1)
+ GotoState(&Klaymen::stWonderAboutAfter);
+ else if (param.asInteger() == 3)
+ GotoState(&Klaymen::stTurnToUseHalf);
+ else if (param.asInteger() == 4)
+ GotoState(&Klaymen::stTurnAwayFromUse);
+ else
+ GotoState(&Klaymen::stWonderAbout);
+ break;
+ case 0x482D:
+ setDoDeltaX(_x > (int16)param.asInteger() ? 1 : 0);
+ gotoNextStateExt();
+ break;
+ case 0x482E:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stWalkToFrontNoStep);
+ else
+ GotoState(&Klaymen::stWalkToFront);
+ break;
+ case 0x482F:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stTurnToFront);
+ else
+ GotoState(&Klaymen::stTurnToBack);
+ break;
+ case 0x4837:
+ stopWalking();
+ break;
+ case 0x483F:
+ startSpecialWalkRight(param.asInteger());
+ break;
+ case 0x4840:
+ startSpecialWalkLeft(param.asInteger());
+ break;
+ }
+ return 0;
+}
+
+KmScene2207::KmScene2207(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y)
+ : Klaymen(vm, parentScene, x, y) {
+
+ // Empty
+}
+
+uint32 KmScene2207::xHandleMessage(int messageNum, const MessageParam &param) {
+ switch (messageNum) {
+ case 0x2001:
+ GotoState(&Klaymen::stRidePlatform);
+ break;
+ case 0x2005:
+ suRidePlatform();
+ GotoState(&Klaymen::stTryStandIdle);
+ break;
+ case 0x4001:
+ case 0x4800:
+ startWalkToX(param.asPoint().x, false);
+ break;
+ case 0x4004:
+ GotoState(&Klaymen::stTryStandIdle);
+ break;
+ case 0x480D:
+ GotoState(&Klaymen::stInteractLever);
+ break;
+ case 0x4812:
+ GotoState(&Klaymen::stPickUpGeneric);
+ break;
+ case 0x4816:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stPressButton);
+ else if (param.asInteger() == 2)
+ GotoState(&Klaymen::stPressFloorButton);
+ else
+ GotoState(&Klaymen::stPressButtonSide);
+ break;
+ case 0x4817:
+ setDoDeltaX(param.asInteger());
+ gotoNextStateExt();
+ break;
+ case 0x481B:
+ if (param.asPoint().y != 0)
+ startWalkToXDistance(param.asPoint().y, param.asPoint().x);
+ else
+ startWalkToAttachedSpriteXDistance(param.asPoint().x);
+ break;
+ case 0x4827:
+ GotoState(&Klaymen::stReleaseLever);
+ break;
+ case 0x482D:
+ setDoDeltaX(_x > (int16)param.asInteger() ? 1 : 0);
+ gotoNextStateExt();
+ break;
+ case 0x483F:
+ startSpecialWalkRight(param.asInteger());
+ break;
+ case 0x4840:
+ startSpecialWalkLeft(param.asInteger());
+ break;
+ }
+ return 0;
+}
+
+KmScene2242::KmScene2242(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y)
+ : Klaymen(vm, parentScene, x, y) {
+
+ // Empty
+}
+
+void KmScene2242::xUpdate() {
+ setGlobalVar(V_KLAYMEN_FRAMEINDEX, _currFrameIndex);
+}
+
+uint32 KmScene2242::xHandleMessage(int messageNum, const MessageParam &param) {
+ switch (messageNum) {
+ case 0x4001:
+ case 0x4800:
+ startWalkToX(param.asPoint().x, false);
+ break;
+ case 0x4004:
+ GotoState(&Klaymen::stTryStandIdle);
+ break;
+ case 0x4804:
+ if (param.asInteger() != 0) {
+ _destX = param.asInteger();
+ GotoState(&Klaymen::stStartWalkingResume);
+ } else
+ GotoState(&Klaymen::stPeekWall);
+ break;
+ case 0x4812:
+ if (param.asInteger() == 2)
+ GotoState(&Klaymen::stPickUpNeedle);
+ else if (param.asInteger() == 1)
+ GotoState(&Klaymen::stPickUpTube);
+ else
+ GotoState(&Klaymen::stPickUpGeneric);
+ break;
+ case 0x4817:
+ setDoDeltaX(param.asInteger());
+ gotoNextStateExt();
+ break;
+ case 0x481B:
+ if (param.asPoint().y != 0)
+ startWalkToXDistance(param.asPoint().y, param.asPoint().x);
+ else
+ startWalkToAttachedSpriteXDistance(param.asPoint().x);
+ break;
+ case 0x481F:
+ if (param.asInteger() == 0)
+ GotoState(&Klaymen::stWonderAboutHalf);
+ else if (param.asInteger() == 1)
+ GotoState(&Klaymen::stWonderAboutAfter);
+ else if (param.asInteger() == 3)
+ GotoState(&Klaymen::stTurnToUseHalf);
+ else if (param.asInteger() == 4)
+ GotoState(&Klaymen::stTurnAwayFromUse);
+ else
+ GotoState(&Klaymen::stWonderAbout);
+ break;
+ case 0x482D:
+ setDoDeltaX(_x > (int16)param.asInteger() ? 1 : 0);
+ gotoNextStateExt();
+ break;
+ case 0x4837:
+ stopWalking();
+ break;
+ }
+ return 0;
+}
+
+KmHallOfRecords::KmHallOfRecords(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y)
+ : Klaymen(vm, parentScene, x, y) {
+ // Empty
+}
+
+void KmHallOfRecords::xUpdate() {
+ setGlobalVar(V_KLAYMEN_FRAMEINDEX, _currFrameIndex);
+}
+
+uint32 KmHallOfRecords::xHandleMessage(int messageNum, const MessageParam &param) {
+ switch (messageNum) {
+ case 0x4001:
+ case 0x4800:
+ startWalkToX(param.asPoint().x, false);
+ break;
+ case 0x4004:
+ GotoState(&Klaymen::stTryStandIdle);
+ break;
+ case 0x4804:
+ if (param.asInteger() != 0) {
+ _destX = param.asInteger();
+ GotoState(&Klaymen::stStartWalkingResume);
+ } else
+ GotoState(&Klaymen::stPeekWall);
+ break;
+ case 0x4817:
+ setDoDeltaX(param.asInteger());
+ gotoNextStateExt();
+ break;
+ case 0x481F:
+ if (param.asInteger() == 0)
+ GotoState(&Klaymen::stWonderAboutHalf);
+ else if (param.asInteger() == 1)
+ GotoState(&Klaymen::stWonderAboutAfter);
+ else if (param.asInteger() == 3)
+ GotoState(&Klaymen::stTurnToUseHalf);
+ else if (param.asInteger() == 4)
+ GotoState(&Klaymen::stTurnAwayFromUse);
+ else
+ GotoState(&Klaymen::stWonderAbout);
+ break;
+ case 0x482D:
+ setDoDeltaX(_x > (int16)param.asInteger() ? 1 : 0);
+ gotoNextStateExt();
+ break;
+ case 0x4837:
+ stopWalking();
+ break;
+ }
+ return 0;
+}
+
+KmScene2247::KmScene2247(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y)
+ : Klaymen(vm, parentScene, x, y) {
+
+ // Empty
+}
+
+void KmScene2247::xUpdate() {
+ setGlobalVar(V_KLAYMEN_FRAMEINDEX, _currFrameIndex);
+}
+
+uint32 KmScene2247::xHandleMessage(int messageNum, const MessageParam &param) {
+ switch (messageNum) {
+ case 0x4001:
+ case 0x4800:
+ startWalkToX(param.asPoint().x, false);
+ break;
+ case 0x4004:
+ GotoState(&Klaymen::stTryStandIdle);
+ break;
+ case 0x4804:
+ if (param.asInteger() != 0) {
+ _destX = param.asInteger();
+ GotoState(&Klaymen::stStartWalkingResume);
+ } else
+ GotoState(&Klaymen::stPeekWall);
+ break;
+ case 0x4817:
+ setDoDeltaX(param.asInteger());
+ gotoNextStateExt();
+ break;
+ case 0x481F:
+ if (param.asInteger() == 0)
+ GotoState(&Klaymen::stWonderAboutHalf);
+ else if (param.asInteger() == 1)
+ GotoState(&Klaymen::stWonderAboutAfter);
+ else if (param.asInteger() == 3)
+ GotoState(&Klaymen::stTurnToUseHalf);
+ else if (param.asInteger() == 4)
+ GotoState(&Klaymen::stTurnAwayFromUse);
+ else
+ GotoState(&Klaymen::stWonderAbout);
+ break;
+ case 0x482D:
+ setDoDeltaX(_x > (int16)param.asInteger() ? 1 : 0);
+ gotoNextStateExt();
+ break;
+ case 0x4837:
+ stopWalking();
+ break;
+ }
+ return 0;
+}
+
+KmScene2401::KmScene2401(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y)
+ : Klaymen(vm, parentScene, x, y) {
+
+ // Empty
+}
+
+uint32 KmScene2401::xHandleMessage(int messageNum, const MessageParam &param) {
+ uint32 messageResult = 0;
+ switch (messageNum) {
+ case 0x4001:
+ case 0x4800:
+ startWalkToX(param.asPoint().x, false);
+ break;
+ case 0x4004:
+ GotoState(&Klaymen::stTryStandIdle);
+ break;
+ case 0x4816:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stPressButton);
+ else if (param.asInteger() == 2)
+ GotoState(&Klaymen::stPressFloorButton);
+ else
+ GotoState(&Klaymen::stPressButtonSide);
+ break;
+ case 0x4817:
+ setDoDeltaX(param.asInteger());
+ gotoNextStateExt();
+ break;
+ case 0x481B:
+ if (param.asPoint().y != 0)
+ startWalkToXDistance(param.asPoint().y, param.asPoint().x);
+ else
+ startWalkToAttachedSpriteXDistance(param.asPoint().x);
+ break;
+ case 0x481F:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stTurnAwayFromUse);
+ else if (param.asInteger() == 0)
+ GotoState(&Klaymen::stTurnToUseHalf);
+ else
+ GotoState(&Klaymen::stWonderAbout);
+ break;
+ case 0x482D:
+ setDoDeltaX(_x > (int16)param.asInteger() ? 1 : 0);
+ gotoNextStateExt();
+ break;
+ case 0x482E:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stWalkToFrontNoStep);
+ else
+ GotoState(&Klaymen::stWalkToFront);
+ break;
+ case 0x482F:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stTurnToFront);
+ else
+ GotoState(&Klaymen::stTurnToBack);
+ break;
+ case 0x4832:
+ GotoState(&Klaymen::stUseTube);
+ break;
+ case 0x4833:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stWonderAbout);
+ else {
+ _spitPipeIndex = sendMessage(_parentScene, 0x2000, 0);
+ GotoState(&Klaymen::stTrySpitIntoPipe);
+ }
+ break;
+ case 0x483F:
+ startSpecialWalkRight(param.asInteger());
+ break;
+ case 0x4840:
+ startSpecialWalkLeft(param.asInteger());
+ break;
+ }
+ return messageResult;
+}
+
+KmScene2402::KmScene2402(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y)
+ : Klaymen(vm, parentScene, x, y) {
+
+ // Empty
+}
+
+uint32 KmScene2402::xHandleMessage(int messageNum, const MessageParam &param) {
+ uint32 messageResult = 0;
+ switch (messageNum) {
+ case 0x4001:
+ case 0x4800:
+ startWalkToX(param.asPoint().x, false);
+ break;
+ case 0x4004:
+ if (!getGlobalVar(V_TV_JOKE_TOLD))
+ GotoState(&Klaymen::stStandWonderAbout);
+ else
+ GotoState(&Klaymen::stTryStandIdle);
+ break;
+ case 0x4804:
+ if (param.asInteger() != 0) {
+ _destX = param.asInteger();
+ GotoState(&Klaymen::stWalkingFirst);
+ } else
+ GotoState(&Klaymen::stPeekWall);
+ break;
+ case 0x4812:
+ GotoState(&Klaymen::stPickUpGeneric);
+ break;
+ case 0x4816:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stPressButton);
+ else if (param.asInteger() == 2)
+ GotoState(&Klaymen::stPressFloorButton);
+ else
+ GotoState(&Klaymen::stPressButtonSide);
+ break;
+ case 0x4817:
+ setDoDeltaX(param.asInteger());
+ gotoNextStateExt();
+ break;
+ case 0x481B:
+ if (param.asPoint().y != 0)
+ startWalkToXDistance(param.asPoint().y, param.asPoint().x);
+ else
+ startWalkToAttachedSpriteXDistance(param.asPoint().x);
+ break;
+ case 0x481F:
+ if (param.asInteger() == 0)
+ GotoState(&Klaymen::stWonderAboutHalf);
+ else if (param.asInteger() == 1)
+ GotoState(&Klaymen::stWonderAboutAfter);
+ else if (param.asInteger() == 3)
+ GotoState(&Klaymen::stTurnToUseHalf);
+ else if (param.asInteger() == 4)
+ GotoState(&Klaymen::stTurnAwayFromUse);
+ else
+ GotoState(&Klaymen::stWonderAbout);
+ break;
+ case 0x483F:
+ startSpecialWalkRight(param.asInteger());
+ break;
+ case 0x4840:
+ startSpecialWalkLeft(param.asInteger());
+ break;
+ }
+ return messageResult;
+}
+
+KmScene2403::KmScene2403(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y)
+ : Klaymen(vm, parentScene, x, y) {
+
+ // Empty
+}
+
+uint32 KmScene2403::xHandleMessage(int messageNum, const MessageParam &param) {
+ uint32 messageResult = 0;
+ switch (messageNum) {
+ case 0x4001:
+ case 0x4800:
+ startWalkToX(param.asPoint().x, false);
+ break;
+ case 0x4004:
+ GotoState(&Klaymen::stTryStandIdle);
+ break;
+ case 0x480D:
+ GotoState(&Klaymen::stPullCord);
+ break;
+ case 0x4812:
+ GotoState(&Klaymen::stPickUpGeneric);
+ break;
+ case 0x4816:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stPressButton);
+ else if (param.asInteger() == 2)
+ GotoState(&Klaymen::stPressFloorButton);
+ else
+ GotoState(&Klaymen::stPressButtonSide);
+ break;
+ case 0x4817:
+ setDoDeltaX(param.asInteger());
+ gotoNextStateExt();
+ break;
+ case 0x481B:
+ if (param.asPoint().y != 0)
+ startWalkToXDistance(param.asPoint().y, param.asPoint().x);
+ else
+ startWalkToAttachedSpriteXDistance(param.asPoint().x);
+ break;
+ case 0x481F:
+ if (param.asInteger() == 0)
+ GotoState(&Klaymen::stWonderAboutHalf);
+ else if (param.asInteger() == 1)
+ GotoState(&Klaymen::stWonderAboutAfter);
+ else if (param.asInteger() == 3)
+ GotoState(&Klaymen::stTurnToUseHalf);
+ else if (param.asInteger() == 4)
+ GotoState(&Klaymen::stTurnAwayFromUse);
+ else
+ GotoState(&Klaymen::stWonderAbout);
+ break;
+ case 0x4820:
+ sendMessage(_parentScene, 0x2000, 0);
+ GotoState(&Klaymen::stContinueClimbLadderUp);
+ break;
+ case 0x4821:
+ sendMessage(_parentScene, 0x2000, 0);
+ _destY = param.asInteger();
+ GotoState(&Klaymen::stStartClimbLadderDown);
+ break;
+ case 0x4822:
+ sendMessage(_parentScene, 0x2000, 0);
+ _destY = param.asInteger();
+ GotoState(&Klaymen::stStartClimbLadderUp);
+ break;
+ case 0x4823:
+ sendMessage(_parentScene, 0x2001, 0);
+ GotoState(&Klaymen::stClimbLadderHalf);
+ break;
+ case 0x482D:
+ setDoDeltaX(_x > (int16)param.asInteger() ? 1 : 0);
+ gotoNextStateExt();
+ break;
+ case 0x483F:
+ startSpecialWalkRight(param.asInteger());
+ break;
+ case 0x4840:
+ startSpecialWalkLeft(param.asInteger());
+ break;
+ }
+ return messageResult;
+}
+
+KmScene2406::KmScene2406(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y, NRect *clipRects, int clipRectsCount)
+ : Klaymen(vm, parentScene, x, y) {
+
+ _surface->setClipRects(clipRects, clipRectsCount);
+}
+
+uint32 KmScene2406::xHandleMessage(int messageNum, const MessageParam &param) {
+ uint32 messageResult = 0;
+ switch (messageNum) {
+ case 0x4001:
+ case 0x4800:
+ startWalkToX(param.asPoint().x, false);
+ break;
+ case 0x4004:
+ GotoState(&Klaymen::stTryStandIdle);
+ break;
+ case 0x4804:
+ if (param.asInteger() != 0) {
+ _destX = param.asInteger();
+ GotoState(&Klaymen::stWalkingFirst);
+ } else
+ GotoState(&Klaymen::stPeekWall);
+ break;
+ case 0x4812:
+ if (param.asInteger() == 2)
+ GotoState(&Klaymen::stPickUpNeedle);
+ else if (param.asInteger() == 1)
+ GotoState(&Klaymen::stPickUpTube);
+ else
+ GotoState(&Klaymen::stPickUpGeneric);
+ break;
+ case 0x4817:
+ setDoDeltaX(param.asInteger());
+ gotoNextStateExt();
+ break;
+ case 0x481A:
+ GotoState(&Klaymen::stInsertDisk);
+ break;
+ case 0x481B:
+ if (param.asPoint().y != 0)
+ startWalkToXDistance(param.asPoint().y, param.asPoint().x);
+ else
+ startWalkToAttachedSpriteXDistance(param.asPoint().x);
+ break;
+ case 0x481D:
+ GotoState(&Klaymen::stTurnToUse);
+ break;
+ case 0x481E:
+ GotoState(&Klaymen::stReturnFromUse);
+ break;
+ case 0x481F:
+ if (param.asInteger() == 0)
+ GotoState(&Klaymen::stWonderAboutHalf);
+ else if (param.asInteger() == 1)
+ GotoState(&Klaymen::stWonderAboutAfter);
+ else if (param.asInteger() == 3)
+ GotoState(&Klaymen::stTurnToUseHalf);
+ else if (param.asInteger() == 4)
+ GotoState(&Klaymen::stTurnAwayFromUse);
+ else
+ GotoState(&Klaymen::stWonderAbout);
+ break;
+ case 0x4820:
+ sendMessage(_parentScene, 0x2000, 0);
+ GotoState(&Klaymen::stContinueClimbLadderUp);
+ break;
+ case 0x4821:
+ sendMessage(_parentScene, 0x2000, 0);
+ _destY = param.asInteger();
+ GotoState(&Klaymen::stStartClimbLadderDown);
+ break;
+ case 0x4822:
+ sendMessage(_parentScene, 0x2000, 0);
+ _destY = param.asInteger();
+ GotoState(&Klaymen::stStartClimbLadderUp);
+ break;
+ case 0x4823:
+ sendMessage(_parentScene, 0x2001, 0);
+ GotoState(&Klaymen::stClimbLadderHalf);
+ break;
+ case 0x483F:
+ startSpecialWalkRight(param.asInteger());
+ break;
+ case 0x4840:
+ startSpecialWalkLeft(param.asInteger());
+ break;
+ }
+ return messageResult;
+}
+
+KmScene2501::KmScene2501(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y)
+ : Klaymen(vm, parentScene, x, y) {
+
+ // Empty
+}
+
+uint32 KmScene2501::xHandleMessage(int messageNum, const MessageParam &param) {
+ uint32 messageResult = 0;
+ switch (messageNum) {
+ case 0x2000:
+ _isSittingInTeleporter = param.asInteger() != 0;
+ messageResult = 1;
+ break;
+ case 0x4001:
+ case 0x4800:
+ startWalkToX(param.asPoint().x, false);
+ break;
+ case 0x4004:
+ if (_isSittingInTeleporter)
+ GotoState(&Klaymen::stSitIdleTeleporter);
+ else
+ GotoState(&Klaymen::stTryStandIdle);
+ break;
+ case 0x4817:
+ setDoDeltaX(param.asInteger());
+ gotoNextStateExt();
+ break;
+ case 0x481D:
+ if (_isSittingInTeleporter)
+ GotoState(&Klaymen::stTurnToUseInTeleporter);
+ break;
+ case 0x481E:
+ if (_isSittingInTeleporter)
+ GotoState(&Klaymen::stReturnFromUseInTeleporter);
+ break;
+ case 0x4834:
+ GotoState(&Klaymen::stStepOver);
+ break;
+ case 0x4835:
+ sendMessage(_parentScene, 0x2000, 1);
+ _isSittingInTeleporter = true;
+ GotoState(&Klaymen::stSitInTeleporter);
+ break;
+ case 0x4836:
+ sendMessage(_parentScene, 0x2000, 0);
+ _isSittingInTeleporter = false;
+ GotoState(&Klaymen::stGetUpFromTeleporter);
+ break;
+ }
+ return messageResult;
+}
+
+KmScene2732::KmScene2732(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y)
+ : Klaymen(vm, parentScene, x, y) {
+
+ // Empty
+}
+
+uint32 KmScene2732::xHandleMessage(int messageNum, const MessageParam &param) {
+ switch (messageNum) {
+ case 0x4804:
+ GotoState(&Klaymen::stPeekInside);
+ break;
+ case 0x483C:
+ GotoState(&Klaymen::stPeekInsideReturn);
+ break;
+ }
+ return 0;
+}
+
+KmScene2801::KmScene2801(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y)
+ : Klaymen(vm, parentScene, x, y) {
+
+ // Empty
+}
+
+uint32 KmScene2801::xHandleMessage(int messageNum, const MessageParam &param) {
+ switch (messageNum) {
+ case 0x4001:
+ case 0x4800:
+ startWalkToX(param.asPoint().x, false);
+ break;
+ case 0x4004:
+ GotoState(&Klaymen::stTryStandIdle);
+ break;
+ case 0x4812:
+ GotoState(&Klaymen::stPickUpGeneric);
+ break;
+ case 0x4817:
+ setDoDeltaX(param.asInteger());
+ gotoNextStateExt();
+ break;
+ case 0x481B:
+ if (param.asPoint().y != 0)
+ startWalkToXDistance(param.asPoint().y, param.asPoint().x);
+ else
+ startWalkToAttachedSpriteXDistance(param.asPoint().x);
+ break;
+ case 0x481D:
+ GotoState(&Klaymen::stTurnToUse);
+ break;
+ case 0x481E:
+ GotoState(&Klaymen::stReturnFromUse);
+ break;
+ case 0x481F:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stWonderAboutAfter);
+ else if (param.asInteger() == 0)
+ GotoState(&Klaymen::stWonderAboutHalf);
+ else if (param.asInteger() == 4)
+ GotoState(&Klaymen::stTurnAwayFromUse);
+ else if (param.asInteger() == 3)
+ GotoState(&Klaymen::stTurnToUseHalf);
+ else
+ GotoState(&Klaymen::stWonderAbout);
+ break;
+ case 0x482D:
+ setDoDeltaX(_x > (int16)param.asInteger() ? 1 : 0);
+ gotoNextStateExt();
+ break;
+ case 0x482E:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stWalkToFrontNoStep);
+ else
+ GotoState(&Klaymen::stWalkToFront);
+ break;
+ case 0x482F:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stTurnToFront);
+ else
+ GotoState(&Klaymen::stTurnToBack);
+ break;
+ case 0x4837:
+ stopWalking();
+ break;
+ }
+ return 0;
+}
+
+KmScene2803::KmScene2803(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y, NRect *clipRects, int clipRectsCount)
+ : Klaymen(vm, parentScene, x, y) {
+
+ _surface->setClipRects(clipRects, clipRectsCount);
+ _dataResource.load(0x00900849);
+}
+
+uint32 KmScene2803::xHandleMessage(int messageNum, const MessageParam &param) {
+ switch (messageNum) {
+ case 0x4001:
+ case 0x4800:
+ startWalkToX(param.asPoint().x, false);
+ break;
+ case 0x4004:
+ GotoState(&Klaymen::stTryStandIdle);
+ break;
+ case 0x4803:
+ _destY = param.asInteger();
+ GotoState(&Klaymen::stJumpToGrab);
+ break;
+ case 0x4804:
+ if (param.asInteger() == 3)
+ GotoState(&Klaymen::stFinishGrow);
+ break;
+ case 0x480D:
+ GotoState(&Klaymen::stPullCord);
+ break;
+ case 0x4817:
+ setDoDeltaX(param.asInteger());
+ gotoNextStateExt();
+ break;
+ case 0x4818:
+ startWalkToX(_dataResource.getPoint(param.asInteger()).x, false);
+ break;
+ case 0x481D:
+ GotoState(&Klaymen::stTurnToUse);
+ break;
+ case 0x481E:
+ GotoState(&Klaymen::stReturnFromUse);
+ break;
+ case 0x481F:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stWonderAboutAfter);
+ else
+ GotoState(&Klaymen::stWonderAboutHalf);
+ break;
+ case 0x482E:
+ GotoState(&Klaymen::stWalkToFront);
+ break;
+ case 0x482F:
+ GotoState(&Klaymen::stTurnToBack);
+ break;
+ case 0x4834:
+ GotoState(&Klaymen::stStepOver);
+ break;
+ case 0x4838:
+ GotoState(&Klaymen::stJumpToGrabRelease);
+ break;
+ }
+ return 0;
+}
+
+KmScene2803Small::KmScene2803Small(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y)
+ : Klaymen(vm, parentScene, x, y) {
+
+ _dataResource.load(0x81120132);
+}
+
+uint32 KmScene2803Small::xHandleMessage(int messageNum, const MessageParam &param) {
+ switch (messageNum) {
+ case 0x4001:
+ case 0x4800:
+ startWalkToXSmall(param.asPoint().x);
+ break;
+ case 0x4004:
+ GotoState(&Klaymen::stStandIdleSmall);
+ break;
+ case 0x4817:
+ setDoDeltaX(param.asInteger());
+ gotoNextStateExt();
+ break;
+ case 0x4818:
+ startWalkToXSmall(_dataResource.getPoint(param.asInteger()).x);
+ break;
+ case 0x481F:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stWonderAboutAfterSmall);
+ else if (param.asInteger() == 0)
+ GotoState(&Klaymen::stWonderAboutHalfSmall);
+ else
+ GotoState(&Klaymen::stWonderAboutSmall);
+ break;
+ case 0x482E:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stWalkToFrontNoStepSmall);
+ else if (param.asInteger() == 2)
+ GotoState(&Klaymen::stWalkToFront2Small);
+ else
+ GotoState(&Klaymen::stWalkToFrontSmall);
+ break;
+ case 0x482F:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stTurnToBackHalfSmall);
+ else if (param.asInteger() == 2)
+ GotoState(&Klaymen::stTurnToBackWalkSmall);
+ else
+ GotoState(&Klaymen::stTurnToBackSmall);
+ break;
+ case 0x4830:
+ GotoState(&Klaymen::stShrink);
+ break;
+ }
+ return 0;
+}
+
+KmScene2805::KmScene2805(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y)
+ : Klaymen(vm, parentScene, x, y) {
+
+ // Empty
+}
+
+uint32 KmScene2805::xHandleMessage(int messageNum, const MessageParam &param) {
+ uint32 messageResult = 0;
+ switch (messageNum) {
+ case 0x2000:
+ _isSittingInTeleporter = param.asInteger() != 0;
+ messageResult = 1;
+ break;
+ case 0x4001:
+ case 0x4800:
+ startWalkToX(param.asPoint().x, false);
+ break;
+ case 0x4004:
+ if (_isSittingInTeleporter)
+ GotoState(&Klaymen::stSitIdleTeleporter);
+ else
+ GotoState(&Klaymen::stTryStandIdle);
+ break;
+ case 0x4817:
+ setDoDeltaX(param.asInteger());
+ gotoNextStateExt();
+ break;
+ case 0x481D:
+ if (_isSittingInTeleporter)
+ GotoState(&Klaymen::stTurnToUseInTeleporter);
+ break;
+ case 0x481E:
+ if (_isSittingInTeleporter)
+ GotoState(&Klaymen::stReturnFromUseInTeleporter);
+ break;
+ case 0x4834:
+ GotoState(&Klaymen::stStepOver);
+ break;
+ case 0x4835:
+ sendMessage(_parentScene, 0x2000, 1);
+ _isSittingInTeleporter = true;
+ GotoState(&Klaymen::stSitInTeleporter);
+ break;
+ case 0x4836:
+ sendMessage(_parentScene, 0x2000, 0);
+ _isSittingInTeleporter = false;
+ GotoState(&Klaymen::stGetUpFromTeleporter);
+ break;
+ case 0x483D:
+ teleporterAppear(0xDE284B74);
+ break;
+ case 0x483E:
+ teleporterDisappear(0xD82A4094);
+ break;
+ }
+ return messageResult;
+}
+
+KmScene2806::KmScene2806(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y,
+ bool needsLargeSurface, NRect *clipRects, uint clipRectsCount)
+ : Klaymen(vm, parentScene, x, y) {
+
+ if (needsLargeSurface) {
+ NDimensions dimensions = _animResource.loadSpriteDimensions(0x2838C010);
+ delete _surface;
+ createSurface(1000, dimensions.width, dimensions.height);
+ loadSound(3, 0x58E0C341);
+ loadSound(4, 0x40A00342);
+ loadSound(5, 0xD0A1C348);
+ loadSound(6, 0x166FC6E0);
+ loadSound(7, 0x00018040);
+ }
+
+ _dataResource.load(0x98182003);
+ _surface->setClipRects(clipRects, clipRectsCount);
+}
+
+uint32 KmScene2806::xHandleMessage(int messageNum, const MessageParam &param) {
+ switch (messageNum) {
+ case 0x4001:
+ case 0x4800:
+ startWalkToX(param.asPoint().x, false);
+ break;
+ case 0x4004:
+ GotoState(&Klaymen::stTryStandIdle);
+ break;
+ case 0x4804:
+ startWalkToX(440, true);
+ break;
+ case 0x480D:
+ GotoState(&Klaymen::stPullCord);
+ break;
+ case 0x4816:
+ if (param.asInteger() == 0)
+ GotoState(&Klaymen::stPressButtonSide);
+ break;
+ case 0x4817:
+ setDoDeltaX(param.asInteger());
+ gotoNextStateExt();
+ break;
+ case 0x4818:
+ startWalkToX(_dataResource.getPoint(param.asInteger()).x, false);
+ break;
+ case 0x4831:
+ GotoState(&Klaymen::stGrow);
+ break;
+ case 0x4832:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stDrinkPotion);
+ else
+ GotoState(&Klaymen::stUseTube);
+ break;
+ }
+ return 0;
+}
+
+KmScene2809::KmScene2809(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y,
+ bool needsLargeSurface, NRect *clipRects, uint clipRectsCount)
+ : Klaymen(vm, parentScene, x, y) {
+
+ if (needsLargeSurface) {
+ NDimensions dimensions = _animResource.loadSpriteDimensions(0x2838C010);
+ delete _surface;
+ createSurface(1000, dimensions.width, dimensions.height);
+ loadSound(3, 0x58E0C341);
+ loadSound(4, 0x40A00342);
+ loadSound(5, 0xD0A1C348);
+ loadSound(6, 0x166FC6E0);
+ loadSound(7, 0x00018040);
+ }
+
+ _dataResource.load(0x1830009A);
+ _surface->setClipRects(clipRects, clipRectsCount);
+}
+
+uint32 KmScene2809::xHandleMessage(int messageNum, const MessageParam &param) {
+ switch (messageNum) {
+ case 0x4001:
+ case 0x4800:
+ startWalkToX(param.asPoint().x, false);
+ break;
+ case 0x4004:
+ GotoState(&Klaymen::stTryStandIdle);
+ break;
+ case 0x4804:
+ startWalkToX(226, true);
+ break;
+ case 0x480D:
+ GotoState(&Klaymen::stPullCord);
+ break;
+ case 0x4816:
+ if (param.asInteger() == 0)
+ GotoState(&Klaymen::stPressButtonSide);
+ break;
+ case 0x4817:
+ setDoDeltaX(param.asInteger());
+ gotoNextStateExt();
+ break;
+ case 0x4818:
+ startWalkToX(_dataResource.getPoint(param.asInteger()).x, false);
+ break;
+ case 0x4831:
+ GotoState(&Klaymen::stGrow);
+ break;
+ case 0x4832:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stDrinkPotion);
+ else
+ GotoState(&Klaymen::stUseTube);
+ break;
+ }
+ return 0;
+}
+
+KmScene2810Small::KmScene2810Small(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y)
+ : Klaymen(vm, parentScene, x, y) {
+
+ // Empty
+}
+
+uint32 KmScene2810Small::xHandleMessage(int messageNum, const MessageParam &param) {
+ switch (messageNum) {
+ case 0x4001:
+ case 0x4800:
+ startWalkToXSmall(param.asPoint().x);
+ break;
+ case 0x4004:
+ GotoState(&Klaymen::stStandIdleSmall);
+ break;
+ case 0x4817:
+ setDoDeltaX(param.asInteger());
+ gotoNextStateExt();
+ break;
+ case 0x4818:
+ startWalkToXSmall(_dataResource.getPoint(param.asInteger()).x);
+ break;
+ case 0x481F:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stWonderAboutAfterSmall);
+ else if (param.asInteger() == 0)
+ GotoState(&Klaymen::stWonderAboutHalfSmall);
+ else
+ GotoState(&Klaymen::stWonderAboutSmall);
+ break;
+ case 0x482E:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stWalkToFrontNoStepSmall);
+ else
+ GotoState(&Klaymen::stWalkToFrontSmall);
+ break;
+ case 0x482F:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stTurnToBackHalfSmall);
+ else
+ GotoState(&Klaymen::stTurnToBackSmall);
+ break;
+ case 0x4837:
+ stopWalking();
+ break;
+ }
+ return 0;
+}
+
+KmScene2810::KmScene2810(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y, NRect *clipRects, uint clipRectsCount)
+ : Klaymen(vm, parentScene, x, y) {
+
+ _surface->setClipRects(clipRects, clipRectsCount);
+}
+
+uint32 KmScene2810::xHandleMessage(int messageNum, const MessageParam &param) {
+ switch (messageNum) {
+ case 0x4001:
+ case 0x4800:
+ startWalkToX(param.asPoint().x, false);
+ break;
+ case 0x4004:
+ GotoState(&Klaymen::stTryStandIdle);
+ break;
+ case 0x4803:
+ _destY = param.asInteger();
+ GotoState(&Klaymen::stJumpToGrab);
+ break;
+ case 0x4804:
+ if (param.asInteger() == 3)
+ GotoState(&Klaymen::stFinishGrow);
+ break;
+ case 0x4812:
+ GotoState(&Klaymen::stPickUpGeneric);
+ break;
+ case 0x4817:
+ setDoDeltaX(param.asInteger());
+ gotoNextStateExt();
+ break;
+ case 0x4818:
+ startWalkToX(_dataResource.getPoint(param.asInteger()).x, false);
+ break;
+ case 0x481B:
+ if (param.asPoint().y != 0)
+ startWalkToXDistance(param.asPoint().y, param.asPoint().x);
+ else
+ startWalkToAttachedSpriteXDistance(param.asPoint().x);
+ break;
+ case 0x481F:
+ if (param.asInteger() == 0)
+ GotoState(&Klaymen::stWonderAboutHalf);
+ else if (param.asInteger() == 1)
+ GotoState(&Klaymen::stWonderAboutAfter);
+ else if (param.asInteger() == 3)
+ GotoState(&Klaymen::stTurnToUseHalf);
+ else if (param.asInteger() == 4)
+ GotoState(&Klaymen::stTurnAwayFromUse);
+ else if (param.asInteger() == 5)
+ GotoState(&Klaymen::stTurnToUseExt);
+ else
+ GotoState(&Klaymen::stWonderAbout);
+ break;
+ case 0x4820:
+ sendMessage(_parentScene, 0x2000, 0);
+ GotoState(&Klaymen::stContinueClimbLadderUp);
+ break;
+ case 0x4821:
+ sendMessage(_parentScene, 0x2000, 0);
+ _destY = param.asInteger();
+ GotoState(&Klaymen::stStartClimbLadderDown);
+ break;
+ case 0x4822:
+ sendMessage(_parentScene, 0x2000, 0);
+ _destY = param.asInteger();
+ GotoState(&Klaymen::stStartClimbLadderUp);
+ break;
+ case 0x4823:
+ sendMessage(_parentScene, 0x2001, 0);
+ GotoState(&Klaymen::stClimbLadderHalf);
+ break;
+ case 0x4824:
+ sendMessage(_parentScene, 0x2000, 0);
+ _destY = _dataResource.getPoint(param.asInteger()).y;
+ GotoState(&Klaymen::stStartClimbLadderDown);
+ break;
+ case 0x4825:
+ sendMessage(_parentScene, 0x2000, 0);
+ _destY = _dataResource.getPoint(param.asInteger()).y;
+ GotoState(&Klaymen::stStartClimbLadderUp);
+ break;
+ case 0x482D:
+ setDoDeltaX(_x > (int16)param.asInteger() ? 1 : 0);
+ gotoNextStateExt();
+ break;
+ case 0x4837:
+ stopWalking();
+ break;
+ }
+ return 0;
+}
+
+KmScene2812::KmScene2812(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y)
+ : Klaymen(vm, parentScene, x, y) {
+
+ // Empty
+}
+
+uint32 KmScene2812::xHandleMessage(int messageNum, const MessageParam &param) {
+ switch (messageNum) {
+ case 0x4001:
+ case 0x4800:
+ startWalkToX(param.asPoint().x, false);
+ break;
+ case 0x4004:
+ GotoState(&Klaymen::stTryStandIdle);
+ break;
+ case 0x4805:
+ _destY = param.asInteger();
+ GotoState(&Klaymen::stJumpToGrabFall);
+ break;
+ case 0x4812:
+ if (param.asInteger() == 2)
+ GotoState(&Klaymen::stPickUpNeedle);
+ else if (param.asInteger() == 1)
+ GotoState(&Klaymen::stPickUpTube);
+ else
+ GotoState(&Klaymen::stPickUpGeneric);
+ break;
+ case 0x4817:
+ setDoDeltaX(param.asInteger());
+ gotoNextStateExt();
+ break;
+ case 0x481A:
+ GotoState(&Klaymen::stInsertDisk);
+ break;
+ case 0x481B:
+ if (param.asPoint().y != 0)
+ startWalkToXDistance(param.asPoint().y, param.asPoint().x);
+ else
+ startWalkToAttachedSpriteXDistance(param.asPoint().x);
+ break;
+ case 0x481D:
+ GotoState(&Klaymen::stTurnToUse);
+ break;
+ case 0x481E:
+ GotoState(&Klaymen::stReturnFromUse);
+ break;
+ case 0x4820:
+ sendMessage(_parentScene, 0x2001, 0);
+ GotoState(&Klaymen::stContinueClimbLadderUp);
+ break;
+ case 0x4821:
+ sendMessage(_parentScene, 0x2001, 0);
+ _destY = param.asInteger();
+ GotoState(&Klaymen::stStartClimbLadderDown);
+ break;
+ case 0x4822:
+ sendMessage(_parentScene, 0x2001, 0);
+ _destY = param.asInteger();
+ GotoState(&Klaymen::stStartClimbLadderUp);
+ break;
+ case 0x4823:
+ sendMessage(_parentScene, 0x2002, 0);
+ GotoState(&Klaymen::stClimbLadderHalf);
+ break;
+ case 0x482D:
+ setDoDeltaX(_x > (int16)param.asInteger() ? 1 : 0);
+ gotoNextStateExt();
+ break;
+ case 0x482E:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stWalkToFrontNoStep);
+ else
+ GotoState(&Klaymen::stWalkToFront);
+ break;
+ case 0x482F:
+ if (param.asInteger() == 1)
+ GotoState(&Klaymen::stTurnToFront);
+ else
+ GotoState(&Klaymen::stTurnToBack);
+ break;
+ case 0x483F:
+ startSpecialWalkRight(param.asInteger());
+ break;
+ case 0x4840:
+ startSpecialWalkLeft(param.asInteger());
+ break;
+ }
+ return 0;
+}
+
+} // End of namespace Neverhood
diff --git a/engines/neverhood/klaymen.h b/engines/neverhood/klaymen.h
new file mode 100644
index 0000000000..25443b5a35
--- /dev/null
+++ b/engines/neverhood/klaymen.h
@@ -0,0 +1,771 @@
+/* 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_KLAYMEN_H
+#define NEVERHOOD_KLAYMEN_H
+
+#include "neverhood/neverhood.h"
+#include "neverhood/sprite.h"
+#include "neverhood/graphics.h"
+#include "neverhood/resource.h"
+
+namespace Neverhood {
+
+// TODO This code is horrible and weird and a lot of stuff needs renaming once a better name is found
+// TODO Also the methods should probably rearranged and be grouped together more consistently
+
+class Klaymen;
+class Scene;
+
+const uint32 kKlaymenSpeedUpHash = 0x004A2148;
+
+#include "common/pack-start.h" // START STRUCT PACKING
+
+struct KlaymenIdleTableItem {
+ int weight;
+ uint idleAnimation;
+};
+
+#include "common/pack-end.h" // END STRUCT PACKING
+
+enum {
+ kIdlePickEar,
+ kIdleSpinHead,
+ kIdleArms,
+ kIdleChest,
+ kIdleHeadOff,
+ kIdleTeleporterHands,
+ kIdleTeleporterHands2,
+ kIdleWonderAbout
+};
+
+class Klaymen : public AnimatedSprite {
+public:
+ Klaymen(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y, NRectArray *clipRects = NULL);
+
+ void update();
+
+ void startIdleAnimation(uint32 fileHash, AnimationCb callback);
+ void upIdleAnimation();
+
+ void stIdlePickEar();
+ void evIdlePickEarDone();
+ uint32 hmIdlePickEar(int messageNum, const MessageParam &param, Entity *sender);
+
+ void stIdleSpinHead();
+ uint32 hmIdleSpinHead(int messageNum, const MessageParam &param, Entity *sender);
+
+ void stIdleArms();
+ void evIdleArmsDone();
+ uint32 hmIdleArms(int messageNum, const MessageParam &param, Entity *sender);
+
+ void stIdleChest();
+ uint32 hmIdleChest(int messageNum, const MessageParam &param, Entity *sender);
+
+ void stIdleHeadOff();
+ uint32 hmIdleHeadOff(int messageNum, const MessageParam &param, Entity *sender);
+
+ void stIdleWonderAbout();
+
+ void stIdleTeleporterHands();
+
+ void stIdleTeleporterHands2();
+
+ void stTryStandIdle();
+ void stStandAround();
+ void upStandIdle();
+ void stIdleBlink();
+
+ bool stStartAction(AnimationCb callback3);
+ bool stStartActionFromIdle(AnimationCb callback);
+ uint32 hmStartAction(int messageNum, const MessageParam &param, Entity *sender);
+
+ void stSneak();
+ uint32 hmSneaking(int messageNum, const MessageParam &param, Entity *sender);
+ void suSneaking();
+ void evSneakingDone();
+
+ void stStartWalking();
+ void stStartWalkingExt();
+ void stWalkingOpenDoor();
+ void suWalkingOpenDoor();
+ void stStepOver();
+ uint32 hmStartWalking(int messageNum, const MessageParam &param, Entity *sender);
+ void evStartWalkingDone();
+
+ void stWalkingFirst();
+ void stWalkingFirstExt();
+ void stStartWalkingResume();
+ void stUpdateWalkingFirst();
+ uint32 hmWalking(int messageNum, const MessageParam &param, Entity *sender);
+ void suWalkingFirst();
+
+ void stWalkToFrontNoStep();
+ void stWalkToFront();
+ void stTurnToFront();
+ void stTurnToBack();
+ uint32 hmWalkToFront(int messageNum, const MessageParam &param, Entity *sender);
+
+ void stTurnToBackToUse();
+ uint32 hmTurnToBackToUse(int messageNum, const MessageParam &param, Entity *sender);
+
+ void stPickUpGeneric();
+ void stPickUpNeedle();
+ uint32 hmPickUpObject(int messageNum, const MessageParam &param, Entity *sender);
+
+ void stPickUpTube();
+ uint32 hmPickUpTube(int messageNum, const MessageParam &param, Entity *sender);
+
+ void stTurnToUse();
+ void stTurnToUseHalf();
+ void stTurnAwayFromUse();
+ void stReturnFromUse();
+ void stTurnToUseExt();
+ uint32 hmTurnToUse(int messageNum, const MessageParam &param, Entity *sender);
+
+ void stLargeStep();
+ uint32 hmLargeStep(int messageNum, const MessageParam &param, Entity *sender);
+ void suLargeStep();
+ void evLargeStepDone();
+
+ void stInsertKey();
+ uint32 hmInsertKey(int messageNum, const MessageParam &param, Entity *sender);
+
+ void stReadNote();
+ uint32 hmReadNote(int messageNum, const MessageParam &param, Entity *sender);
+
+ void stHitByDoor();
+ uint32 hmHitByDoor(int messageNum, const MessageParam &param, Entity *sender);
+
+ void stPeekWall();
+ uint32 hmPeekWall(int messageNum, const MessageParam &param, Entity *sender);
+
+ void stPeekWallReturn();
+ uint32 hmPeekWallReturn(int messageNum, const MessageParam &param, Entity *sender);
+
+ void stPeekWallBlink();
+ void upPeekWallBlink();
+
+ void stPeekWall1();
+
+ void stPeekWall2();
+
+ void stPullHammerLever();
+ uint32 hmPullHammerLever(int messageNum, const MessageParam &param, Entity *sender);
+
+ void stRidePlatformDown();
+ void suRidePlatformDown();
+
+ void stCrashDown();
+ void stCrashDownFinished();
+
+ void stShrink();
+ uint32 hmShrink(int messageNum, const MessageParam &param, Entity *sender);
+
+ void stGrow();
+ uint32 hmGrow(int messageNum, const MessageParam &param, Entity *sender);
+
+ void stDrinkPotion();
+ uint32 hmDrinkPotion(int messageNum, const MessageParam &param, Entity *sender);
+
+ void stPullCord();
+ void stReleaseCord();
+ uint32 hmPullReleaseCord(int messageNum, const MessageParam &param, Entity *sender);
+
+ void stUseTube();
+ uint32 hmUseTube(int messageNum, const MessageParam &param, Entity *sender);
+
+ void stUseLever();
+ void stUseLeverRelease();
+ void stReleaseLever();
+ void stInteractLever();
+ uint32 hmLever(int messageNum, const MessageParam &param, Entity *sender);
+
+ void stLetGoOfLever();
+ void evLeverReleasedEvent();
+
+ void stWakeUp();
+
+ void stSleeping();
+ uint32 hmSleeping(int messageNum, const MessageParam &param, Entity *sender);
+
+ void stPressButton();
+ void stPressFloorButton();
+ void stPressButtonSide();
+ uint32 hmPressButton(int messageNum, const MessageParam &param, Entity *sender);
+
+ void stWonderAbout();
+ void stWonderAboutHalf();
+ void stWonderAboutAfter();
+
+ void stStandWonderAbout();
+
+ void stStartClimbLadderUp();
+ void stStartClimbLadderDown();
+ uint32 hmClimbLadderUpDown(int messageNum, const MessageParam &param, Entity *sender);
+
+ void stContinueClimbLadderUp();
+
+ void stClimbLadderHalf();
+ uint32 hmClimbLadderHalf(int messageNum, const MessageParam &param, Entity *sender);
+
+ void setupJumpToRing();
+ void stJumpToRing1();
+ void stJumpToRing2();
+ void stJumpToRing4();
+ uint32 hmJumpToRing(int messageNum, const MessageParam &param, Entity *sender);
+
+ void stHangOnRing();
+
+ void stJumpToRing3();
+ uint32 hmJumpToRing3(int messageNum, const MessageParam &param, Entity *sender);
+
+ void stHoldRing3();
+ uint32 hmHoldRing3(int messageNum, const MessageParam &param, Entity *sender);
+
+ void stReleaseRing();
+
+ void stLandOnFeet();
+ uint32 hmLandOnFeet(int messageNum, const MessageParam &param, Entity *sender);
+
+ void stPullLeverDown();
+ void stHoldLeverDown();
+
+ void stInsertDisk();
+ uint32 hmInsertDisk(int messageNum, const MessageParam &param, Entity *sender);
+
+ void stMoveObjectSkipTurnFaceObject();
+ void stMoveObjectSkipTurn();
+ void stMoveObjectFaceObject();
+ uint32 hmMoveObjectTurn(int messageNum, const MessageParam &param, Entity *sender);
+ void evMoveObjectTurnDone();
+
+ void stJumpToGrab();
+ void stJumpToGrabFall();
+ uint32 hmJumpToGrab(int messageNum, const MessageParam &param, Entity *sender);
+ void suJumpToGrab();
+
+ void stJumpToGrabRelease();
+ uint32 hmJumpToGrabRelease(int messageNum, const MessageParam &param, Entity *sender);
+
+ void stSitInTeleporter();
+ uint32 hmSitInTeleporter(int messageNum, const MessageParam &param, Entity *sender);
+
+ void stSitIdleTeleporter();
+ void upSitIdleTeleporter();
+
+ void stSitIdleTeleporterBlink();
+
+ void stSitIdleTeleporterBlinkSecond();
+
+ void stTurnToUseInTeleporter();
+
+ void stReturnFromUseInTeleporter();
+
+ void stGetUpFromTeleporter();
+
+ void teleporterAppear(uint32 fileHash);
+ void teleporterDisappear(uint32 fileHash);
+ uint32 hmTeleporterAppearDisappear(int messageNum, const MessageParam &param, Entity *sender);
+
+ void stClayDoorOpen();
+ uint32 hmClayDoorOpen(int messageNum, const MessageParam &param, Entity *sender);
+
+ void stFallSkipJump();
+ void suFallSkipJump();
+
+ void stMoveObject();
+ void stContinueMoveObject();
+ uint32 hmMoveObject(int messageNum, const MessageParam &param, Entity *sender);
+ void upMoveObject();
+
+ void stCloseEyes();
+
+ void stTumbleHeadless();
+ uint32 hmTumbleHeadless(int messageNum, const MessageParam &param, Entity *sender);
+
+ void stFetchMatch();
+ void stLightMatch();
+ uint32 hmMatch(int messageNum, const MessageParam &param, Entity *sender);
+
+ void stHitByBoxingGlove();
+ uint32 hmHitByBoxingGlove(int messageNum, const MessageParam &param, Entity *sender);
+ void evHitByBoxingGloveDone();
+
+ void stStandIdleSmall();
+ void stWonderAboutSmall();
+ void stWonderAboutHalfSmall();
+ void stWonderAboutAfterSmall();
+
+ void stWalkToFrontNoStepSmall();
+ void stWalkToFrontSmall();
+ void stWalkToFront2Small();
+ void stTurnToBackHalfSmall();
+ void stTurnToBackWalkSmall();
+ void stTurnToBackSmall();
+ uint32 hmWalkFrontBackSmall(int messageNum, const MessageParam &param, Entity *sender);
+
+ void stFinishGrow();
+ uint32 hmFinishGrow(int messageNum, const MessageParam &param, Entity *sender);
+
+ void stJumpToRingVenusFlyTrap();
+ uint32 hmJumpToRingVenusFlyTrap(int messageNum, const MessageParam &param, Entity *sender);
+
+ void stDropFromRing();
+
+ void stStandIdleSpecial();
+ uint32 hmStandIdleSpecial(int messageNum, const MessageParam &param, Entity *sender);
+
+ void stPressDoorButton();
+ uint32 hmPressDoorButton(int messageNum, const MessageParam &param, Entity *sender);
+
+ void stSpitOutFall0();
+ void stSpitOutFall2();
+ void suFallDown();
+ void upSpitOutFall();
+
+ void stJumpAndFall();
+ uint32 hmJumpAndFall(int messageNum, const MessageParam &param, Entity *sender);
+
+ void stFalling();
+ void stFallTouchdown();
+
+ void stMoveVenusFlyTrap();
+ void stContinueMovingVenusFlyTrap();
+ uint32 hmMoveVenusFlyTrap(int messageNum, const MessageParam &param, Entity *sender);
+ uint32 hmFirstMoveVenusFlyTrap(int messageNum, const MessageParam &param, Entity *sender);
+ void evMoveVenusFlyTrapDone();
+
+ void stPeekInside();
+ void stPeekInsideReturn();
+ void stPeekInsideBlink();
+ void upPeekInsideBlink();
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ void stopWalking();
+
+ void suAction();
+ void suUpdateDestX();
+ void suWalkingTestExit();
+
+ uint32 hmLowLevel(int messageNum, const MessageParam &param, Entity *sender);
+ uint32 hmLowLevelAnimation(int messageNum, const MessageParam &param, Entity *sender);
+
+ void setKlaymenIdleTable(const KlaymenIdleTableItem *table, uint tableCount);
+ void setKlaymenIdleTable1();
+ void setKlaymenIdleTable2();
+ void setKlaymenIdleTable3();
+
+ void setSoundFlag(bool value) { _soundFlag = value; }
+
+ void spitIntoPipe();
+ void stTrySpitIntoPipe();
+ void stContSpitIntoPipe();
+ uint32 hmSpit(int messageNum, const MessageParam &param, Entity *sender);
+
+ void stRidePlatform();
+ void suRidePlatform();
+ void stPullLever();
+ void stLookLeverDown();
+ void stWaitLeverDown();
+
+protected:
+ Scene *_parentScene;
+ Sprite *_attachedSprite;
+ int _ladderStatus;
+ bool _isWalking;
+ bool _isSneaking;
+ bool _isLargeStep;
+ bool _isMoveObjectRequested;
+ bool _acceptInput;
+ int16 _destX, _destY;
+ int16 _idleCounter, _idleCounterMax;
+ int16 _blinkCounter, _blinkCounterMax;
+ int16 _tapesToInsert, _keysToInsert;
+ bool _doYHitIncr;
+ bool _isLeverDown;
+ bool _isWalkingOpenDoorNotified;
+ int _busyStatus;
+ bool _actionStatusChanged;
+ int _actionStatus;
+
+ const KlaymenIdleTableItem *_idleTable;
+ uint _idleTableCount;
+ int _idleTableTotalWeight;
+ int _idleTableNum;
+
+ NPointArray *_pathPoints;
+ bool _soundFlag;
+
+ int16 _spitOutCountdown;
+
+ bool _isSittingInTeleporter;
+
+ bool _potionFlag1;
+ bool _potionFlag2;
+
+ int16 _platformDeltaY;
+
+ Sprite *_otherSprite;
+
+ int16 _walkResumeFrameIncr;
+
+ int _moveObjectCountdown;
+
+ bool _canSpitPipe;
+ bool _contSpitPipe;
+ bool _readyToSpit;
+ uint32 _spitPipeIndex;
+ uint32 _spitDestPipeIndex;
+ uint32 _spitContDestPipeIndex;
+
+ virtual void xUpdate();
+ virtual uint32 xHandleMessage(int messageNum, const MessageParam &param);
+
+ void startWalkToX(int16 x, bool walkExt);
+ void startWalkToXExt(int16 x);
+ void startWalkToXSmall(int16 x);
+ void startSpecialWalkLeft(int16 x);
+ void startSpecialWalkRight(int16 x);
+ void startWalkToXDistance(int16 destX, int16 distance);
+ void startWalkToAttachedSpriteXDistance(int16 distance);
+
+ void gotoNextStateExt();
+ void beginAction();
+ void endAction();
+
+ void stStartWalkingSmall();
+ uint32 hmWalkingSmall(int messageNum, const MessageParam &param, Entity *sender);
+
+ void enterIdleAnimation(uint idleAnimation);
+ void walkAlongPathPoints();
+
+};
+
+class KmScene1001 : public Klaymen {
+public:
+ KmScene1001(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
+protected:
+ uint32 xHandleMessage(int messageNum, const MessageParam &param);
+};
+
+class KmScene1002 : public Klaymen {
+public:
+ KmScene1002(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
+protected:
+ void xUpdate();
+ uint32 xHandleMessage(int messageNum, const MessageParam &param);
+};
+
+class KmScene1004 : public Klaymen {
+public:
+ KmScene1004(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
+protected:
+ uint32 xHandleMessage(int messageNum, const MessageParam &param);
+};
+
+class KmScene1109 : public Klaymen {
+public:
+ KmScene1109(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
+protected:
+ uint32 xHandleMessage(int messageNum, const MessageParam &param);
+};
+
+class KmScene1201 : public Klaymen {
+public:
+ KmScene1201(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
+protected:
+ uint32 xHandleMessage(int messageNum, const MessageParam &param);
+};
+
+class KmScene1303 : public Klaymen {
+public:
+ KmScene1303(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
+protected:
+ uint32 xHandleMessage(int messageNum, const MessageParam &param);
+};
+
+class KmScene1304 : public Klaymen {
+public:
+ KmScene1304(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
+protected:
+ uint32 xHandleMessage(int messageNum, const MessageParam &param);
+};
+
+class KmScene1305 : public Klaymen {
+public:
+ KmScene1305(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
+protected:
+ uint32 xHandleMessage(int messageNum, const MessageParam &param);
+};
+
+class KmScene1306 : public Klaymen {
+public:
+ KmScene1306(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
+protected:
+ uint32 xHandleMessage(int messageNum, const MessageParam &param);
+};
+
+class KmScene1308 : public Klaymen {
+public:
+ KmScene1308(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
+protected:
+ uint32 xHandleMessage(int messageNum, const MessageParam &param);
+};
+
+class KmScene1401 : public Klaymen {
+public:
+ KmScene1401(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
+protected:
+ uint32 xHandleMessage(int messageNum, const MessageParam &param);
+};
+
+class KmScene1402 : public Klaymen {
+public:
+ KmScene1402(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
+protected:
+ uint32 xHandleMessage(int messageNum, const MessageParam &param);
+};
+
+class KmScene1403 : public Klaymen {
+public:
+ KmScene1403(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
+protected:
+ uint32 xHandleMessage(int messageNum, const MessageParam &param);
+};
+
+class KmScene1404 : public Klaymen {
+public:
+ KmScene1404(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
+protected:
+ uint32 xHandleMessage(int messageNum, const MessageParam &param);
+};
+
+class KmScene1608 : public Klaymen {
+public:
+ KmScene1608(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
+protected:
+ uint32 xHandleMessage(int messageNum, const MessageParam &param);
+};
+
+class KmScene1705 : public Klaymen {
+public:
+ KmScene1705(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
+protected:
+ uint32 xHandleMessage(int messageNum, const MessageParam &param);
+};
+
+class KmScene1901 : public Klaymen {
+public:
+ KmScene1901(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
+protected:
+ uint32 xHandleMessage(int messageNum, const MessageParam &param);
+};
+
+class KmScene2001 : public Klaymen {
+public:
+ KmScene2001(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
+protected:
+ uint32 xHandleMessage(int messageNum, const MessageParam &param);
+};
+
+class KmScene2101 : public Klaymen {
+public:
+ KmScene2101(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
+protected:
+ uint32 xHandleMessage(int messageNum, const MessageParam &param);
+};
+
+class KmScene2201 : public Klaymen {
+public:
+ KmScene2201(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y, NRect *clipRects, int clipRectsCount);
+protected:
+ uint32 xHandleMessage(int messageNum, const MessageParam &param);
+};
+
+class KmScene2203 : public Klaymen {
+public:
+ KmScene2203(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
+protected:
+ uint32 xHandleMessage(int messageNum, const MessageParam &param);
+};
+
+class KmScene2205 : public Klaymen {
+public:
+ KmScene2205(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
+protected:
+ void xUpdate();
+ uint32 xHandleMessage(int messageNum, const MessageParam &param);
+};
+
+class KmScene2206 : public Klaymen {
+public:
+ KmScene2206(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
+ ~KmScene2206();
+protected:
+ void xUpdate();
+ uint32 xHandleMessage(int messageNum, const MessageParam &param);
+};
+
+class KmScene2207 : public Klaymen {
+public:
+ KmScene2207(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
+protected:
+ uint32 xHandleMessage(int messageNum, const MessageParam &param);
+};
+
+class KmScene2242 : public Klaymen {
+public:
+ KmScene2242(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
+protected:
+ void xUpdate();
+ uint32 xHandleMessage(int messageNum, const MessageParam &param);
+};
+
+class KmHallOfRecords : public Klaymen {
+public:
+ KmHallOfRecords(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
+protected:
+ void xUpdate();
+ uint32 xHandleMessage(int messageNum, const MessageParam &param);
+};
+
+class KmScene2247 : public Klaymen {
+public:
+ KmScene2247(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
+protected:
+ void xUpdate();
+ uint32 xHandleMessage(int messageNum, const MessageParam &param);
+};
+
+class KmScene2401 : public Klaymen {
+public:
+ KmScene2401(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
+protected:
+ uint32 xHandleMessage(int messageNum, const MessageParam &param);
+};
+
+class KmScene2402 : public Klaymen {
+public:
+ KmScene2402(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
+protected:
+ uint32 xHandleMessage(int messageNum, const MessageParam &param);
+};
+
+class KmScene2403 : public Klaymen {
+public:
+ KmScene2403(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
+protected:
+ uint32 xHandleMessage(int messageNum, const MessageParam &param);
+};
+
+class KmScene2406 : public Klaymen {
+public:
+ KmScene2406(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y, NRect *clipRects, int clipRectsCount);
+protected:
+ uint32 xHandleMessage(int messageNum, const MessageParam &param);
+};
+
+class KmScene2501 : public Klaymen {
+public:
+ KmScene2501(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
+protected:
+ uint32 xHandleMessage(int messageNum, const MessageParam &param);
+};
+
+class KmScene2732 : public Klaymen {
+public:
+ KmScene2732(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
+protected:
+ uint32 xHandleMessage(int messageNum, const MessageParam &param);
+};
+
+class KmScene2801 : public Klaymen {
+public:
+ KmScene2801(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
+protected:
+ uint32 xHandleMessage(int messageNum, const MessageParam &param);
+};
+
+class KmScene2803 : public Klaymen {
+public:
+ KmScene2803(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y, NRect *clipRects, int clipRectsCount);
+protected:
+ uint32 xHandleMessage(int messageNum, const MessageParam &param);
+};
+
+class KmScene2803Small : public Klaymen {
+public:
+ KmScene2803Small(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
+protected:
+ uint32 xHandleMessage(int messageNum, const MessageParam &param);
+};
+
+class KmScene2805 : public Klaymen {
+public:
+ KmScene2805(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
+protected:
+ uint32 xHandleMessage(int messageNum, const MessageParam &param);
+};
+
+class KmScene2806 : public Klaymen {
+public:
+ KmScene2806(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y,
+ bool needsLargeSurface, NRect *clipRects, uint clipRectsCount);
+protected:
+ uint32 xHandleMessage(int messageNum, const MessageParam &param);
+};
+
+class KmScene2809 : public Klaymen {
+public:
+ KmScene2809(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y,
+ bool needsLargeSurface, NRect *clipRects, uint clipRectsCount);
+protected:
+ uint32 xHandleMessage(int messageNum, const MessageParam &param);
+};
+
+class KmScene2810Small : public Klaymen {
+public:
+ KmScene2810Small(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
+protected:
+ uint32 xHandleMessage(int messageNum, const MessageParam &param);
+};
+
+class KmScene2810 : public Klaymen {
+public:
+ KmScene2810(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y,
+ NRect *clipRects, uint clipRectsCount);
+protected:
+ uint32 xHandleMessage(int messageNum, const MessageParam &param);
+};
+
+class KmScene2812 : public Klaymen {
+public:
+ KmScene2812(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
+protected:
+ uint32 xHandleMessage(int messageNum, const MessageParam &param);
+};
+
+} // End of namespace Neverhood
+
+#endif /* NEVERHOOD_KLAYMEN_H */
diff --git a/engines/neverhood/menumodule.cpp b/engines/neverhood/menumodule.cpp
new file mode 100644
index 0000000000..a8631cb0d6
--- /dev/null
+++ b/engines/neverhood/menumodule.cpp
@@ -0,0 +1,1116 @@
+/* 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/menumodule.h"
+#include "neverhood/gamemodule.h"
+
+#include "engines/savestate.h"
+
+namespace Neverhood {
+
+enum {
+ MAIN_MENU = 0,
+ CREDITS_SCENE = 1,
+ MAKING_OF = 2,
+ LOAD_GAME_MENU = 3,
+ SAVE_GAME_MENU = 4,
+ DELETE_GAME_MENU = 5,
+ QUERY_OVR_MENU = 6
+};
+
+enum {
+ kMainMenuRestartGame = 0,
+ kMainMenuLoadGame = 1,
+ kMainMenuSaveGame = 2,
+ kMainMenuResumeGame = 3,
+ kMainMenuQuitGame = 4,
+ kMainMenuCredits = 5,
+ kMainMenuMakingOf = 6,
+ kMainMenuToggleMusic = 7,
+ kMainMenuDeleteGame = 8
+};
+
+static const uint32 kMakingOfSmackerFileHashList[] = {
+ 0x21082409,
+ 0x21082809,
+ 0x21083009,
+ 0x21080009,
+ 0x21086009,
+ 0x2108A009,
+ 0x21092009,
+ 0x210A2009,
+ 0x210C2009,
+ 0x21082411,
+ 0x21082811,
+ 0x21083011,
+ 0x21080011,
+ 0
+};
+
+MenuModule::MenuModule(NeverhoodEngine *vm, Module *parentModule, int which)
+ : Module(vm, parentModule), _savegameList(NULL) {
+
+ SetMessageHandler(&MenuModule::handleMessage);
+
+ _savedPaletteData = _vm->_screen->getPaletteData();
+ _vm->_mixer->pauseAll(true);
+
+ createScene(MAIN_MENU, -1);
+}
+
+MenuModule::~MenuModule() {
+ _vm->_mixer->pauseAll(false);
+ _vm->_screen->setPaletteData(_savedPaletteData);
+}
+
+void MenuModule::setLoadgameInfo(uint index) {
+ _savegameSlot = (*_savegameList)[index].slotNum;
+}
+
+void MenuModule::setSavegameInfo(const Common::String &description, uint index, bool newSavegame) {
+ _savegameDescription = description;
+ _savegameSlot = newSavegame ? -1 : (*_savegameList)[index].slotNum;
+}
+
+void MenuModule::setDeletegameInfo(uint index) {
+ _savegameSlot = (*_savegameList)[index].slotNum;
+}
+
+void MenuModule::createScene(int sceneNum, int which) {
+ _sceneNum = sceneNum;
+ switch (_sceneNum) {
+ case MAIN_MENU:
+ _childObject = new MainMenu(_vm, this);
+ break;
+ case CREDITS_SCENE:
+ _childObject = new CreditsScene(_vm, this, true);
+ break;
+ case MAKING_OF:
+ createSmackerScene(kMakingOfSmackerFileHashList, false, true, true);
+ break;
+ case LOAD_GAME_MENU:
+ createLoadGameMenu();
+ break;
+ case SAVE_GAME_MENU:
+ createSaveGameMenu();
+ break;
+ case DELETE_GAME_MENU:
+ createDeleteGameMenu();
+ break;
+ case QUERY_OVR_MENU:
+ _childObject = new QueryOverwriteMenu(_vm, this, _savegameDescription);
+ break;
+ }
+ SetUpdateHandler(&MenuModule::updateScene);
+ _childObject->handleUpdate();
+}
+
+void MenuModule::updateScene() {
+ if (!updateChild()) {
+ switch (_sceneNum) {
+ case MAIN_MENU:
+ switch (_moduleResult) {
+ case kMainMenuRestartGame:
+ _vm->_gameModule->requestRestartGame(false);
+ leaveModule(0);
+ break;
+ case kMainMenuLoadGame:
+ createScene(LOAD_GAME_MENU, -1);
+ break;
+ case kMainMenuSaveGame:
+ createScene(SAVE_GAME_MENU, -1);
+ break;
+ case kMainMenuResumeGame:
+ leaveModule(0);
+ break;
+ case kMainMenuQuitGame:
+ leaveModule(0);
+ _vm->quitGame();
+ break;
+ case kMainMenuCredits:
+ createScene(CREDITS_SCENE, -1);
+ break;
+ case kMainMenuMakingOf:
+ createScene(MAKING_OF, -1);
+ break;
+ case kMainMenuToggleMusic:
+ // TODO Toggle music 0048A367
+ createScene(MAIN_MENU, -1);
+ break;
+ case kMainMenuDeleteGame:
+ createScene(DELETE_GAME_MENU, -1);
+ break;
+ default:
+ createScene(MAIN_MENU, -1);
+ break;
+ }
+ break;
+ case CREDITS_SCENE:
+ case MAKING_OF:
+ createScene(MAIN_MENU, -1);
+ break;
+ case LOAD_GAME_MENU:
+ handleLoadGameMenuAction(_moduleResult != 1);
+ break;
+ case SAVE_GAME_MENU:
+ handleSaveGameMenuAction(_moduleResult != 1, true);
+ break;
+ case DELETE_GAME_MENU:
+ handleDeleteGameMenuAction(_moduleResult != 1);
+ break;
+ case QUERY_OVR_MENU:
+ handleSaveGameMenuAction(_moduleResult != 1, false);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+uint32 MenuModule::handleMessage(int messageNum, const MessageParam &param, Entity *sender) {
+ return Module::handleMessage(messageNum, param, sender);;
+}
+
+void MenuModule::createLoadGameMenu() {
+ _savegameSlot = -1;
+ _savegameList = new SavegameList();
+ loadSavegameList();
+ _childObject = new LoadGameMenu(_vm, this, _savegameList);
+}
+
+void MenuModule::createSaveGameMenu() {
+ _savegameSlot = -1;
+ _savegameList = new SavegameList();
+ loadSavegameList();
+ _childObject = new SaveGameMenu(_vm, this, _savegameList);
+}
+
+void MenuModule::createDeleteGameMenu() {
+ _savegameSlot = -1;
+ _savegameList = new SavegameList();
+ loadSavegameList();
+ _childObject = new DeleteGameMenu(_vm, this, _savegameList);
+}
+
+void MenuModule::handleLoadGameMenuAction(bool doLoad) {
+ createScene(MAIN_MENU, -1);
+ if (doLoad && _savegameSlot >= 0) {
+ _vm->loadGameState(_savegameSlot);
+ leaveModule(0);
+ }
+ delete _savegameList;
+ _savegameList = NULL;
+}
+
+void MenuModule::handleSaveGameMenuAction(bool doSave, bool doQuery) {
+ if (doSave && doQuery && _savegameSlot >= 0) {
+ createScene(QUERY_OVR_MENU, -1);
+ } else if (doSave) {
+ // Get a new slot number if it's a new savegame
+ if (_savegameSlot < 0)
+ _savegameSlot = _savegameList->size() > 0 ? _savegameList->back().slotNum + 1 : 0;
+ // Restore the scene palette and background so that the correct thumbnail is saved
+ byte *menuPaletteData = _vm->_screen->getPaletteData();
+ _vm->_screen->setPaletteData(_savedPaletteData);
+ _vm->_gameModule->redrawPrevChildObject();
+ _vm->saveGameState(_savegameSlot, _savegameDescription);
+ _vm->_screen->setPaletteData(menuPaletteData);
+ createScene(MAIN_MENU, -1);
+ } else {
+ createScene(MAIN_MENU, -1);
+ }
+ delete _savegameList;
+ _savegameList = NULL;
+}
+
+void MenuModule::handleDeleteGameMenuAction(bool doDelete) {
+ createScene(MAIN_MENU, -1);
+ if (doDelete && _savegameSlot >= 0) {
+ _vm->removeGameState(_savegameSlot);
+ }
+ delete _savegameList;
+ _savegameList = NULL;
+}
+
+void MenuModule::loadSavegameList() {
+
+ Common::SaveFileManager *saveFileMan = _vm->_system->getSavefileManager();
+ Neverhood::NeverhoodEngine::SaveHeader header;
+ Common::String pattern = _vm->getTargetName();
+ pattern += ".???";
+
+ Common::StringArray filenames;
+ filenames = saveFileMan->listSavefiles(pattern.c_str());
+ Common::sort(filenames.begin(), filenames.end());
+
+ SaveStateList saveList;
+ for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); file++) {
+ int slotNum = atoi(file->c_str() + file->size() - 3);
+ if (slotNum >= 0 && slotNum <= 999) {
+ Common::InSaveFile *in = saveFileMan->openForLoading(file->c_str());
+ if (in) {
+ if (Neverhood::NeverhoodEngine::readSaveHeader(in, false, header) == Neverhood::NeverhoodEngine::kRSHENoError) {
+ SavegameItem savegameItem;
+ savegameItem.slotNum = slotNum;
+ savegameItem.description = header.description;
+ _savegameList->push_back(savegameItem);
+ }
+ delete in;
+ }
+ }
+ }
+
+}
+
+MenuButton::MenuButton(NeverhoodEngine *vm, Scene *parentScene, uint buttonIndex, uint32 fileHash, const NRect &collisionBounds)
+ : StaticSprite(vm, 900), _parentScene(parentScene), _buttonIndex(buttonIndex), _countdown(0) {
+
+ loadSprite(fileHash, kSLFDefDrawOffset | kSLFDefPosition, 100);
+ _collisionBounds = collisionBounds;
+ setVisible(false);
+ SetUpdateHandler(&MenuButton::update);
+ SetMessageHandler(&MenuButton::handleMessage);
+}
+
+void MenuButton::update() {
+ updatePosition();
+ if (_countdown != 0 && (--_countdown) == 0) {
+ setVisible(false);
+ sendMessage(_parentScene, 0x2000, _buttonIndex);
+ }
+}
+
+uint32 MenuButton::handleMessage(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = Sprite::handleMessage(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x1011:
+ if (_countdown == 0) {
+ setVisible(true);
+ _countdown = 4;
+ }
+ messageResult = 1;
+ break;
+ }
+ return messageResult;
+}
+
+MainMenu::MainMenu(NeverhoodEngine *vm, Module *parentModule)
+ : Scene(vm, parentModule) {
+
+ static const uint32 kMenuButtonFileHashes[] = {
+ 0x36C62120,
+ 0x56C62120,
+ 0x96C62120,
+ 0x16C62121,
+ 0x16C62122,
+ 0x16C62124,
+ 0x16C62128,
+ 0x16C62130,
+ 0x16C62100
+ };
+
+ static const NRect kMenuButtonCollisionBounds[] = {
+ NRect(52, 121, 110, 156),
+ NRect(52, 192, 109, 222),
+ NRect(60, 257, 119, 286),
+ NRect(67, 326, 120, 354),
+ NRect(70, 389, 128, 416),
+ NRect(523, 113, 580, 144),
+ NRect(525, 176, 577, 206),
+ NRect(527, 384, 580, 412),
+ NRect(522, 255, 580, 289)
+ };
+
+ setBackground(0x08C0020C);
+ setPalette(0x08C0020C);
+ insertScreenMouse(0x00208084);
+
+ insertStaticSprite(0x41137051, 100);
+ insertStaticSprite(0xC10B2015, 100);
+
+ // TODO Only if music is enabled
+ _musicOnButton = insertStaticSprite(0x0C24C0EE, 100);
+
+ for (uint buttonIndex = 0; buttonIndex < 9; ++buttonIndex) {
+ Sprite *menuButton = insertSprite<MenuButton>(this, buttonIndex,
+ kMenuButtonFileHashes[buttonIndex], kMenuButtonCollisionBounds[buttonIndex]);
+ addCollisionSprite(menuButton);
+ }
+
+ SetUpdateHandler(&Scene::update);
+ SetMessageHandler(&MainMenu::handleMessage);
+
+}
+
+uint32 MainMenu::handleMessage(int messageNum, const MessageParam &param, Entity *sender) {
+ Scene::handleMessage(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x2000:
+ leaveScene(param.asInteger());
+ break;
+ }
+ return 0;
+}
+
+static const uint32 kCreditsSceneFileHashes[] = {
+ 0x6081128C, 0x608112BC, 0x608112DC,
+ 0x6081121C, 0x6081139C, 0x6081109C,
+ 0x6081169C, 0x60811A9C, 0x6081029C,
+ 0x0081128C, 0x008112BC, 0x008012BC,
+ 0x008112DC, 0x0081121C, 0x0081139C,
+ 0x0081109C, 0x0081169C, 0x00811A9C,
+ 0x0081029C, 0x0081329C, 0xC08112BC,
+ 0xC08112DC, 0xC081121C, 0xC081139C,
+ 0
+};
+
+CreditsScene::CreditsScene(NeverhoodEngine *vm, Module *parentModule, bool canAbort)
+ : Scene(vm, parentModule), _canAbort(canAbort), _screenIndex(0), _ticksDuration(0),
+ _countdown(216) {
+
+ SetUpdateHandler(&CreditsScene::update);
+ SetMessageHandler(&CreditsScene::handleMessage);
+
+ setBackground(0x6081128C);
+ setPalette(0x6081128C);
+
+ _ticksTime = _vm->_system->getMillis() + 202100;
+
+ _musicResource = new MusicResource(_vm);
+ _musicResource->load(0x30812225);
+ _musicResource->play(0);
+
+}
+
+CreditsScene::~CreditsScene() {
+ _musicResource->unload();
+ delete _musicResource;
+}
+
+void CreditsScene::update() {
+ Scene::update();
+ if (_countdown != 0) {
+ if (_screenIndex == 23 && _vm->_system->getMillis() > _ticksTime)
+ leaveScene(0);
+ else if ((--_countdown) == 0) {
+ ++_screenIndex;
+ if (kCreditsSceneFileHashes[_screenIndex] == 0)
+ leaveScene(0);
+ else {
+ _background->load(kCreditsSceneFileHashes[_screenIndex]);
+ _palette->addPalette(kCreditsSceneFileHashes[_screenIndex], 0, 256, 0);
+ if (_screenIndex < 5)
+ _countdown = 192;
+ else if (_screenIndex < 15)
+ _countdown = 144;
+ else if (_screenIndex < 16)
+ _countdown = 216;
+ else if (_screenIndex < 23)
+ _countdown = 144;
+ else
+ _countdown = 1224;
+ }
+ }
+ }
+}
+
+uint32 CreditsScene::handleMessage(int messageNum, const MessageParam &param, Entity *sender) {
+ Scene::handleMessage(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x0009:
+ leaveScene(0);
+ break;
+ case 0x000B:
+ if (param.asInteger() == Common::KEYCODE_ESCAPE && _canAbort)
+ leaveScene(0);
+ break;
+ case 0x101D:
+ _ticksDuration = _ticksTime - _vm->_system->getMillis();
+ break;
+ case 0x101E:
+ _ticksTime = _ticksDuration + _vm->_system->getMillis();
+ break;
+ }
+ return 0;
+}
+
+Widget::Widget(NeverhoodEngine *vm, int16 x, int16 y, GameStateMenu *parentScene,
+ int baseObjectPriority, int baseSurfacePriority)
+ : StaticSprite(vm, baseObjectPriority), _parentScene(parentScene),
+ _baseObjectPriority(baseObjectPriority), _baseSurfacePriority(baseSurfacePriority) {
+
+ SetUpdateHandler(&Widget::update);
+ SetMessageHandler(&Widget::handleMessage);
+
+ setPosition(x, y);
+}
+
+void Widget::onClick() {
+ _parentScene->setCurrWidget(this);
+}
+
+void Widget::setPosition(int16 x, int16 y) {
+ _x = x;
+ _y = y;
+ updateBounds();
+}
+
+void Widget::refreshPosition() {
+ _needRefresh = true;
+ StaticSprite::updatePosition();
+ _collisionBoundsOffset.set(0, 0,
+ _spriteResource.getDimensions().width, _spriteResource.getDimensions().height);
+ updateBounds();
+}
+
+void Widget::initialize() {
+ // Empty
+}
+
+int16 Widget::getWidth() {
+ return _spriteResource.getDimensions().width;
+}
+
+int16 Widget::getHeight() {
+ return _spriteResource.getDimensions().height;
+}
+
+void Widget::enterWidget() {
+ // Empty
+}
+
+void Widget::exitWidget() {
+ // Empty
+}
+
+void Widget::update() {
+ handleSpriteUpdate();
+ StaticSprite::updatePosition();
+}
+
+uint32 Widget::handleMessage(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = Sprite::handleMessage(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x1011:
+ onClick();
+ messageResult = 1;
+ break;
+ }
+ return messageResult;
+}
+
+TextLabelWidget::TextLabelWidget(NeverhoodEngine *vm, int16 x, int16 y, GameStateMenu *parentScene,
+ int baseObjectPriority, int baseSurfacePriority,
+ const byte *string, int stringLen, BaseSurface *drawSurface, int16 tx, int16 ty, FontSurface *fontSurface)
+ : Widget(vm, x, y, parentScene, baseObjectPriority, baseSurfacePriority),
+ _string(string), _stringLen(stringLen), _drawSurface(drawSurface), _tx(tx), _ty(ty), _fontSurface(fontSurface) {
+
+}
+
+void TextLabelWidget::initialize() {
+ _parentScene->addSprite(this);
+ _parentScene->addCollisionSprite(this);
+}
+
+int16 TextLabelWidget::getWidth() {
+ return _fontSurface->getStringWidth(_string, _stringLen);
+}
+
+int16 TextLabelWidget::getHeight() {
+ return _fontSurface->getCharHeight();
+}
+
+void TextLabelWidget::drawString(int maxStringLength) {
+ _fontSurface->drawString(_drawSurface, _x, _y, _string, MIN(_stringLen, maxStringLength));
+ _collisionBoundsOffset.set(_tx, _ty, getWidth(), getHeight());
+ updateBounds();
+}
+
+void TextLabelWidget::clear() {
+ _collisionBoundsOffset.set(0, 0, 0, 0);
+ updateBounds();
+}
+
+void TextLabelWidget::setString(const byte *string, int stringLen) {
+ _string = string;
+ _stringLen = stringLen;
+}
+
+TextEditWidget::TextEditWidget(NeverhoodEngine *vm, int16 x, int16 y, GameStateMenu *parentScene,
+ int maxStringLength, FontSurface *fontSurface, uint32 fileHash, const NRect &rect)
+ : Widget(vm, x, y, parentScene, 1000, 1000),
+ _maxStringLength(maxStringLength), _fontSurface(fontSurface), _fileHash(fileHash), _rect(rect),
+ _cursorSurface(NULL), _cursorTicks(0), _cursorPos(0), _cursorFileHash(0), _cursorWidth(0), _cursorHeight(0),
+ _modified(false), _readOnly(false) {
+
+ _maxVisibleChars = (_rect.x2 - _rect.x1) / _fontSurface->getCharWidth();
+ _cursorPos = 0;
+
+ SetUpdateHandler(&TextEditWidget::update);
+ SetMessageHandler(&TextEditWidget::handleMessage);
+}
+
+TextEditWidget::~TextEditWidget() {
+ delete _cursorSurface;
+}
+
+void TextEditWidget::onClick() {
+ NPoint mousePos = _parentScene->getMousePos();
+ mousePos.x -= _x + _rect.x1;
+ mousePos.y -= _y + _rect.y1;
+ if (mousePos.x >= 0 && mousePos.x <= _rect.x2 - _rect.x1 &&
+ mousePos.y >= 0 && mousePos.y <= _rect.y2 - _rect.y1) {
+ if (_entryString.size() == 1)
+ _cursorPos = 0;
+ else {
+ int newCursorPos = mousePos.x / _fontSurface->getCharWidth();
+ if (mousePos.x % _fontSurface->getCharWidth() > _fontSurface->getCharWidth() / 2 && newCursorPos <= (int)_entryString.size())
+ ++newCursorPos;
+ _cursorPos = MIN((int)_entryString.size(), newCursorPos);
+ }
+ _cursorSurface->setVisible(true);
+ refresh();
+ }
+ Widget::onClick();
+}
+
+void TextEditWidget::initialize() {
+ SpriteResource cursorSpriteResource(_vm);
+
+ _spriteResource.load(_fileHash, true);
+ createSurface(_baseSurfacePriority, _spriteResource.getDimensions().width, _spriteResource.getDimensions().height);
+ refreshPosition();
+ _parentScene->addSprite(this);
+ _parentScene->addCollisionSprite(this);
+ _surface->setVisible(true);
+ _textLabelWidget = new TextLabelWidget(_vm, _rect.x1, _rect.y1 + (_rect.y2 - _rect.y1 + 1 - _fontSurface->getCharHeight()) / 2,
+ _parentScene, _baseObjectPriority + 1, _baseSurfacePriority + 1,
+ (const byte*)_entryString.c_str(), _entryString.size(), _surface, _x, _y, _fontSurface);
+ _textLabelWidget->initialize();
+ if (_cursorFileHash != 0) {
+ cursorSpriteResource.load(_cursorFileHash, true);
+ _cursorSurface = new BaseSurface(_vm, 0, cursorSpriteResource.getDimensions().width, cursorSpriteResource.getDimensions().height);
+ _cursorSurface->drawSpriteResourceEx(cursorSpriteResource, false, false, cursorSpriteResource.getDimensions().width, cursorSpriteResource.getDimensions().height);
+ _cursorSurface->setVisible(!_readOnly);
+ }
+ refresh();
+}
+
+void TextEditWidget::enterWidget() {
+ if (!_readOnly) {
+ _cursorSurface->setVisible(true);
+ _vm->_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true);
+ }
+ refresh();
+}
+
+void TextEditWidget::exitWidget() {
+ if (!_readOnly) {
+ _cursorSurface->setVisible(false);
+ _vm->_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
+ }
+ refresh();
+}
+
+void TextEditWidget::setCursor(uint32 cursorFileHash, int16 cursorWidth, int16 cursorHeight) {
+ _cursorFileHash = cursorFileHash;
+ _cursorWidth = cursorWidth;
+ _cursorHeight = cursorHeight;
+}
+
+void TextEditWidget::drawCursor() {
+ if (_cursorSurface->getVisible() && _cursorPos >= 0 && _cursorPos <= _maxVisibleChars) {
+ NDrawRect sourceRect(0, 0, _cursorWidth, _cursorHeight);
+ _surface->copyFrom(_cursorSurface->getSurface(), _rect.x1 + _cursorPos * _fontSurface->getCharWidth(),
+ _rect.y1 + (_rect.y2 - _cursorHeight - _rect.y1 + 1) / 2, sourceRect);
+ } else if (!_readOnly)
+ _cursorSurface->setVisible(false);
+}
+
+void TextEditWidget::updateString() {
+ _textLabelWidget->setString((const byte *)_entryString.c_str(), _entryString.size());
+ _textLabelWidget->drawString(_maxVisibleChars);
+}
+
+Common::String& TextEditWidget::getString() {
+ return _entryString;
+}
+
+void TextEditWidget::setString(const Common::String &string) {
+ _entryString = string;
+ _cursorPos = _entryString.size();
+ _modified = false;
+ refresh();
+}
+
+void TextEditWidget::handleAsciiKey(char ch) {
+ if ((int)_entryString.size() < _maxStringLength &&
+ ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') || ch == ' ')) {
+ _entryString.insertChar(ch, _cursorPos);
+ ++_cursorPos;
+ _modified = true;
+ refresh();
+ }
+}
+
+void TextEditWidget::handleKeyDown(Common::KeyCode keyCode) {
+ bool doRefresh = true;
+ switch (keyCode) {
+ case Common::KEYCODE_HOME:
+ _cursorPos = 0;
+ break;
+ case Common::KEYCODE_END:
+ _cursorPos = _entryString.size();
+ break;
+ case Common::KEYCODE_LEFT:
+ if (_entryString.size() > 0 && _cursorPos > 0)
+ --_cursorPos;
+ break;
+ case Common::KEYCODE_RIGHT:
+ if (_cursorPos < (int)_entryString.size())
+ ++_cursorPos;
+ break;
+ case Common::KEYCODE_DELETE:
+ if (_entryString.size() > 0 && _cursorPos < (int)_entryString.size()) {
+ _entryString.deleteChar(_cursorPos);
+ _modified = true;
+ }
+ break;
+ case Common::KEYCODE_BACKSPACE:
+ if (_entryString.size() > 0 && _cursorPos > 0) {
+ _entryString.deleteChar(--_cursorPos);
+ _modified = true;
+ }
+ break;
+ default:
+ doRefresh = false;
+ break;
+ }
+ if (doRefresh) {
+ _cursorSurface->setVisible(!_readOnly);
+ _cursorTicks = 0;
+ refresh();
+ }
+}
+
+void TextEditWidget::refresh() {
+ refreshPosition();
+ updateString();
+ if (_cursorFileHash != 0)
+ drawCursor();
+}
+
+void TextEditWidget::update() {
+ Widget::update();
+ if (!_readOnly && _parentScene->getCurrWidget() == this && _cursorTicks++ == 10) {
+ _cursorSurface->setVisible(!_cursorSurface->getVisible());
+ refresh();
+ _cursorTicks = 0;
+ }
+}
+
+uint32 TextEditWidget::handleMessage(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = Widget::handleMessage(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x000A:
+ handleAsciiKey(param.asInteger());
+ break;
+ case 0x000B:
+ handleKeyDown((Common::KeyCode)param.asInteger());
+ break;
+ }
+ return messageResult;
+}
+
+SavegameListBox::SavegameListBox(NeverhoodEngine *vm, int16 x, int16 y, GameStateMenu *parentScene,
+ SavegameList *savegameList, FontSurface *fontSurface, uint32 bgFileHash, const NRect &rect)
+ : Widget(vm, x, y, parentScene, 1000, 1000),
+ _savegameList(savegameList), _fontSurface(fontSurface), _bgFileHash(bgFileHash), _rect(rect),
+ _maxStringLength(0), _firstVisibleItem(0), _lastVisibleItem(0), _currIndex(0) {
+
+ _maxVisibleItemsCount = (_rect.y2 - _rect.y1) / _fontSurface->getCharHeight();
+ _maxStringLength = (_rect.x2 - _rect.x1) / _fontSurface->getCharWidth();
+}
+
+void SavegameListBox::onClick() {
+ NPoint mousePos = _parentScene->getMousePos();
+ mousePos.x -= _x + _rect.x1;
+ mousePos.y -= _y + _rect.y1;
+ if (mousePos.x >= 0 && mousePos.x <= _rect.x2 - _rect.x1 &&
+ mousePos.y >= 0 && mousePos.y <= _rect.y2 - _rect.y1) {
+ // We add 1 to the char height to ensure that the correct entry is chosen if the
+ // user clicks at the bottom the text entry
+ int newIndex = _firstVisibleItem + mousePos.y / (_fontSurface->getCharHeight() + 1);
+ if (newIndex <= _lastVisibleItem) {
+ _currIndex = newIndex;
+ refresh();
+ _parentScene->setCurrWidget(this);
+ _parentScene->refreshDescriptionEdit();
+ }
+ }
+}
+
+void SavegameListBox::initialize() {
+ _spriteResource.load(_bgFileHash, true);
+ createSurface(_baseSurfacePriority, _spriteResource.getDimensions().width, _spriteResource.getDimensions().height);
+ refreshPosition();
+ _parentScene->addSprite(this);
+ _parentScene->addCollisionSprite(this);
+ _surface->setVisible(true);
+ buildItems();
+ _firstVisibleItem = 0;
+ _lastVisibleItem = MIN(_maxVisibleItemsCount, (int)_textLabelItems.size()) - 1;
+ refresh();
+}
+
+void SavegameListBox::buildItems() {
+ SavegameList &savegameList = *_savegameList;
+ int16 itemX = _rect.x1, itemY = 0;
+ for (uint i = 0; i < savegameList.size(); ++i) {
+ const byte *string = (const byte*)savegameList[i].description.c_str();
+ int stringLen = (int)savegameList[i].description.size();
+ TextLabelWidget *label = new TextLabelWidget(_vm, itemX, itemY, _parentScene, _baseObjectPriority + 1,
+ _baseSurfacePriority + 1, string, MIN(stringLen, _maxStringLength), _surface, _x, _y, _fontSurface);
+ label->initialize();
+ _textLabelItems.push_back(label);
+ }
+}
+
+void SavegameListBox::drawItems() {
+ for (int i = 0; i < (int)_textLabelItems.size(); ++i) {
+ TextLabelWidget *label = _textLabelItems[i];
+ if (i >= _firstVisibleItem && i <= _lastVisibleItem) {
+ label->setY(_rect.y1 + (i - _firstVisibleItem) * _fontSurface->getCharHeight());
+ label->updateBounds();
+ label->drawString(_maxStringLength);
+ } else
+ label->clear();
+ }
+}
+
+void SavegameListBox::refresh() {
+ refreshPosition();
+ drawItems();
+}
+
+void SavegameListBox::scrollUp() {
+ if (_firstVisibleItem > 0) {
+ --_firstVisibleItem;
+ --_lastVisibleItem;
+ refresh();
+ }
+}
+
+void SavegameListBox::scrollDown() {
+ if (_lastVisibleItem < (int)_textLabelItems.size()) {
+ ++_firstVisibleItem;
+ ++_lastVisibleItem;
+ refresh();
+ }
+}
+
+void SavegameListBox::pageUp() {
+ int amount = MIN(_firstVisibleItem, _maxVisibleItemsCount);
+ if (amount > 0) {
+ _firstVisibleItem -= amount;
+ _lastVisibleItem -= amount;
+ refresh();
+ }
+}
+
+void SavegameListBox::pageDown() {
+ int amount = MIN((int)_textLabelItems.size() - _lastVisibleItem, _maxVisibleItemsCount);
+ if (amount > 0) {
+ _firstVisibleItem += amount;
+ _lastVisibleItem += amount;
+ refresh();
+ }
+}
+
+GameStateMenu::GameStateMenu(NeverhoodEngine *vm, Module *parentModule, SavegameList *savegameList,
+ const uint32 *buttonFileHashes, const NRect *buttonCollisionBounds,
+ uint32 backgroundFileHash, uint32 fontFileHash,
+ uint32 mouseFileHash, const NRect *mouseRect,
+ uint32 listBoxBackgroundFileHash, int16 listBoxX, int16 listBoxY, const NRect &listBoxRect,
+ uint32 textEditBackgroundFileHash, uint32 textEditCursorFileHash, int16 textEditX, int16 textEditY, const NRect &textEditRect,
+ uint32 textFileHash1, uint32 textFileHash2)
+ : Scene(vm, parentModule), _currWidget(NULL), _savegameList(savegameList) {
+
+ _fontSurface = new FontSurface(_vm, fontFileHash, 32, 7, 32, 11, 17);
+
+ setBackground(backgroundFileHash);
+ setPalette(backgroundFileHash);
+ insertScreenMouse(mouseFileHash, mouseRect);
+ insertStaticSprite(textFileHash1, 200);
+ insertStaticSprite(textFileHash2, 200);
+
+ _listBox = new SavegameListBox(_vm, listBoxX, listBoxY, this,
+ _savegameList, _fontSurface, listBoxBackgroundFileHash, listBoxRect);
+ _listBox->initialize();
+
+ _textEditWidget = new TextEditWidget(_vm, textEditX, textEditY, this, 29,
+ _fontSurface, textEditBackgroundFileHash, textEditRect);
+ if (textEditCursorFileHash != 0)
+ _textEditWidget->setCursor(textEditCursorFileHash, 2, 13);
+ else
+ _textEditWidget->setReadOnly(true);
+ _textEditWidget->initialize();
+ setCurrWidget(_textEditWidget);
+
+ for (uint buttonIndex = 0; buttonIndex < 6; ++buttonIndex) {
+ Sprite *menuButton = insertSprite<MenuButton>(this, buttonIndex,
+ buttonFileHashes[buttonIndex], buttonCollisionBounds[buttonIndex]);
+ addCollisionSprite(menuButton);
+ }
+
+ SetUpdateHandler(&Scene::update);
+ SetMessageHandler(&GameStateMenu::handleMessage);
+
+}
+
+GameStateMenu::~GameStateMenu() {
+ delete _fontSurface;
+}
+
+NPoint GameStateMenu::getMousePos() {
+ NPoint pt;
+ pt.x = _mouseCursor->getX();
+ pt.y = _mouseCursor->getY();
+ return pt;
+}
+
+void GameStateMenu::setCurrWidget(Widget *newWidget) {
+ if (newWidget && newWidget != _currWidget) {
+ if (_currWidget)
+ _currWidget->exitWidget();
+ newWidget->enterWidget();
+ _currWidget = newWidget;
+ }
+}
+
+void GameStateMenu::refreshDescriptionEdit() {
+ uint currIndex = _listBox->getCurrIndex();
+ _textEditWidget->setString((*_savegameList)[currIndex].description);
+ setCurrWidget(_textEditWidget);
+}
+
+void GameStateMenu::performAction() {
+ // Empty
+}
+
+uint32 GameStateMenu::handleMessage(int messageNum, const MessageParam &param, Entity *sender) {
+ Scene::handleMessage(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x000A:
+ if (!_textEditWidget->isReadOnly()) {
+ sendMessage(_textEditWidget, 0x000A, param.asInteger());
+ setCurrWidget(_textEditWidget);
+ }
+ break;
+ case 0x000B:
+ if (param.asInteger() == Common::KEYCODE_RETURN)
+ performAction();
+ else if (param.asInteger() == Common::KEYCODE_ESCAPE)
+ leaveScene(1);
+ else if (!_textEditWidget->isReadOnly()) {
+ sendMessage(_textEditWidget, 0x000B, param.asInteger());
+ setCurrWidget(_textEditWidget);
+ }
+ break;
+ case 0x2000:
+ // Handle menu button click
+ switch (param.asInteger()) {
+ case 0:
+ performAction();
+ break;
+ case 1:
+ leaveScene(1);
+ break;
+ case 2:
+ _listBox->pageUp();
+ break;
+ case 3:
+ _listBox->scrollUp();
+ break;
+ case 4:
+ _listBox->scrollDown();
+ break;
+ case 5:
+ _listBox->pageDown();
+ break;
+ }
+ break;
+ }
+ return 0;
+}
+
+static const uint32 kSaveGameMenuButtonFileHashes[] = {
+ 0x8359A824, 0x0690E260, 0x0352B050,
+ 0x1392A223, 0x13802260, 0x0B32B200
+};
+
+static const NRect kSaveGameMenuButtonCollisionBounds[] = {
+ NRect(518, 106, 602, 160),
+ NRect(516, 378, 596, 434),
+ NRect(394, 108, 458, 206),
+ NRect(400, 204, 458, 276),
+ NRect(398, 292, 456, 352),
+ NRect(396, 352, 460, 444)
+};
+
+static const NRect kSaveGameMenuListBoxRect(0, 0, 320, 272);
+static const NRect kSaveGameMenuTextEditRect(0, 0, 377, 17);
+static const NRect kSaveGameMenuMouseRect(50, 47, 427, 64);
+
+SaveGameMenu::SaveGameMenu(NeverhoodEngine *vm, Module *parentModule, SavegameList *savegameList)
+ : GameStateMenu(vm, parentModule, savegameList, kSaveGameMenuButtonFileHashes, kSaveGameMenuButtonCollisionBounds,
+ 0x30084E25, 0x2328121A,
+ 0x84E21308, &kSaveGameMenuMouseRect,
+ 0x1115A223, 60, 142, kSaveGameMenuListBoxRect,
+ 0x3510A868, 0x8290AC20, 50, 47, kSaveGameMenuTextEditRect,
+ 0x1340A5C2, 0x1301A7EA) {
+
+}
+
+void SaveGameMenu::performAction() {
+ ((MenuModule*)_parentModule)->setSavegameInfo(_textEditWidget->getString(),
+ _listBox->getCurrIndex(), _textEditWidget->isModified());
+ leaveScene(0);
+}
+
+static const uint32 kLoadGameMenuButtonFileHashes[] = {
+ 0x100B2091, 0x84822B03, 0x20E22087,
+ 0x04040107, 0x04820122, 0x24423047
+};
+
+static const NRect kLoadGameMenuButtonCollisionBounds[] = {
+ NRect( 44, 115, 108, 147),
+ NRect( 52, 396, 112, 426),
+ NRect(188, 116, 245, 196),
+ NRect(189, 209, 239, 269),
+ NRect(187, 301, 233, 349),
+ NRect(182, 358, 241, 433)
+};
+
+static const NRect kLoadGameMenuListBoxRect(0, 0, 320, 271);
+static const NRect kLoadGameMenuTextEditRect(0, 0, 320, 17);
+static const NRect kLoadGameMenuMouseRect(263, 48, 583, 65);
+
+LoadGameMenu::LoadGameMenu(NeverhoodEngine *vm, Module *parentModule, SavegameList *savegameList)
+ : GameStateMenu(vm, parentModule, savegameList, kLoadGameMenuButtonFileHashes, kLoadGameMenuButtonCollisionBounds,
+ 0x98620234, 0x201C2474,
+ 0x2023098E, &kLoadGameMenuMouseRect,
+ 0x04040409, 263, 142, kLoadGameMenuListBoxRect,
+ 0x10924C03, 0, 263, 48, kLoadGameMenuTextEditRect,
+ 0x0BC600A3, 0x0F960021) {
+
+}
+
+void LoadGameMenu::performAction() {
+ ((MenuModule*)_parentModule)->setLoadgameInfo(_listBox->getCurrIndex());
+ leaveScene(0);
+}
+
+static const uint32 kDeleteGameMenuButtonFileHashes[] = {
+ 0x8198E268, 0xDD0C4620, 0x81296520,
+ 0x8D284211, 0x8C004621, 0x07294020
+};
+
+static const NRect kDeleteGameMenuButtonCollisionBounds[] = {
+ NRect(518, 46, 595, 91),
+ NRect(524, 322, 599, 369),
+ NRect(395, 40, 462, 127),
+ NRect(405, 126, 460, 185),
+ NRect(397, 205, 456, 273),
+ NRect(395, 278, 452, 372)
+};
+
+static const NRect kDeleteGameMenuListBoxRect(0, 0, 320, 271);
+static const NRect kDeleteGameMenuTextEditRect(0, 0, 320, 17);
+
+DeleteGameMenu::DeleteGameMenu(NeverhoodEngine *vm, Module *parentModule, SavegameList *savegameList)
+ : GameStateMenu(vm, parentModule, savegameList, kDeleteGameMenuButtonFileHashes, kDeleteGameMenuButtonCollisionBounds,
+ 0x4080E01C, 0x728523ED,
+ 0x0E018400, NULL,
+ 0xA5584211, 61, 64, kDeleteGameMenuListBoxRect,
+ 0x250A3060, 0, 49, 414, kDeleteGameMenuTextEditRect,
+ 0x80083C01, 0x84181E81) {
+
+}
+
+void DeleteGameMenu::performAction() {
+ ((MenuModule*)_parentModule)->setDeletegameInfo(_listBox->getCurrIndex());
+ leaveScene(0);
+}
+
+QueryOverwriteMenu::QueryOverwriteMenu(NeverhoodEngine *vm, Module *parentModule, const Common::String &description)
+ : Scene(vm, parentModule) {
+
+ static const uint32 kQueryOverwriteMenuButtonFileHashes[] = {
+ 0x90312400,
+ 0x94C22A22
+ };
+
+ static const NRect kQueryOverwriteMenuCollisionBounds[] = {
+ NRect(145, 334, 260, 385),
+ NRect(365, 340, 477, 388)
+ };
+
+ setBackground(0x043692C4);
+ setPalette(0x043692C4);
+ insertScreenMouse(0x692C004B);
+ insertStaticSprite(0x08C0AC24, 200);
+
+ for (uint buttonIndex = 0; buttonIndex < 2; ++buttonIndex) {
+ Sprite *menuButton = insertSprite<MenuButton>(this, buttonIndex,
+ kQueryOverwriteMenuButtonFileHashes[buttonIndex], kQueryOverwriteMenuCollisionBounds[buttonIndex]);
+ addCollisionSprite(menuButton);
+ }
+
+ // Draw the query text to the background, each text line is centered
+ // NOTE The original had this text in its own class
+ FontSurface *fontSurface = new FontSurface(_vm, 0x94188D4D, 32, 7, 32, 11, 17);
+ Common::StringArray textLines;
+ textLines.push_back(description);
+ textLines.push_back("Game exists.");
+ textLines.push_back("Overwrite it?");
+ for (uint i = 0; i < textLines.size(); ++i)
+ fontSurface->drawString(_background->getSurface(), 106 + (423 - textLines[i].size() * 11) / 2,
+ 127 + 31 + i * 17, (const byte*)textLines[i].c_str());
+ delete fontSurface;
+
+ SetUpdateHandler(&Scene::update);
+ SetMessageHandler(&QueryOverwriteMenu::handleMessage);
+}
+
+uint32 QueryOverwriteMenu::handleMessage(int messageNum, const MessageParam &param, Entity *sender) {
+ Scene::handleMessage(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x2000:
+ // Handle menu button click
+ leaveScene(param.asInteger());
+ break;
+ }
+ return 0;
+}
+
+} // End of namespace Neverhood
diff --git a/engines/neverhood/menumodule.h b/engines/neverhood/menumodule.h
new file mode 100644
index 0000000000..08858ad204
--- /dev/null
+++ b/engines/neverhood/menumodule.h
@@ -0,0 +1,265 @@
+/* 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_MENUMODULE_H
+#define NEVERHOOD_MENUMODULE_H
+
+#include "common/str.h"
+#include "common/str-array.h"
+#include "neverhood/neverhood.h"
+#include "neverhood/module.h"
+#include "neverhood/scene.h"
+
+namespace Neverhood {
+
+struct SavegameItem {
+ int slotNum;
+ Common::String description;
+};
+
+typedef Common::Array<SavegameItem> SavegameList;
+
+class MenuModule : public Module {
+public:
+ MenuModule(NeverhoodEngine *vm, Module *parentModule, int which);
+ virtual ~MenuModule();
+ void setLoadgameInfo(uint index);
+ void setSavegameInfo(const Common::String &description, uint index, bool newSavegame);
+ void setDeletegameInfo(uint index);
+protected:
+ int _sceneNum;
+ byte *_savedPaletteData;
+ SavegameList *_savegameList;
+ Common::String _savegameDescription;
+ int _savegameSlot;
+ void createScene(int sceneNum, int which);
+ void updateScene();
+ uint32 handleMessage(int messageNum, const MessageParam &param, Entity *sender);
+ void createLoadGameMenu();
+ void createSaveGameMenu();
+ void createDeleteGameMenu();
+ void handleLoadGameMenuAction(bool doLoad);
+ void handleSaveGameMenuAction(bool doSave, bool doQuery);
+ void handleDeleteGameMenuAction(bool doDelete);
+ void loadSavegameList();
+};
+
+class MenuButton : public StaticSprite {
+public:
+ MenuButton(NeverhoodEngine *vm, Scene *parentScene, uint buttonIndex, uint32 fileHash, const NRect &collisionBounds);
+protected:
+ Scene *_parentScene;
+ int _countdown;
+ uint _buttonIndex;
+ void update();
+ uint32 handleMessage(int messageNum, const MessageParam &param, Entity *sender);
+};
+
+class MainMenu : public Scene {
+public:
+ MainMenu(NeverhoodEngine *vm, Module *parentModule);
+protected:
+ Sprite *_musicOnButton;
+ uint32 handleMessage(int messageNum, const MessageParam &param, Entity *sender);
+};
+
+class CreditsScene : public Scene {
+public:
+ CreditsScene(NeverhoodEngine *vm, Module *parentModule, bool canAbort);
+ virtual ~CreditsScene();
+protected:
+ int _screenIndex;
+ int _countdown;
+ MusicResource *_musicResource;
+ uint32 _ticksTime;
+ uint32 _ticksDuration;
+ bool _canAbort;
+ void update();
+ uint32 handleMessage(int messageNum, const MessageParam &param, Entity *sender);
+};
+
+class Widget;
+class GameStateMenu;
+
+class Widget : public StaticSprite {
+public:
+ Widget(NeverhoodEngine *vm, int16 x, int16 y, GameStateMenu *parentScene,
+ int baseObjectPriority, int baseSurfacePriority);
+ virtual void onClick();
+ virtual void setPosition(int16 x, int16 y);
+ virtual void refreshPosition();
+ virtual void initialize();
+ virtual int16 getWidth();
+ virtual int16 getHeight();
+ virtual void enterWidget();
+ virtual void exitWidget();
+protected:
+ GameStateMenu *_parentScene;
+ int _baseObjectPriority;
+ int _baseSurfacePriority;
+ void update();
+ uint32 handleMessage(int messageNum, const MessageParam &param, Entity *sender);
+};
+
+class TextLabelWidget : public Widget {
+public:
+ TextLabelWidget(NeverhoodEngine *vm, int16 x, int16 y, GameStateMenu *parentScene,
+ int baseObjectPriority, int baseSurfacePriority,
+ const byte *string, int stringLen, BaseSurface *drawSurface, int16 tx, int16 ty, FontSurface *fontSurface);
+ virtual void initialize();
+ virtual int16 getWidth();
+ virtual int16 getHeight();
+ void drawString(int maxStringLength);
+ void clear();
+ void setString(const byte *string, int stringLen);
+ FontSurface *getFontSurface() const { return _fontSurface; }
+protected:
+ BaseSurface *_drawSurface;
+ int16 _tx, _ty;
+ FontSurface *_fontSurface;
+ const byte *_string;
+ int _stringLen;
+};
+
+class TextEditWidget : public Widget {
+public:
+ TextEditWidget(NeverhoodEngine *vm, int16 x, int16 y, GameStateMenu *parentScene,
+ int maxStringLength, FontSurface *fontSurface, uint32 fileHash, const NRect &rect);
+ ~TextEditWidget();
+ virtual void onClick();
+ virtual void initialize();
+ virtual void enterWidget();
+ virtual void exitWidget();
+ void setCursor(uint32 cursorFileHash, int16 cursorWidth, int16 cursorHeight);
+ void drawCursor();
+ void updateString();
+ Common::String& getString();
+ void setString(const Common::String &string);
+ void handleAsciiKey(char ch);
+ void handleKeyDown(Common::KeyCode keyCode);
+ void refresh();
+ void setReadOnly(bool value) { _readOnly = value; }
+ bool isReadOnly() const { return _readOnly; }
+ bool isModified() const { return _modified; }
+protected:
+ NRect _rect;
+ uint32 _fileHash;
+ int _maxVisibleChars;
+ int _maxStringLength;
+ int _cursorPos;
+ int _cursorTicks;
+ Common::String _entryString;
+ FontSurface *_fontSurface;
+ TextLabelWidget *_textLabelWidget;
+ BaseSurface *_cursorSurface;
+ uint32 _cursorFileHash;
+ int16 _cursorWidth, _cursorHeight;
+ bool _modified;
+ bool _readOnly;
+ void update();
+ uint32 handleMessage(int messageNum, const MessageParam &param, Entity *sender);
+};
+
+class SavegameListBox : public Widget {
+public:
+ SavegameListBox(NeverhoodEngine *vm, int16 x, int16 y, GameStateMenu *parentScene,
+ SavegameList *savegameList, FontSurface *fontSurface, uint32 bgFileHash, const NRect &rect);
+ virtual void onClick();
+ virtual void initialize();
+ void buildItems();
+ void drawItems();
+ void refresh();
+ void scrollUp();
+ void scrollDown();
+ void pageUp();
+ void pageDown();
+ uint getCurrIndex() const { return _currIndex; }
+protected:
+ const NRect _rect;
+ uint32 _bgFileHash;
+ int _maxStringLength;
+ Common::Array<TextLabelWidget*> _textLabelItems;
+ int _firstVisibleItem;
+ int _lastVisibleItem;
+ SavegameList *_savegameList;
+ FontSurface *_fontSurface;
+ uint _currIndex;
+ int _maxVisibleItemsCount;
+};
+
+class GameStateMenu : public Scene {
+public:
+ GameStateMenu(NeverhoodEngine *vm, Module *parentModule, SavegameList *savegameList,
+ const uint32 *buttonFileHashes, const NRect *buttonCollisionBounds,
+ uint32 backgroundFileHash, uint32 fontFileHash,
+ uint32 mouseFileHash, const NRect *mouseRect,
+ uint32 listBoxBackgroundFileHash, int16 listBoxX, int16 listBoxY, const NRect &listBoxRect,
+ uint32 textEditBackgroundFileHash, uint32 textEditCursorFileHash, int16 textEditX, int16 textEditY, const NRect &textEditRect,
+ uint32 textFileHash1, uint32 textFileHash2);
+ virtual ~GameStateMenu();
+ NPoint getMousePos();
+ virtual void setCurrWidget(Widget *newWidget);
+ virtual Widget *getCurrWidget() { return _currWidget; }
+ virtual void refreshDescriptionEdit();
+protected:
+ Widget *_currWidget;
+ SavegameList *_savegameList;
+ FontSurface *_fontSurface;
+ SavegameListBox *_listBox;
+ TextEditWidget *_textEditWidget;
+ Common::String _savegameDescription;
+ uint32 handleMessage(int messageNum, const MessageParam &param, Entity *sender);
+ virtual void performAction();
+};
+
+class SaveGameMenu : public GameStateMenu {
+public:
+ SaveGameMenu(NeverhoodEngine *vm, Module *parentModule, SavegameList *savegameList);
+protected:
+ virtual void performAction();
+};
+
+class LoadGameMenu : public GameStateMenu {
+public:
+ LoadGameMenu(NeverhoodEngine *vm, Module *parentModule, SavegameList *savegameList);
+protected:
+ virtual void performAction();
+};
+
+class DeleteGameMenu : public GameStateMenu {
+public:
+ DeleteGameMenu(NeverhoodEngine *vm, Module *parentModule, SavegameList *savegameList);
+protected:
+ virtual void performAction();
+};
+
+class QueryOverwriteMenu : public Scene {
+public:
+ QueryOverwriteMenu(NeverhoodEngine *vm, Module *parentModule, const Common::String &description);
+protected:
+ void update();
+ uint32 handleMessage(int messageNum, const MessageParam &param, Entity *sender);
+};
+
+} // End of namespace Neverhood
+
+#endif /* NEVERHOOD_MENUMODULE_H */
diff --git a/engines/neverhood/messages.h b/engines/neverhood/messages.h
new file mode 100644
index 0000000000..78c66868d5
--- /dev/null
+++ b/engines/neverhood/messages.h
@@ -0,0 +1,37 @@
+/* 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_MESSAGES_H
+#define NEVERHOOD_MESSAGES_H
+
+namespace Neverhood {
+
+enum NeverhoodMessage {
+ NM_KLAYMEN_PICKUP = 0x4812,
+ NM_KLAYMEN_PRESS_BUTTON = 0x4816,
+ NM_KLAYMEN_INSERT_DISK = 0x481A,
+ NM_KLAYMEN_RELEASE_LEVER = 0x4827
+};
+
+} // End of namespace Neverhood
+
+#endif /* NEVERHOOD_MESSAGES_H */
diff --git a/engines/neverhood/microtiles.cpp b/engines/neverhood/microtiles.cpp
new file mode 100644
index 0000000000..3dd6475046
--- /dev/null
+++ b/engines/neverhood/microtiles.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/microtiles.h"
+
+namespace Neverhood {
+
+MicroTileArray::MicroTileArray(int16 width, int16 height) {
+ _tilesW = (width / TileSize) + ((width % TileSize) > 0 ? 1 : 0);
+ _tilesH = (height / TileSize) + ((height % TileSize) > 0 ? 1 : 0);
+ _tiles = new BoundingBox[_tilesW * _tilesH];
+ clear();
+}
+
+MicroTileArray::~MicroTileArray() {
+ delete[] _tiles;
+}
+
+void MicroTileArray::addRect(Common::Rect r) {
+
+ int ux0, uy0, ux1, uy1;
+ int tx0, ty0, tx1, ty1;
+ int ix0, iy0, ix1, iy1;
+
+ r.clip(Common::Rect(0, 0, 639, 479));
+
+ ux0 = r.left / TileSize;
+ uy0 = r.top / TileSize;
+ ux1 = r.right / TileSize;
+ uy1 = r.bottom / TileSize;
+
+ tx0 = r.left % TileSize;
+ ty0 = r.top % TileSize;
+ tx1 = r.right % TileSize;
+ ty1 = r.bottom % TileSize;
+
+ for (int yc = uy0; yc <= uy1; yc++) {
+ for (int xc = ux0; xc <= ux1; xc++) {
+ ix0 = (xc == ux0) ? tx0 : 0;
+ ix1 = (xc == ux1) ? tx1 : TileSize - 1;
+ iy0 = (yc == uy0) ? ty0 : 0;
+ iy1 = (yc == uy1) ? ty1 : TileSize - 1;
+ updateBoundingBox(_tiles[xc + yc * _tilesW], ix0, iy0, ix1, iy1);
+ }
+ }
+
+}
+
+void MicroTileArray::clear() {
+ memset(_tiles, 0, _tilesW * _tilesH * sizeof(BoundingBox));
+}
+
+byte MicroTileArray::TileX0(const BoundingBox &boundingBox) {
+ return (boundingBox >> 24) & 0xFF;
+}
+
+byte MicroTileArray::TileY0(const BoundingBox &boundingBox) {
+ return (boundingBox >> 16) & 0xFF;
+}
+
+byte MicroTileArray::TileX1(const BoundingBox &boundingBox) {
+ return (boundingBox >> 8) & 0xFF;
+}
+
+byte MicroTileArray::TileY1(const BoundingBox &boundingBox) {
+ return boundingBox & 0xFF;
+}
+
+bool MicroTileArray::isBoundingBoxEmpty(const BoundingBox &boundingBox) {
+ return boundingBox == EmptyBoundingBox;
+}
+
+bool MicroTileArray::isBoundingBoxFull(const BoundingBox &boundingBox) {
+ return boundingBox == FullBoundingBox;
+}
+
+void MicroTileArray::setBoundingBox(BoundingBox &boundingBox, byte x0, byte y0, byte x1, byte y1) {
+ boundingBox = (x0 << 24) | (y0 << 16) | (x1 << 8) | y1;
+}
+
+void MicroTileArray::updateBoundingBox(BoundingBox &boundingBox, byte x0, byte y0, byte x1, byte y1) {
+ if (!isBoundingBoxEmpty(boundingBox)) {
+ x0 = MIN(TileX0(boundingBox), x0);
+ y0 = MIN(TileY0(boundingBox), y0);
+ x1 = MAX(TileX1(boundingBox), x1);
+ y1 = MAX(TileY1(boundingBox), y1);
+ }
+ setBoundingBox(boundingBox, x0, y0, x1, y1);
+}
+
+RectangleList *MicroTileArray::getRectangles() {
+
+ RectangleList *rects = new RectangleList();
+
+ int x, y;
+ int x0, y0, x1, y1;
+ int i = 0;
+
+ for (y = 0; y < _tilesH; ++y) {
+ for (x = 0; x < _tilesW; ++x) {
+ int finish = 0;
+ BoundingBox boundingBox = _tiles[i];
+
+ if (isBoundingBoxEmpty(boundingBox)) {
+ ++i;
+ continue;
+ }
+
+ x0 = (x * TileSize) + TileX0(boundingBox);
+ y0 = (y * TileSize) + TileY0(boundingBox);
+ y1 = (y * TileSize) + TileY1(boundingBox);
+
+ if (TileX1(boundingBox) == TileSize - 1 && x != _tilesW - 1) { // check if the tile continues
+ while (!finish) {
+ ++x;
+ ++i;
+ if (x == _tilesW || i >= _tilesW * _tilesH ||
+ TileY0(_tiles[i]) != TileY0(boundingBox) ||
+ TileY1(_tiles[i]) != TileY1(boundingBox) ||
+ TileX0(_tiles[i]) != 0)
+ {
+ --x;
+ --i;
+ finish = 1;
+ }
+ }
+ }
+
+ x1 = (x * TileSize) + TileX1(_tiles[i]);
+
+ rects->push_back(Common::Rect(x0, y0, x1 + 1, y1 + 1));
+
+ ++i;
+ }
+ }
+
+ return rects;
+}
+
+} // End of namespace Neverhood
diff --git a/engines/neverhood/microtiles.h b/engines/neverhood/microtiles.h
new file mode 100644
index 0000000000..29af3d956a
--- /dev/null
+++ b/engines/neverhood/microtiles.h
@@ -0,0 +1,64 @@
+/* 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_MICROTILES_H
+#define NEVERHOOD_MICROTILES_H
+
+#include "common/scummsys.h"
+#include "common/list.h"
+#include "common/util.h"
+#include "common/rect.h"
+
+namespace Neverhood {
+
+typedef uint32 BoundingBox;
+
+const BoundingBox FullBoundingBox = 0x00001F1F;
+const BoundingBox EmptyBoundingBox = 0x00000000;
+const int TileSize = 32;
+
+typedef Common::List<Common::Rect> RectangleList;
+
+class MicroTileArray {
+public:
+ MicroTileArray(int16 width, int16 height);
+ ~MicroTileArray();
+ void addRect(Common::Rect r);
+ void clear();
+ RectangleList *getRectangles();
+protected:
+ BoundingBox *_tiles;
+ int16 _tilesW, _tilesH;
+ byte TileX0(const BoundingBox &boundingBox);
+ byte TileY0(const BoundingBox &boundingBox);
+ byte TileX1(const BoundingBox &boundingBox);
+ byte TileY1(const BoundingBox &boundingBox);
+ bool isBoundingBoxEmpty(const BoundingBox &boundingBox);
+ bool isBoundingBoxFull(const BoundingBox &boundingBox);
+ void setBoundingBox(BoundingBox &boundingBox, byte x0, byte y0, byte x1, byte y1);
+ void updateBoundingBox(BoundingBox &boundingBox, byte x0, byte y0, byte x1, byte y1);
+};
+
+} // namespace Neverhood
+
+#endif // NEVERHOOD_MICROTILES_H
diff --git a/engines/neverhood/module.cpp b/engines/neverhood/module.cpp
new file mode 100644
index 0000000000..e384b5a4d2
--- /dev/null
+++ b/engines/neverhood/module.cpp
@@ -0,0 +1,123 @@
+/* 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/module.h"
+#include "neverhood/navigationscene.h"
+#include "neverhood/smackerscene.h"
+#include "neverhood/modules/module1000.h"
+#include "neverhood/modules/module1500.h"
+
+namespace Neverhood {
+
+Module::Module(NeverhoodEngine *vm, Module *parentModule)
+ : Entity(vm, 0), _parentModule(parentModule), _childObject(NULL),
+ _done(false), _sceneType(kSceneTypeNormal) {
+
+ SetMessageHandler(&Module::handleMessage);
+
+}
+
+Module::~Module() {
+ delete _childObject;
+}
+
+void Module::draw() {
+ if (_childObject)
+ _childObject->draw();
+}
+
+uint32 Module::handleMessage(int messageNum, const MessageParam &param, Entity *sender) {
+ switch (messageNum) {
+ case 0x0008:
+ sendMessage(_parentModule, 8, 0);
+ return 0;
+ case 0x1009:
+ _moduleResult = param.asInteger();
+ _done = true;
+ return 0;
+ case 0x100A:
+ case 0x1023:
+ case 0x1024:
+ // Unused resource preloading messages
+ return 0;
+ default:
+ if (_childObject && sender == _parentModule)
+ return sender->sendMessage(_childObject, messageNum, param);
+ }
+ return 0;
+}
+
+NavigationScene *Module::navigationScene() {
+ return (NavigationScene*)_childObject;
+}
+
+void Module::createNavigationScene(uint32 navigationListId, int navigationIndex, const byte *itemsTypes) {
+ _sceneType = kSceneTypeNavigation;
+ _childObject = new NavigationScene(_vm, this, navigationListId, navigationIndex, itemsTypes);
+}
+
+void Module::createSmackerScene(uint32 fileHash, bool doubleSurface, bool canSkip, bool canAbort) {
+ _sceneType = kSceneTypeSmacker;
+ SmackerScene *smackerScene = new SmackerScene(_vm, this, doubleSurface, canSkip, canAbort);
+ smackerScene->setFileHash(fileHash);
+ smackerScene->nextVideo();
+ _childObject = smackerScene;
+}
+
+void Module::createSmackerScene(const uint32 *fileHashList, bool doubleSurface, bool canSkip, bool canAbort) {
+ _sceneType = kSceneTypeSmacker;
+ SmackerScene *smackerScene = new SmackerScene(_vm, this, doubleSurface, canSkip, canAbort);
+ smackerScene->setFileHashList(fileHashList);
+ smackerScene->nextVideo();
+ _childObject = smackerScene;
+}
+
+void Module::createStaticScene(uint32 backgroundFileHash, uint32 cursorFileHash) {
+ _childObject = new StaticScene(_vm, this, backgroundFileHash, cursorFileHash);
+}
+
+void Module::createDemoScene() {
+ _childObject = new Scene1501(_vm, this, 0x0009B624, 0, 288, 0);
+}
+
+bool Module::updateChild() {
+ if (_childObject) {
+ _childObject->handleUpdate();
+ if (_done) {
+ _done = false;
+ // Save the last area type if it's a NavigationScene for further processing
+ if (_sceneType == kSceneTypeNavigation)
+ _navigationAreaType = navigationScene()->getNavigationAreaType();
+ delete _childObject;
+ _childObject = NULL;
+ _sceneType = kSceneTypeNormal;
+ return false;
+ }
+ }
+ return true;
+}
+
+void Module::leaveModule(uint32 result) {
+ sendMessage(_parentModule, 0x1009, result);
+}
+
+} // End of namespace Neverhood
diff --git a/engines/neverhood/module.h b/engines/neverhood/module.h
new file mode 100644
index 0000000000..e98012cbea
--- /dev/null
+++ b/engines/neverhood/module.h
@@ -0,0 +1,71 @@
+/* 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_MODULE_H
+#define NEVERHOOD_MODULE_H
+
+#include "neverhood/neverhood.h"
+#include "neverhood/background.h"
+#include "neverhood/entity.h"
+#include "neverhood/graphics.h"
+#include "neverhood/mouse.h"
+#include "neverhood/palette.h"
+#include "neverhood/screen.h"
+
+namespace Neverhood {
+
+class NavigationScene;
+
+enum SceneType {
+ kSceneTypeNormal,
+ kSceneTypeSmacker,
+ kSceneTypeNavigation
+};
+
+class Module : public Entity {
+public:
+ Module(NeverhoodEngine *vm, Module *parentModule);
+ virtual ~Module();
+ virtual void draw();
+protected:
+ Module *_parentModule;
+ Entity *_childObject;
+ bool _done;
+ uint32 _moduleResult;
+ SceneType _sceneType;
+ int _navigationAreaType;
+ uint32 handleMessage(int messageNum, const MessageParam &param, Entity *sender);
+ NavigationScene *navigationScene();
+ void createNavigationScene(uint32 navigationListId, int navigationIndex, const byte *itemsTypes = NULL);
+ void createSmackerScene(uint32 fileHash, bool doubleSurface, bool canSkip, bool canAbort);
+ void createSmackerScene(const uint32 *fileHashList, bool doubleSurface, bool canSkip, bool canAbort);
+ void createStaticScene(uint32 backgroundFileHash, uint32 cursorFileHash);
+ void createDemoScene();
+ bool updateChild();
+ void leaveModule(uint32 result);
+};
+
+} // End of namespace Neverhood
+
+#endif /* NEVERHOOD_MODULE_H */
diff --git a/engines/neverhood/module.mk b/engines/neverhood/module.mk
new file mode 100644
index 0000000000..714fc3079b
--- /dev/null
+++ b/engines/neverhood/module.mk
@@ -0,0 +1,58 @@
+MODULE := engines/neverhood
+
+MODULE_OBJS = \
+ background.o \
+ blbarchive.o \
+ detection.o \
+ diskplayerscene.o \
+ entity.o \
+ gamemodule.o \
+ gamevars.o \
+ graphics.o \
+ klaymen.o \
+ menumodule.o \
+ microtiles.o \
+ module.o \
+ modules/module1000.o \
+ modules/module1100.o \
+ modules/module1200.o \
+ modules/module1300.o \
+ modules/module1400.o \
+ modules/module1500.o \
+ modules/module1600.o \
+ modules/module1700.o \
+ modules/module1800.o \
+ modules/module1900.o \
+ modules/module2000.o \
+ modules/module2100.o \
+ modules/module2200.o \
+ modules/module2300.o \
+ modules/module2400.o \
+ modules/module2500.o \
+ modules/module2600.o \
+ modules/module2700.o \
+ modules/module2800.o \
+ modules/module2900.o \
+ modules/module3000.o \
+ mouse.o \
+ navigationscene.o \
+ neverhood.o \
+ palette.o \
+ resource.o \
+ resourceman.o \
+ saveload.o \
+ scene.o \
+ screen.o \
+ smackerscene.o \
+ smackerplayer.o \
+ sound.o \
+ sprite.o \
+ staticdata.o
+
+# This module can be built as a plugin
+ifeq ($(ENABLE_NEVERHOOD), DYNAMIC_PLUGIN)
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/rules.mk
diff --git a/engines/neverhood/modules/module1000.cpp b/engines/neverhood/modules/module1000.cpp
new file mode 100644
index 0000000000..415d0f72b1
--- /dev/null
+++ b/engines/neverhood/modules/module1000.cpp
@@ -0,0 +1,1699 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "neverhood/modules/module1000.h"
+
+namespace Neverhood {
+
+Module1000::Module1000(NeverhoodEngine *vm, Module *parentModule, int which)
+ : Module(vm, parentModule) {
+
+ debug("Create Module1000(%d)", which);
+
+ _musicFileHash = getGlobalVar(V_ENTRANCE_OPEN) ? 0x81106480 : 0x00103144;
+
+ _vm->_soundMan->addMusic(0x03294419, 0x061880C6);
+ _vm->_soundMan->addMusic(0x03294419, _musicFileHash);
+
+ if (which < 0)
+ createScene(_vm->gameState().sceneNum, -1);
+ else if (which == 0)
+ createScene(0, 0);
+ else if (which == 1)
+ createScene(1, 1);
+
+}
+
+Module1000::~Module1000() {
+ _vm->_soundMan->deleteMusicGroup(0x03294419);
+}
+
+void Module1000::createScene(int sceneNum, int which) {
+ debug("Module1000::createScene(%d, %d)", sceneNum, which);
+ _sceneNum = sceneNum;
+ switch (_sceneNum) {
+ case 0:
+ _vm->gameState().sceneNum = 0;
+ _vm->_soundMan->startMusic(0x061880C6, 0, 0);
+ _childObject = new Scene1001(_vm, this, which);
+ break;
+ case 1:
+ _vm->gameState().sceneNum = 1;
+ _vm->_soundMan->startMusic(0x061880C6, 0, 0);
+ _childObject = new Scene1002(_vm, this, which);
+ break;
+ case 2:
+ _vm->gameState().sceneNum = 2;
+ _vm->_soundMan->startMusic(0x061880C6, 0, 0);
+ createStaticScene(0xC084110C, 0x41108C00);
+ break;
+ case 3:
+ _vm->gameState().sceneNum = 3;
+ _vm->_soundMan->stopMusic(0x061880C6, 0, 2);
+ _childObject = new Scene1004(_vm, this, which);
+ break;
+ case 4:
+ _vm->gameState().sceneNum = 4;
+ _vm->_soundMan->stopMusic(0x061880C6, 0, 0);
+ _vm->_soundMan->startMusic(_musicFileHash, 0, 0);
+ _childObject = new Scene1005(_vm, this, which);
+ break;
+ }
+ SetUpdateHandler(&Module1000::updateScene);
+ _childObject->handleUpdate();
+}
+
+void Module1000::updateScene() {
+ if (!updateChild()) {
+ switch (_sceneNum) {
+ case 0:
+ if (_moduleResult == 2)
+ createScene(2, 0);
+ else
+ createScene(1, 0);
+ break;
+ case 1:
+ if (_moduleResult == 1)
+ leaveModule(0);
+ else if (_moduleResult == 2) {
+ if (_vm->isDemo())
+ // Demo version returns to the same scene
+ createScene(1, 2);
+ else
+ createScene(3, 0);
+ } else
+ createScene(0, 1);
+ break;
+ case 2:
+ createScene(0, 2);
+ break;
+ case 3:
+ if (_moduleResult == 1)
+ createScene(4, 0);
+ else
+ createScene(1, 2);
+ break;
+ case 4:
+ _vm->_soundMan->stopMusic(_musicFileHash, 0, 1);
+ createScene(3, 1);
+ break;
+ }
+ }
+}
+
+// Scene1001
+
+AsScene1001Door::AsScene1001Door(NeverhoodEngine *vm)
+ : AnimatedSprite(vm, 1100) {
+
+ createSurface(800, 137, 242);
+ _x = 726;
+ _y = 440;
+ stShowIdleDoor();
+ loadSound(1, 0xED403E03);
+ SetUpdateHandler(&AnimatedSprite::update);
+ SetMessageHandler(&AsScene1001Door::handleMessage);
+}
+
+uint32 AsScene1001Door::handleMessage(int messageNum, const MessageParam &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), _countdown(0) {
+
+ _soundFileHash = soundFileHash != 0 ? soundFileHash : 0x44141000;
+ _fileHashes[0] = fileHash1;
+ _fileHashes[1] = fileHash2;
+ createSurface(surfacePriority, 40, 40);
+ loadSprite(fileHash1, kSLFDefDrawOffset | kSLFDefPosition);
+ setVisible(false);
+ SetUpdateHandler(&SsCommonPressButton::update);
+ SetMessageHandler(&SsCommonPressButton::handleMessage);
+}
+
+void SsCommonPressButton::setFileHashes(uint32 fileHash1, uint32 fileHash2) {
+ _fileHashes[0] = fileHash1;
+ _fileHashes[1] = fileHash2;
+ loadSprite(_status == 2 ? fileHash2 : fileHash1, kSLFDefDrawOffset | kSLFDefPosition);
+}
+
+void SsCommonPressButton::update() {
+ if (_countdown != 0 && (--_countdown) == 0) {
+ if (_status == 1) {
+ _status = 2;
+ loadSprite(_fileHashes[1], kSLFDefDrawOffset | kSLFDefPosition);
+ _countdown = 4;
+ } else if (_status == 2) {
+ _status = 3;
+ loadSprite(_fileHashes[0], kSLFDefDrawOffset | kSLFDefPosition);
+ _countdown = 4;
+ } else if (_status == 3) {
+ _status = 0;
+ setVisible(false);
+ }
+ }
+}
+
+uint32 SsCommonPressButton::handleMessage(int messageNum, const MessageParam &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..4f2d9e8fd2
--- /dev/null
+++ b/engines/neverhood/modules/module2200.cpp
@@ -0,0 +1,2565 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "neverhood/modules/module2200.h"
+#include "neverhood/modules/module1000.h"
+#include "neverhood/modules/module1200.h"
+#include "neverhood/gamemodule.h"
+#include "neverhood/diskplayerscene.h"
+
+namespace Neverhood {
+
+Module2200::Module2200(NeverhoodEngine *vm, Module *parentModule, int which)
+ : Module(vm, parentModule) {
+
+ debug("Create Module2200(%d)", which);
+
+ _vm->_soundMan->addMusic(0x11391412, 0x601C908C);
+
+ if (which < 0)
+ createScene(_vm->gameState().sceneNum, -1);
+ else
+ createScene(0, 0);
+
+}
+
+Module2200::~Module2200() {
+ _vm->_soundMan->deleteGroup(0x11391412);
+}
+
+void Module2200::createScene(int sceneNum, int which) {
+ debug("Module2200::createScene(%d, %d)", sceneNum, which);
+ _sceneNum = sceneNum;
+ switch (_sceneNum) {
+ case 0:
+ _vm->gameState().sceneNum = 0;
+ _childObject = new Scene2201(_vm, this, which);
+ break;
+ case 1:
+ _vm->gameState().sceneNum = 1;
+ _vm->_soundMan->startMusic(0x601C908C, 0, 2);
+ _childObject = new Scene2202(_vm, this, which);
+ break;
+ case 2:
+ _vm->gameState().sceneNum = 2;
+ _vm->_soundMan->startMusic(0x601C908C, 0, 2);
+ _childObject = new Scene2203(_vm, this, which);
+ break;
+ case 3:
+ _vm->gameState().sceneNum = 3;
+ _vm->_soundMan->stopMusic(0x601C908C, 0, 2);
+ _childObject = new DiskplayerScene(_vm, this, 3);
+ break;
+ case 4:
+ _vm->gameState().sceneNum = 4;
+ _vm->_soundMan->stopMusic(0x601C908C, 0, 2);
+ _childObject = new Scene2205(_vm, this, which);
+ break;
+ case 5:
+ _vm->gameState().sceneNum = 5;
+ _vm->_soundMan->stopMusic(0x601C908C, 0, 2);
+ _childObject = new Scene2206(_vm, this, which);
+ break;
+ case 6:
+ _vm->gameState().sceneNum = 6;
+ _childObject = new Scene2207(_vm, this);
+ break;
+ case 7:
+ if (which >= 0)
+ _vm->gameState().which = _vm->gameState().sceneNum;
+ _vm->gameState().sceneNum = 7;
+ _childObject = new Scene2208(_vm, this, which);
+ break;
+ case 8:
+ _vm->gameState().sceneNum = 8;
+ _childObject = new Scene2208(_vm, this, which);
+ break;
+ case 9:
+ _vm->gameState().sceneNum = 9;
+ createHallOfRecordsScene(which, 0x004B7180);
+ break;
+ case 10:
+ _vm->gameState().sceneNum = 10;
+ createHallOfRecordsScene(which, 0x004B7198);
+ break;
+ case 11:
+ _vm->gameState().sceneNum = 11;
+ createHallOfRecordsScene(which, 0x004B71B0);
+ break;
+ case 12:
+ _vm->gameState().sceneNum = 12;
+ createHallOfRecordsScene(which, 0x004B71C8);
+ break;
+ case 13:
+ _vm->gameState().sceneNum = 13;
+ createHallOfRecordsScene(which, 0x004B71E0);
+ break;
+ case 14:
+ _vm->gameState().sceneNum = 14;
+ createHallOfRecordsScene(which, 0x004B71F8);
+ break;
+ case 15:
+ _vm->gameState().sceneNum = 15;
+ createHallOfRecordsScene(which, 0x004B7210);
+ break;
+ case 16:
+ _vm->gameState().sceneNum = 16;
+ createHallOfRecordsScene(which, 0x004B7228);
+ break;
+ case 17:
+ _vm->gameState().sceneNum = 17;
+ createHallOfRecordsScene(which, 0x004B7240);
+ break;
+ case 18:
+ _vm->gameState().sceneNum = 18;
+ createHallOfRecordsScene(which, 0x004B7258);
+ break;
+ case 19:
+ _vm->gameState().sceneNum = 19;
+ createHallOfRecordsScene(which, 0x004B7270);
+ break;
+ case 20:
+ _vm->gameState().sceneNum = 20;
+ createHallOfRecordsScene(which, 0x004B7288);
+ break;
+ case 21:
+ _vm->gameState().sceneNum = 21;
+ createHallOfRecordsScene(which, 0x004B72A0);
+ break;
+ case 22:
+ _vm->gameState().sceneNum = 22;
+ createHallOfRecordsScene(which, 0x004B72B8);
+ break;
+ case 23:
+ _vm->gameState().sceneNum = 23;
+ createHallOfRecordsScene(which, 0x004B72D0);
+ break;
+ case 24:
+ _vm->gameState().sceneNum = 24;
+ createHallOfRecordsScene(which, 0x004B72E8);
+ break;
+ case 25:
+ _vm->gameState().sceneNum = 25;
+ createHallOfRecordsScene(which, 0x004B7300);
+ break;
+ case 26:
+ _vm->gameState().sceneNum = 26;
+ createHallOfRecordsScene(which, 0x004B7318);
+ break;
+ case 27:
+ _vm->gameState().sceneNum = 27;
+ createHallOfRecordsScene(which, 0x004B7330);
+ break;
+ case 28:
+ _vm->gameState().sceneNum = 28;
+ createHallOfRecordsScene(which, 0x004B7348);
+ break;
+ case 29:
+ _vm->gameState().sceneNum = 29;
+ createHallOfRecordsScene(which, 0x004B7360);
+ break;
+ case 30:
+ _vm->gameState().sceneNum = 30;
+ createHallOfRecordsScene(which, 0x004B7378);
+ break;
+ case 31:
+ _vm->gameState().sceneNum = 31;
+ createHallOfRecordsScene(which, 0x004B7390);
+ break;
+ case 32:
+ _vm->gameState().sceneNum = 32;
+ createHallOfRecordsScene(which, 0x004B73A8);
+ break;
+ case 33:
+ _vm->gameState().sceneNum = 33;
+ createHallOfRecordsScene(which, 0x004B73C0);
+ break;
+ case 34:
+ _vm->gameState().sceneNum = 34;
+ createHallOfRecordsScene(which, 0x004B73D8);
+ break;
+ case 35:
+ _vm->gameState().sceneNum = 35;
+ createHallOfRecordsScene(which, 0x004B73F0);
+ break;
+ case 36:
+ _vm->gameState().sceneNum = 36;
+ createHallOfRecordsScene(which, 0x004B7408);
+ break;
+ case 37:
+ _vm->gameState().sceneNum = 37;
+ createHallOfRecordsScene(which, 0x004B7420);
+ break;
+ case 38:
+ _vm->gameState().sceneNum = 38;
+ createHallOfRecordsScene(which, 0x004B7438);
+ break;
+ case 39:
+ _vm->gameState().sceneNum = 39;
+ createHallOfRecordsScene(which, 0x004B7450);
+ break;
+ case 40:
+ _vm->gameState().sceneNum = 40;
+ createHallOfRecordsScene(which, 0x004B7468);
+ break;
+ case 41:
+ _vm->gameState().sceneNum = 41;
+ _childObject = new Scene2242(_vm, this, which);
+ break;
+ case 42:
+ _vm->gameState().sceneNum = 42;
+ createHallOfRecordsScene(which, 0x004B7480);
+ break;
+ case 43:
+ _vm->gameState().sceneNum = 43;
+ createHallOfRecordsScene(which, 0x004B7498);
+ break;
+ case 44:
+ _vm->gameState().sceneNum = 44;
+ createHallOfRecordsScene(which, 0x004B74B0);
+ break;
+ case 45:
+ _vm->gameState().sceneNum = 45;
+ createHallOfRecordsScene(which, 0x004B74C8);
+ break;
+ case 46:
+ _vm->gameState().sceneNum = 46;
+ _childObject = new Scene2247(_vm, this, which);
+ break;
+ case 47:
+ _vm->gameState().sceneNum = 47;
+ if (!getGlobalVar(V_WORLDS_JOINED)) {
+ if (getGlobalVar(V_LIGHTS_ON))
+ createStaticScene(0x83110287, 0x10283839);
+ else
+ createStaticScene(0x83412B9D, 0x12B9983C);
+ } else {
+ if (getGlobalVar(V_LIGHTS_ON))
+ createStaticScene(0x48632087, 0x3208348E);
+ else
+ createStaticScene(0x08C74886, 0x74882084);
+ }
+ break;
+ }
+ SetUpdateHandler(&Module2200::updateScene);
+ _childObject->handleUpdate();
+}
+
+#define HallOfRecordsSceneLink(nextSceneNum, prevSceneNum) \
+ if (_moduleResult == 1) createScene(nextSceneNum, 0); else if (_moduleResult == 2) createScene(7, 0); else createScene(prevSceneNum, 1)
+
+void Module2200::updateScene() {
+ if (!updateChild()) {
+ switch (_sceneNum) {
+ case 0:
+ if (_moduleResult == 1)
+ createScene(2, 0);
+ else if (_moduleResult == 2)
+ createScene(1, 0);
+ else
+ leaveModule(0);
+ break;
+ case 1:
+ createScene(0, 2);
+ break;
+ case 2:
+ if (_moduleResult == 1)
+ createScene(4, 0);
+ else if (_moduleResult == 2)
+ createScene(3, 0);
+ else
+ createScene(0, 1);
+ break;
+ case 3:
+ createScene(2, 2);
+ break;
+ case 4:
+ if (_moduleResult == 1)
+ createScene(5, 0);
+ else if (_moduleResult == 2)
+ createScene(4, 2);
+ else
+ createScene(2, 1);
+ break;
+ case 5:
+ if (_moduleResult == 1)
+ createScene(46, 0);
+ else if (_moduleResult == 2)
+ createScene(6, 0);
+ else if (_moduleResult == 3)
+ createScene(8, 0);
+ else
+ createScene(4, 1);
+ break;
+ case 6:
+ createScene(5, 2);
+ break;
+ case 7:
+ createScene(_vm->gameState().which, 2);
+ break;
+ case 8:
+ createScene(5, 3);
+ break;
+ case 9:
+ HallOfRecordsSceneLink(10, 46);
+ break;
+ case 10:
+ HallOfRecordsSceneLink(11, 9);
+ break;
+ case 11:
+ HallOfRecordsSceneLink(12, 10);
+ break;
+ case 12:
+ HallOfRecordsSceneLink(13, 11);
+ break;
+ case 13:
+ HallOfRecordsSceneLink(14, 12);
+ break;
+ case 14:
+ HallOfRecordsSceneLink(15, 13);
+ break;
+ case 15:
+ HallOfRecordsSceneLink(16, 14);
+ break;
+ case 16:
+ HallOfRecordsSceneLink(17, 15);
+ break;
+ case 17:
+ HallOfRecordsSceneLink(18, 16);
+ break;
+ case 18:
+ HallOfRecordsSceneLink(19, 17);
+ break;
+ case 19:
+ HallOfRecordsSceneLink(20, 18);
+ break;
+ case 20:
+ HallOfRecordsSceneLink(21, 19);
+ break;
+ case 21:
+ HallOfRecordsSceneLink(22, 20);
+ break;
+ case 22:
+ HallOfRecordsSceneLink(23, 21);
+ break;
+ case 23:
+ HallOfRecordsSceneLink(24, 22);
+ break;
+ case 24:
+ HallOfRecordsSceneLink(25, 23);
+ break;
+ case 25:
+ HallOfRecordsSceneLink(26, 24);
+ break;
+ case 26:
+ HallOfRecordsSceneLink(27, 25);
+ break;
+ case 27:
+ HallOfRecordsSceneLink(28, 26);
+ break;
+ case 28:
+ HallOfRecordsSceneLink(29, 27);
+ break;
+ case 29:
+ HallOfRecordsSceneLink(30, 28);
+ break;
+ case 30:
+ HallOfRecordsSceneLink(31, 29);
+ break;
+ case 31:
+ HallOfRecordsSceneLink(32, 30);
+ break;
+ case 32:
+ HallOfRecordsSceneLink(33, 31);
+ break;
+ case 33:
+ HallOfRecordsSceneLink(34, 32);
+ break;
+ case 34:
+ HallOfRecordsSceneLink(42, 33);
+ break;
+ case 35:
+ HallOfRecordsSceneLink(36, 45);
+ break;
+ case 36:
+ HallOfRecordsSceneLink(37, 35);
+ break;
+ case 37:
+ HallOfRecordsSceneLink(38, 36);
+ break;
+ case 38:
+ HallOfRecordsSceneLink(39, 37);
+ break;
+ case 39:
+ HallOfRecordsSceneLink(40, 38);
+ break;
+ case 40:
+ HallOfRecordsSceneLink(41, 39);
+ break;
+ case 41:
+ HallOfRecordsSceneLink(47, 40);
+ break;
+ case 42:
+ HallOfRecordsSceneLink(43, 34);
+ break;
+ case 43:
+ HallOfRecordsSceneLink(44, 42);
+ break;
+ case 44:
+ HallOfRecordsSceneLink(45, 43);
+ break;
+ case 45:
+ HallOfRecordsSceneLink(35, 44);
+ break;
+ case 46:
+ HallOfRecordsSceneLink(9, 5);
+ break;
+ case 47:
+ createScene(41, 1);
+ break;
+ }
+ }
+}
+
+#undef HallOfRecordsSceneLink
+
+void Module2200::createHallOfRecordsScene(int which, uint32 hallOfRecordsInfoId) {
+ _childObject = new HallOfRecordsScene(_vm, this, which, hallOfRecordsInfoId);
+}
+
+// Scene2201
+
+AsScene2201CeilingFan::AsScene2201CeilingFan(NeverhoodEngine *vm)
+ : AnimatedSprite(vm, 1100) {
+
+ _x = 403;
+ _y = 259;
+ createSurface(100, 233, 96);
+ startAnimation(0x8600866, 0, -1);
+ SetUpdateHandler(&AnimatedSprite::update);
+}
+
+AsScene2201Door::AsScene2201Door(NeverhoodEngine *vm, Klaymen *klaymen, Sprite *ssDoorLight, bool isOpen)
+ : AnimatedSprite(vm, 1100), _klaymen(klaymen), _ssDoorLight(ssDoorLight), _countdown(0), _isOpen(isOpen) {
+
+ _x = 408;
+ _y = 290;
+ createSurface(900, 63, 266);
+ SetUpdateHandler(&AsScene2201Door::update);
+ SetMessageHandler(&AsScene2201Door::handleMessage);
+ if (_isOpen) {
+ startAnimation(0xE2CB0412, -1, -1);
+ _countdown = 48;
+ _newStickFrameIndex = STICK_LAST_FRAME;
+ } else {
+ startAnimation(0xE2CB0412, 0, -1);
+ _newStickFrameIndex = 0;
+ _ssDoorLight->setVisible(false);
+ }
+}
+
+void AsScene2201Door::update() {
+ if (_countdown != 0 && _isOpen && (--_countdown == 0))
+ stCloseDoor();
+ AnimatedSprite::update();
+}
+
+uint32 AsScene2201Door::handleMessage(int messageNum, const MessageParam &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;
+ debug(1, "TODO: Unused SurfacePriority: %d", surfacePriority);
+ loadSprite(kSsScene2202PuzzleCubeFileHashes2[_cubeSymbol], kSLFCenteredDrawOffset | kSLFSetPosition | kSLFDefCollisionBoundsOffset, 0,
+ kSsScene2202PuzzleCubePoints[_cubePosition].x, kSsScene2202PuzzleCubePoints[_cubePosition].y);
+ loadSound(0, 0x40958621);
+ loadSound(1, 0x51108241);
+}
+
+void SsScene2202PuzzleCube::update() {
+ handleSpriteUpdate();
+ updatePosition();
+}
+
+uint32 SsScene2202PuzzleCube::handleMessage(int messageNum, const MessageParam &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..26df0aefa5
--- /dev/null
+++ b/engines/neverhood/modules/module2800.cpp
@@ -0,0 +1,3203 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "neverhood/modules/module2800.h"
+#include "neverhood/gamemodule.h"
+#include "neverhood/modules/module1000.h"
+#include "neverhood/modules/module1200.h"
+#include "neverhood/modules/module1700.h"
+#include "neverhood/modules/module2200.h"
+#include "neverhood/diskplayerscene.h"
+
+namespace Neverhood {
+
+Module2800::Module2800(NeverhoodEngine *vm, Module *parentModule, int which)
+ : Module(vm, parentModule), _musicResource(NULL) {
+
+ _currentMusicFileHash = 0;
+ _vm->_soundMan->addMusic(0x64210814, 0xD2FA4D14);
+ setGlobalVar(V_RADIO_MOVE_DISH_VIDEO, 1);
+
+ if (which < 0) {
+ createScene(_vm->gameState().sceneNum, which);
+ } else if (which == 2) {
+ createScene(4, 3);
+ } else if (which == 1) {
+ createScene(4, 1);
+ } else {
+ createScene(0, 0);
+ }
+
+}
+
+Module2800::~Module2800() {
+ if (_musicResource) {
+ _musicResource->unload();
+ delete _musicResource;
+ }
+ _vm->_soundMan->deleteGroup(0x64210814);
+}
+
+void Module2800::createScene(int sceneNum, int which) {
+ debug("Module2800::createScene(%d, %d)", sceneNum, which);
+ _sceneNum = sceneNum;
+ switch (_sceneNum) {
+ case 0:
+ _vm->gameState().sceneNum = 0;
+ _vm->_soundMan->stopMusic(0xD2FA4D14, 0, 0);
+ _childObject = new Scene2801(_vm, this, which);
+ break;
+ case 1:
+ _vm->gameState().sceneNum = 1;
+ _vm->_soundMan->stopMusic(0xD2FA4D14, 0, 0);
+ if (getGlobalVar(V_RADIO_ENABLED))
+ _childObject = new Scene2802(_vm, this, which);
+ else
+ createStaticScene(0x000C6444, 0xC6440008);
+ break;
+ case 2:
+ _vm->gameState().sceneNum = 2;
+ _vm->_soundMan->startMusic(0xD2FA4D14, 0, 2);
+ if (getGlobalVar(V_KLAYMEN_SMALL))
+ _childObject = new Scene2803Small(_vm, this, which);
+ else
+ _childObject = new Scene2803(_vm, this, which);
+ break;
+ case 3:
+ _vm->gameState().sceneNum = 3;
+ _childObject = new Scene2804(_vm, this, which);
+ break;
+ case 4:
+ _vm->gameState().sceneNum = 4;
+ _vm->_soundMan->stopMusic(0xD2FA4D14, 0, 2);
+ _childObject = new Scene2805(_vm, this, which);
+ break;
+ case 5:
+ _vm->gameState().sceneNum = 5;
+ _vm->_soundMan->startMusic(0xD2FA4D14, 0, 2);
+ _childObject = new Scene2806(_vm, this, which);
+ break;
+ case 6:
+ _vm->gameState().sceneNum = 6;
+ _vm->_soundMan->startMusic(0xD2FA4D14, 0, 2);
+ _childObject = new Scene2807(_vm, this, which);
+ break;
+ case 7:
+ _vm->gameState().sceneNum = 7;
+ _vm->_soundMan->startMusic(0xD2FA4D14, 0, 2);
+ _childObject = new Scene2808(_vm, this, 0);
+ break;
+ case 8:
+ _vm->gameState().sceneNum = 8;
+ _vm->_soundMan->startMusic(0xD2FA4D14, 0, 2);
+ _childObject = new Scene2809(_vm, this, which);
+ break;
+ case 9:
+ _vm->gameState().sceneNum = 9;
+ _vm->_soundMan->startMusic(0xD2FA4D14, 0, 2);
+ _childObject = new Scene2810(_vm, this, which);
+ break;
+ case 10:
+ _vm->gameState().sceneNum = 10;
+ _vm->_soundMan->startMusic(0xD2FA4D14, 0, 2);
+ _childObject = new Scene2808(_vm, this, 1);
+ break;
+ case 11:
+ _vm->gameState().sceneNum = 11;
+ _vm->_soundMan->startMusic(0xD2FA4D14, 0, 2);
+ _childObject = new Scene2812(_vm, this, which);
+ break;
+ case 12:
+ _vm->gameState().sceneNum = 12;
+ _vm->_soundMan->startMusic(0xD2FA4D14, 0, 2);
+ createStaticScene(0x0000A245, 0x0A241008);
+ break;
+ case 13:
+ _vm->gameState().sceneNum = 13;
+ _vm->_soundMan->startMusic(0xD2FA4D14, 0, 2);
+ createStaticScene(0x81C60635, 0x60631814);
+ break;
+ case 14:
+ _vm->gameState().sceneNum = 14;
+ _vm->_soundMan->startMusic(0xD2FA4D14, 0, 2);
+ createStaticScene(0xCA811204, 0x11200CA0);
+ break;
+ case 15:
+ _vm->gameState().sceneNum = 15;
+ _vm->_soundMan->startMusic(0xD2FA4D14, 0, 2);
+ createStaticScene(0x2D438A00, 0x38A042DC);
+ break;
+ case 16:
+ _vm->gameState().sceneNum = 16;
+ _vm->_soundMan->startMusic(0xD2FA4D14, 0, 2);
+ createStaticScene(0x0A806204, 0x062000A0);
+ break;
+ case 17:
+ _vm->gameState().sceneNum = 17;
+ _vm->_soundMan->startMusic(0xD2FA4D14, 0, 2);
+ createStaticScene(0x010F9284, 0xF9280018);
+ break;
+ case 18:
+ _vm->gameState().sceneNum = 18;
+ _vm->_soundMan->startMusic(0xD2FA4D14, 0, 2);
+ createStaticScene(0x0100022B, 0x0022F018);
+ break;
+ case 19:
+ _vm->gameState().sceneNum = 19;
+ _vm->_soundMan->startMusic(0xD2FA4D14, 0, 2);
+ createStaticScene(0x10866205, 0x66201100);
+ break;
+ case 20:
+ _vm->gameState().sceneNum = 20;
+ _vm->_soundMan->startMusic(0xD2FA4D14, 0, 2);
+ createStaticScene(0x01C58000, 0x58004014);
+ break;
+ case 21:
+ _vm->gameState().sceneNum = 21;
+ _vm->_soundMan->startMusic(0xD2FA4D14, 0, 2);
+ _childObject = new Scene2822(_vm, this, which);
+ break;
+ case 22:
+ _vm->gameState().sceneNum = 22;
+ _vm->_soundMan->startMusic(0xD2FA4D14, 0, 2);
+ createStaticScene(0x9408121E, 0x8121A948);
+ break;
+ case 23:
+ _vm->gameState().sceneNum = 23;
+ _vm->_soundMan->startMusic(0xD2FA4D14, 0, 2);
+ createStaticScene(0x048C0600, 0xC0604040);
+ break;
+ case 24:
+ _vm->gameState().sceneNum = 24;
+ _vm->_soundMan->startMusic(0xD2FA4D14, 0, 2);
+ createStaticScene(0x04270A94, 0x70A9004A);
+ break;
+ case 25:
+ _vm->gameState().sceneNum = 25;
+ _vm->_soundMan->startMusic(0xD2FA4D14, 0, 2);
+ if (getGlobalVar(V_SHRINK_LIGHTS_ON))
+ createStaticScene(0x01600204, 0x0020001E);
+ else
+ createStaticScene(0x08611204, 0x1120008E);
+ break;
+ case 26:
+ _vm->gameState().sceneNum = 26;
+ _vm->_soundMan->startMusic(0xD2FA4D14, 0, 2);
+ _childObject = new DiskplayerScene(_vm, this, 4);
+ break;
+ case 1001:
+ _vm->_soundMan->stopMusic(0xD2FA4D14, 0, 0);
+ createSmackerScene(0x00800801, true, true, false);
+ break;
+ }
+ SetUpdateHandler(&Module2800::updateScene);
+ _childObject->handleUpdate();
+}
+
+void Module2800::updateScene() {
+ if (!updateChild()) {
+ switch (_sceneNum) {
+ case 0:
+ if (_moduleResult != 2) {
+ if (_musicResource) {
+ _musicResource->unload();
+ delete _musicResource;
+ _musicResource = NULL;
+ }
+ _currentMusicFileHash = 0;
+ }
+ if (_moduleResult == 1) {
+ createScene(2, 0);
+ } else if (_moduleResult == 2) {
+ createScene(1, 0);
+ } else {
+ leaveModule(0);
+ }
+ break;
+ case 1:
+ if (_moduleResult == 0) {
+ createScene(0, 2);
+ } else {
+ createScene(1001, -1);
+ }
+ break;
+ case 2:
+ if (_moduleResult == 1)
+ createScene(3, 0);
+ else if (_moduleResult == 2)
+ createScene(5, 0);
+ else if (_moduleResult == 3)
+ createScene(6, 0);
+ else if (_moduleResult == 4)
+ createScene(9, 0);
+ else if (_moduleResult == 5)
+ createScene(25, 0);
+ else
+ createScene(0, 1);
+ break;
+ case 3:
+ createScene(2, 1);
+ break;
+ case 4:
+ if (_moduleResult == 1) {
+ leaveModule(1);
+ } else {
+ createScene(11, 1);
+ }
+ break;
+ case 5:
+ if (_moduleResult == 1) {
+ createScene(7, 0);
+ } else {
+ createScene(2, 2);
+ }
+ break;
+ case 6:
+ createScene(2, 3);
+ break;
+ case 7:
+ createScene(5, _moduleResult);
+ break;
+ case 8:
+ if (_moduleResult == 1)
+ createScene(10, 0);
+ else
+ createScene(9, 4);
+ break;
+ case 9:
+ if (_moduleResult == 1)
+ createScene(11, 0);
+ else if (_moduleResult == 2)
+ createScene(2, 0);
+ else if (_moduleResult == 3)
+ createScene(24, 0);
+ else if (_moduleResult == 4)
+ createScene(8, 0);
+ else if (_moduleResult == 6)
+ createScene(2, 6);
+ else if (_moduleResult == 11)
+ createScene(12, 0);
+ else if (_moduleResult == 12)
+ createScene(13, 0);
+ else if (_moduleResult == 13)
+ createScene(14, 0);
+ else if (_moduleResult == 14)
+ createScene(15, 0);
+ else if (_moduleResult == 15)
+ createScene(16, 0);
+ else if (_moduleResult == 16)
+ createScene(17, 0);
+ else if (_moduleResult == 17)
+ createScene(18, 0);
+ else if (_moduleResult == 18)
+ createScene(19, 0);
+ else if (_moduleResult == 19)
+ createScene(20, 0);
+ else if (_moduleResult == 20)
+ createScene(21, 0);
+ else if (_moduleResult == 21)
+ createScene(22, 0);
+ else if (_moduleResult == 22)
+ createScene(23, 0);
+ else
+ createScene(2, 4);
+ break;
+ case 10:
+ createScene(8, _moduleResult);
+ break;
+ case 11:
+ if (_moduleResult == 1)
+ createScene(4, 0);
+ else if (_moduleResult == 2)
+ createScene(26, 0);
+ else if (_moduleResult == 3)
+ createScene(9, 5);
+ else
+ createScene(9, 1);
+ break;
+ case 12:
+ createScene(9, 11);
+ break;
+ case 13:
+ createScene(9, 12);
+ break;
+ case 14:
+ createScene(9, 13);
+ break;
+ case 15:
+ createScene(9, 14);
+ break;
+ case 16:
+ createScene(9, 15);
+ break;
+ case 17:
+ createScene(9, 16);
+ break;
+ case 18:
+ createScene(9, 17);
+ break;
+ case 19:
+ createScene(9, 18);
+ break;
+ case 20:
+ createScene(9, 19);
+ break;
+ case 21:
+ createScene(9, 20);
+ break;
+ case 22:
+ createScene(9, 21);
+ break;
+ case 23:
+ createScene(9, 22);
+ break;
+ case 24:
+ createScene(9, 3);
+ break;
+ case 25:
+ createScene(2, 5);
+ break;
+ case 26:
+ createScene(11, 2);
+ break;
+ case 1001:
+ createScene(1, -1);
+ break;
+ }
+ } else {
+ switch (_sceneNum) {
+ case 0:
+ updateMusic(true);
+ break;
+ case 1:
+ updateMusic(false);
+ break;
+ }
+ }
+}
+
+void Module2800::updateMusic(bool halfVolume) {
+
+ uint32 newMusicFileHash = _vm->_gameModule->getCurrRadioMusicFileHash();
+
+ if (!_musicResource)
+ _musicResource = new MusicResource(_vm);
+
+ if (newMusicFileHash != _currentMusicFileHash) {
+ _currentMusicFileHash = newMusicFileHash;
+ if (_currentMusicFileHash != 0) {
+ _musicResource->load(_currentMusicFileHash);
+ _musicResource->setVolume(halfVolume ? 60 : 100);
+ _musicResource->play(0);
+ } else {
+ _musicResource->stop(0);
+ }
+ } else if (_currentMusicFileHash != 0) {
+ if (!_musicResource->isPlaying()) {
+ _musicResource->setVolume(halfVolume ? 60 : 100);
+ _musicResource->play(0);
+ } else {
+ _musicResource->setVolume(halfVolume ? 60 : 100);
+ }
+ } else {
+ _musicResource->stop(0);
+ }
+
+}
+
+Scene2801::Scene2801(NeverhoodEngine *vm, Module *parentModule, int which)
+ : Scene(vm, parentModule) {
+
+ Sprite *_sprite1;
+ Sprite *_sprite2;
+
+ _vm->gameModule()->initRadioPuzzle();
+
+ SetMessageHandler(&Scene2801::handleMessage);
+ SetUpdateHandler(&Scene::update);
+
+ // Display the disabled radio; only possible when the left door is open
+ if (!getGlobalVar(V_RADIO_ENABLED))
+ insertStaticSprite(0x0001264C, 100);
+
+ if (which < 0) {
+ insertKlaymen<KmScene2801>(194, 430);
+ setMessageList(0x004B6BB8);
+ } else if (which == 1) {
+ insertKlaymen<KmScene2801>(443, 398);
+ setMessageList(0x004B6BC0);
+ } else if (which == 2) {
+ if (getGlobalVar(V_KLAYMEN_IS_DELTA_X)) {
+ insertKlaymen<KmScene2801>(312, 432);
+ _klaymen->setDoDeltaX(1);
+ } else {
+ insertKlaymen<KmScene2801>(194, 432);
+ }
+ setMessageList(0x004B6C10);
+ } else {
+ insertKlaymen<KmScene2801>(0, 432);
+ setMessageList(0x004B6BB0);
+ }
+
+ if (getGlobalVar(V_RADIO_ROOM_LEFT_DOOR)) {
+ setRectList(0x004B6CE0);
+ setBackground(0x01400666);
+ setPalette(0x01400666);
+ _paletteHash = 0x15021024;
+ _palette->addBasePalette(0x01400666, 0, 256, 0);
+ _sprite1 = insertStaticSprite(0x100CA0A8, 1100);
+ _sprite2 = insertStaticSprite(0x287C21A4, 1100);
+ _klaymen->setClipRect(_sprite1->getDrawRect().x, 0, _sprite2->getDrawRect().x2(), 480);
+ insertScreenMouse(0x0066201C);
+ _asTape = insertSprite<AsScene1201Tape>(this, 8, 1100, 302, 437, 0x9148A011);
+ addCollisionSprite(_asTape);
+ } else if (getGlobalVar(V_RADIO_ROOM_RIGHT_DOOR)) {
+ setRectList(0x004B6CD0);
+ setBackground(0x11E00684);
+ setPalette(0x11E00684);
+ _paletteHash = 0x15021024;
+ _palette->addBasePalette(0x11E00684, 0, 256, 0);
+ _sprite2 = insertStaticSprite(0x061601C8, 1100);
+ _klaymen->setClipRect(0, 0, _sprite2->getDrawRect().x2(), 480);
+ insertScreenMouse(0x00680116);
+ _asTape = insertSprite<SsScene1705Tape>(this, 8, 1100, 302, 437, 0x01142428);
+ addCollisionSprite(_asTape);
+ } else {
+ setRectList(0x004B6CF0);
+ setBackground(0x030006E6);
+ setPalette(0x030006E6);
+ _paletteHash = 0x15021024;
+ _palette->addBasePalette(0x030006E6, 0, 256, 0);
+ _sprite2 = insertStaticSprite(0x273801CE, 1100);
+ _klaymen->setClipRect(0, 0, _sprite2->getDrawRect().x2(), 480);
+ insertScreenMouse(0x006E2038);
+ _asTape = insertSprite<AsScene1201Tape>(this, 8, 1100, 302, 437, 0x9148A011);
+ addCollisionSprite(_asTape);
+ }
+
+ addEntity(_palette);
+
+ if (which == 1) {
+ _palette->addPalette(0xB103B604, 0, 65, 0);
+ _palette->addBasePalette(0xB103B604, 0, 65, 0);
+ } else {
+ _palette->addPalette(_paletteHash, 0, 65, 0);
+ _palette->addBasePalette(_paletteHash, 0, 65, 0);
+ }
+
+}
+
+Scene2801::~Scene2801() {
+ setGlobalVar(V_KLAYMEN_IS_DELTA_X, _klaymen->isDoDeltaX() ? 1 : 0);
+}
+
+uint32 Scene2801::handleMessage(int messageNum, const MessageParam &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;
+
+ 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 */
diff --git a/engines/neverhood/mouse.cpp b/engines/neverhood/mouse.cpp
new file mode 100644
index 0000000000..632f56fb74
--- /dev/null
+++ b/engines/neverhood/mouse.cpp
@@ -0,0 +1,256 @@
+/* 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/mouse.h"
+#include "graphics/cursorman.h"
+
+namespace Neverhood {
+
+Mouse::Mouse(NeverhoodEngine *vm, uint32 fileHash, const NRect &mouseRect)
+ : StaticSprite(vm, 2000), _mouseType(kMouseType433),
+ _mouseCursorResource(vm), _frameNum(0) {
+
+ _mouseRect = mouseRect;
+ init(fileHash);
+ if (_x >= _mouseRect.x1 && _x <= _mouseRect.x2 &&
+ _y >= _mouseRect.y1 && _y <= _mouseRect.y2) {
+ _mouseCursorResource.setCursorNum(1);
+ } else {
+ _mouseCursorResource.setCursorNum(4);
+ }
+ updateCursor();
+}
+
+Mouse::Mouse(NeverhoodEngine *vm, uint32 fileHash, int16 x1, int16 x2)
+ : StaticSprite(vm, 2000), _mouseType(kMouseType435),
+ _mouseCursorResource(vm), _frameNum(0), _x1(x1), _x2(x2) {
+
+ init(fileHash);
+ if (_x <= _x1) {
+ _mouseCursorResource.setCursorNum(6);
+ } else if (_x >= _x2) {
+ _mouseCursorResource.setCursorNum(5);
+ } else {
+ _mouseCursorResource.setCursorNum(4);
+ }
+ updateCursor();
+}
+
+Mouse::Mouse(NeverhoodEngine *vm, uint32 fileHash, int type)
+ : StaticSprite(vm, 2000), _mouseType(kMouseTypeNavigation),
+ _mouseCursorResource(vm), _type(type), _frameNum(0) {
+
+ init(fileHash);
+ _mouseCursorResource.setCursorNum(0);
+}
+
+Mouse::~Mouse() {
+ CursorMan.showMouse(false);
+}
+
+void Mouse::init(uint32 fileHash) {
+ _mouseCursorResource.load(fileHash);
+ _x = _vm->getMouseX();
+ _y = _vm->getMouseY();
+ createSurface(2000, 32, 32);
+ SetUpdateHandler(&Mouse::update);
+ SetMessageHandler(&Mouse::handleMessage);
+ _drawOffset.x = 0;
+ _drawOffset.y = 0;
+ _drawOffset.width = 32;
+ _drawOffset.height = 32;
+ _collisionBoundsOffset = _drawOffset;
+ updateBounds();
+ _needRefresh = true;
+ CursorMan.showMouse(false);
+}
+
+void Mouse::load(uint32 fileHash) {
+ _mouseCursorResource.load(fileHash);
+ _needRefresh = true;
+}
+
+void Mouse::update() {
+ if (CursorMan.isVisible() && !_surface->getVisible()) {
+ CursorMan.showMouse(false);
+ } else if (!CursorMan.isVisible() && _surface->getVisible()) {
+ CursorMan.showMouse(true);
+ }
+ updateCursor();
+ _frameNum++;
+ if (_frameNum >= 6)
+ _frameNum = 0;
+ _needRefresh = _frameNum % 2 == 0;
+}
+
+uint32 Mouse::handleMessage(int messageNum, const MessageParam &param, Entity *sender) {
+ debug(7, "Mouse::handleMessage(%04X)", messageNum);
+ uint32 messageResult = Sprite::handleMessage(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x2064:
+ _x = param.asPoint().x;
+ _y = param.asPoint().y;
+ switch (_type) {
+ case 1:
+ if (_x >= 320)
+ messageResult = 1;
+ else
+ messageResult = 0;
+ break;
+ case 2:
+ default:
+ if (_x < 100)
+ messageResult = 0;
+ else if (_x > 540)
+ messageResult = 1;
+ else
+ messageResult = 2;
+ break;
+ case 3:
+ if (_x < 100)
+ messageResult = 0;
+ else if (_x > 540)
+ messageResult = 1;
+ else
+ messageResult = 4;
+ break;
+ case 4:
+ if (_x < 100)
+ messageResult = 0;
+ else if (_x > 540)
+ messageResult = 1;
+ else if (_y >= 150)
+ messageResult = 2;
+ else
+ messageResult = 3;
+ break;
+ case 5:
+ if (_y >= 240)
+ messageResult = 4;
+ else
+ messageResult = 3;
+ break;
+ }
+ break;
+ case 0x4002:
+ _x = param.asPoint().x;
+ _y = param.asPoint().y;
+ updateCursorNum();
+ updateBounds();
+ break;
+ }
+ return messageResult;
+}
+
+void Mouse::updateCursor() {
+
+ if (!_surface)
+ return;
+
+ if (_doDeltaX) {
+ _surface->getDrawRect().x = filterX(_x - _drawOffset.width - _drawOffset.x + 1);
+ } else {
+ _surface->getDrawRect().x = filterX(_x + _drawOffset.x);
+ }
+
+ if (_doDeltaY) {
+ _surface->getDrawRect().y = filterY(_y - _drawOffset.height - _drawOffset.y + 1);
+ } else {
+ _surface->getDrawRect().y = filterY(_y + _drawOffset.y);
+ }
+
+ if (_needRefresh) {
+ _needRefresh = false;
+ _drawOffset = _mouseCursorResource.getRect();
+ _surface->drawMouseCursorResource(_mouseCursorResource, _frameNum / 2);
+ Graphics::Surface *cursorSurface = _surface->getSurface();
+ CursorMan.replaceCursor((const byte*)cursorSurface->pixels,
+ cursorSurface->w, cursorSurface->h, -_drawOffset.x, -_drawOffset.y, 0);
+ }
+
+}
+
+void Mouse::updateCursorNum() {
+ switch (_mouseType) {
+ case kMouseType433:
+ if (_x >= _mouseRect.x1 && _x <= _mouseRect.x2 &&
+ _y >= _mouseRect.y1 && _y <= _mouseRect.y2) {
+ _mouseCursorResource.setCursorNum(1);
+ } else {
+ _mouseCursorResource.setCursorNum(4);
+ }
+ break;
+ case kMouseType435:
+ if (_x <= _x1) {
+ _mouseCursorResource.setCursorNum(6);
+ } else if (_x >= _x2) {
+ _mouseCursorResource.setCursorNum(5);
+ } else {
+ _mouseCursorResource.setCursorNum(4);
+ }
+ break;
+ case kMouseTypeNavigation:
+ switch (_type) {
+ case 1:
+ if (_x >= 320)
+ _mouseCursorResource.setCursorNum(5);
+ else
+ _mouseCursorResource.setCursorNum(6);
+ break;
+ case 2:
+ default:
+ if (_x < 100)
+ _mouseCursorResource.setCursorNum(6);
+ else if (_x > 540)
+ _mouseCursorResource.setCursorNum(5);
+ else
+ _mouseCursorResource.setCursorNum(0);
+ break;
+ case 3:
+ if (_x < 100)
+ _mouseCursorResource.setCursorNum(1);
+ else if (_x > 540)
+ _mouseCursorResource.setCursorNum(1);
+ break;
+ case 4:
+ if (_x < 100)
+ _mouseCursorResource.setCursorNum(6);
+ else if (_x > 540)
+ _mouseCursorResource.setCursorNum(5);
+ else if (_y >= 150)
+ _mouseCursorResource.setCursorNum(0);
+ else
+ _mouseCursorResource.setCursorNum(3);
+ break;
+ case 5:
+ if (_y >= 240)
+ _mouseCursorResource.setCursorNum(2);
+ else
+ _mouseCursorResource.setCursorNum(3);
+ break;
+ }
+ break;
+ }
+
+}
+
+} // End of namespace Neverhood
diff --git a/engines/neverhood/mouse.h b/engines/neverhood/mouse.h
new file mode 100644
index 0000000000..0b927de4df
--- /dev/null
+++ b/engines/neverhood/mouse.h
@@ -0,0 +1,63 @@
+/* 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_MOUSE_H
+#define NEVERHOOD_MOUSE_H
+
+#include "neverhood/neverhood.h"
+#include "neverhood/sprite.h"
+#include "neverhood/graphics.h"
+#include "neverhood/resource.h"
+
+namespace Neverhood {
+
+enum MouseType {
+ kMouseType433,
+ kMouseType435,
+ kMouseTypeNavigation
+};
+
+class Mouse : public StaticSprite {
+public:
+ Mouse(NeverhoodEngine *vm, uint32 fileHash, const NRect &mouseRect);
+ Mouse(NeverhoodEngine *vm, uint32 fileHash, int16 x1, int16 x2);
+ Mouse(NeverhoodEngine *vm, uint32 fileHash, int _type);
+ virtual ~Mouse();
+ void load(uint32 fileHash);
+ void updateCursor();
+protected:
+ MouseType _mouseType;
+ MouseCursorResource _mouseCursorResource;
+ int _frameNum;
+ NRect _mouseRect;
+ int16 _x1;
+ int16 _x2;
+ int _type;
+ void init(uint32 fileHash);
+ void update();
+ void updateCursorNum();
+ uint32 handleMessage(int messageNum, const MessageParam &param, Entity *sender);
+};
+
+} // End of namespace Neverhood
+
+#endif /* NEVERHOOD_MOUSE_H */
diff --git a/engines/neverhood/navigationscene.cpp b/engines/neverhood/navigationscene.cpp
new file mode 100644
index 0000000000..9752ee618c
--- /dev/null
+++ b/engines/neverhood/navigationscene.cpp
@@ -0,0 +1,225 @@
+/* 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/navigationscene.h"
+#include "neverhood/mouse.h"
+
+namespace Neverhood {
+
+NavigationScene::NavigationScene(NeverhoodEngine *vm, Module *parentModule, uint32 navigationListId, int navigationIndex, const byte *itemsTypes)
+ : Scene(vm, parentModule), _itemsTypes(itemsTypes), _navigationIndex(navigationIndex), _smackerDone(false),
+ _isWalkingForward(false), _isTurning(false), _smackerFileHash(0), _interactive(true), _leaveSceneAfter(false) {
+
+ _navigationList = _vm->_staticData->getNavigationList(navigationListId);
+
+ if (_navigationIndex < 0) {
+ _navigationIndex = (int)getGlobalVar(V_NAVIGATION_INDEX);
+ if (_navigationIndex >= (int)_navigationList->size())
+ _navigationIndex = 0;
+ }
+ setGlobalVar(V_NAVIGATION_INDEX, _navigationIndex);
+
+ SetUpdateHandler(&NavigationScene::update);
+ SetMessageHandler(&NavigationScene::handleMessage);
+
+ _smackerPlayer = new SmackerPlayer(_vm, this, (*_navigationList)[_navigationIndex].fileHash, true, true);
+ addEntity(_smackerPlayer);
+ addSurface(_smackerPlayer->getSurface());
+
+ createMouseCursor();
+
+ _vm->_screen->clear();
+ _vm->_screen->setSmackerDecoder(_smackerPlayer->getSmackerDecoder());
+
+ sendMessage(_parentModule, 0x100A, _navigationIndex);
+
+}
+
+NavigationScene::~NavigationScene() {
+ _vm->_soundMan->setTwoSoundsPlayFlag(false);
+ _vm->_soundMan->setSoundThreePlayFlag(false);
+}
+
+int NavigationScene::getNavigationAreaType() {
+ NPoint mousePos;
+ mousePos.x = _mouseCursor->getX();
+ mousePos.y = _mouseCursor->getY();
+ return sendPointMessage(_mouseCursor, 0x2064, mousePos);
+}
+
+void NavigationScene::update() {
+ if (_smackerFileHash != 0) {
+ showMouse(false);
+ openSmacker(_smackerFileHash, false);
+ _vm->_screen->clear();
+ _vm->_screen->setSmackerDecoder(_smackerPlayer->getSmackerDecoder());
+ _smackerDone = false;
+ /*
+ if (!_interactive)
+ _smackerDone = true;
+ */
+ _smackerFileHash = 0;
+ } else if (_smackerDone) {
+ if (_leaveSceneAfter) {
+ _vm->_screen->setSmackerDecoder(NULL);
+ sendMessage(_parentModule, 0x1009, _navigationIndex);
+ } else {
+ const NavigationItem &navigationItem = (*_navigationList)[_navigationIndex];
+ createMouseCursor();
+ showMouse(true);
+ _isTurning = false;
+ _isWalkingForward = false;
+ _interactive = true;
+ _vm->_soundMan->setTwoSoundsPlayFlag(false);
+ _vm->_soundMan->setSoundThreePlayFlag(false);
+ _smackerDone = false;
+ openSmacker(navigationItem.fileHash, true);
+ _vm->_screen->clear();
+ _vm->_screen->setSmackerDecoder(_smackerPlayer->getSmackerDecoder());
+ sendMessage(_parentModule, 0x100A, _navigationIndex);
+ }
+ }
+ Scene::update();
+}
+
+void NavigationScene::openSmacker(uint32 fileHash, bool keepLastFrame) {
+ // The old Smacker surface is deleted when a new Smacker is opened.
+ removeSurface(_smackerPlayer->getSurface());
+ _smackerPlayer->open(fileHash, keepLastFrame);
+ addSurface(_smackerPlayer->getSurface());
+}
+
+uint32 NavigationScene::handleMessage(int messageNum, const MessageParam &param, Entity *sender) {
+ switch (messageNum) {
+ case 0x0000:
+ if (_interactive)
+ sendMessage(_mouseCursor, 0x4002, param);
+ break;
+ case 0x0001:
+ if (_interactive)
+ handleNavigation(param.asPoint());
+ break;
+ case 0x0009:
+ if (!_interactive)
+ _smackerDone = true;
+ break;
+ case 0x3002:
+ _smackerDone = true;
+ break;
+ }
+ return 0;
+}
+
+void NavigationScene::createMouseCursor() {
+
+ const NavigationItem &navigationItem = (*_navigationList)[_navigationIndex];
+ uint32 mouseCursorFileHash;
+ int areaType;
+
+ if (_mouseCursor) {
+ deleteSprite((Sprite**)&_mouseCursor);
+ }
+
+ mouseCursorFileHash = navigationItem.mouseCursorFileHash;
+ if (mouseCursorFileHash == 0)
+ mouseCursorFileHash = 0x63A40028;
+
+ if (_itemsTypes) {
+ areaType = _itemsTypes[_navigationIndex];
+ } else if (navigationItem.middleSmackerFileHash != 0 || navigationItem.middleFlag) {
+ areaType = 0;
+ } else {
+ areaType = 1;
+ }
+
+ insertNavigationMouse(mouseCursorFileHash, areaType);
+ sendPointMessage(_mouseCursor, 0x4002, _vm->getMousePos());
+
+}
+
+void NavigationScene::handleNavigation(const NPoint &mousePos) {
+
+ const NavigationItem &navigationItem = (*_navigationList)[_navigationIndex];
+ bool oldIsWalkingForward = _isWalkingForward;
+ bool oldIsTurning = _isTurning;
+ uint32 direction = sendPointMessage(_mouseCursor, 0x2064, mousePos);
+
+ switch (direction) {
+ case 0:
+ if (navigationItem.leftSmackerFileHash != 0) {
+ _smackerFileHash = navigationItem.leftSmackerFileHash;
+ _interactive = false;
+ _isWalkingForward = false;
+ _isTurning = true;
+ do {
+ _navigationIndex--;
+ if (_navigationIndex < 0)
+ _navigationIndex = _navigationList->size() - 1;
+ } while (!(*_navigationList)[_navigationIndex].interactive);
+ setGlobalVar(V_NAVIGATION_INDEX, _navigationIndex);
+ } else {
+ _vm->_screen->setSmackerDecoder(NULL);
+ sendMessage(_parentModule, 0x1009, _navigationIndex);
+ }
+ break;
+ case 1:
+ if (navigationItem.rightSmackerFileHash != 0) {
+ _smackerFileHash = navigationItem.rightSmackerFileHash;
+ _interactive = false;
+ _isWalkingForward = false;
+ _isTurning = true;
+ do {
+ _navigationIndex++;
+ if (_navigationIndex >= (int)_navigationList->size())
+ _navigationIndex = 0;
+ } while (!(*_navigationList)[_navigationIndex].interactive);
+ setGlobalVar(V_NAVIGATION_INDEX, _navigationIndex);
+ } else {
+ _vm->_screen->setSmackerDecoder(NULL);
+ sendMessage(_parentModule, 0x1009, _navigationIndex);
+ }
+ break;
+ case 2:
+ case 3:
+ case 4:
+ if (navigationItem.middleFlag) {
+ _vm->_screen->setSmackerDecoder(NULL);
+ sendMessage(_parentModule, 0x1009, _navigationIndex);
+ } else if (navigationItem.middleSmackerFileHash != 0) {
+ _smackerFileHash = navigationItem.middleSmackerFileHash;
+ _interactive = false;
+ _isWalkingForward = true;
+ _isTurning = false;
+ _leaveSceneAfter = true;
+ }
+ break;
+ }
+
+ if (oldIsTurning != _isTurning)
+ _vm->_soundMan->setSoundThreePlayFlag(_isTurning);
+
+ if (oldIsWalkingForward != _isWalkingForward)
+ _vm->_soundMan->setTwoSoundsPlayFlag(_isWalkingForward);
+
+}
+
+} // End of namespace Neverhood
diff --git a/engines/neverhood/navigationscene.h b/engines/neverhood/navigationscene.h
new file mode 100644
index 0000000000..767b3f5d20
--- /dev/null
+++ b/engines/neverhood/navigationscene.h
@@ -0,0 +1,61 @@
+/* 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_NAVIGATIONSCENE_H
+#define NEVERHOOD_NAVIGATIONSCENE_H
+
+#include "neverhood/neverhood.h"
+#include "neverhood/resourceman.h"
+#include "neverhood/scene.h"
+
+namespace Neverhood {
+
+class NavigationScene : public Scene {
+public:
+ NavigationScene(NeverhoodEngine *vm, Module *parentModule, uint32 navigationListId, int navigationIndex, const byte *itemsTypes);
+ virtual ~NavigationScene();
+ int getNavigationAreaType();
+ int getNavigationIndex() const { return _navigationIndex; }
+ bool isWalkingForward() const { return _isWalkingForward; }
+ bool isTurning() const { return _isTurning; }
+ int getFrameNumber() const { return _smackerPlayer->getFrameNumber(); }
+protected:
+ SmackerPlayer *_smackerPlayer;
+ bool _smackerDone;
+ NavigationList *_navigationList;
+ int _navigationIndex;
+ uint32 _smackerFileHash;
+ bool _interactive;
+ bool _isWalkingForward;
+ bool _isTurning;
+ bool _leaveSceneAfter;
+ const byte *_itemsTypes;
+ void update();
+ void openSmacker(uint32 fileHash, bool keepLastFrame);
+ uint32 handleMessage(int messageNum, const MessageParam &param, Entity *sender);
+ void createMouseCursor();
+ void handleNavigation(const NPoint &mousePos);
+};
+
+} // End of namespace Neverhood
+
+#endif /* NEVERHOOD_NAVIGATIONSCENE_H */
diff --git a/engines/neverhood/neverhood.cpp b/engines/neverhood/neverhood.cpp
new file mode 100644
index 0000000000..6b27343d03
--- /dev/null
+++ b/engines/neverhood/neverhood.cpp
@@ -0,0 +1,189 @@
+/* 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 "common/file.h"
+#include "common/config-manager.h"
+#include "base/plugins.h"
+#include "base/version.h"
+#include "graphics/cursorman.h"
+#include "engines/util.h"
+#include "neverhood/neverhood.h"
+#include "neverhood/blbarchive.h"
+#include "neverhood/gamemodule.h"
+#include "neverhood/gamevars.h"
+#include "neverhood/graphics.h"
+#include "neverhood/resourceman.h"
+#include "neverhood/resource.h"
+#include "neverhood/screen.h"
+#include "neverhood/sound.h"
+#include "neverhood/staticdata.h"
+
+namespace Neverhood {
+
+NeverhoodEngine::NeverhoodEngine(OSystem *syst, const NeverhoodGameDescription *gameDesc) : Engine(syst), _gameDescription(gameDesc) {
+ // Setup mixer
+ if (!_mixer->isReady()) {
+ warning("Sound initialization failed.");
+ }
+
+ _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume"));
+ _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume"));
+
+ _rnd = new Common::RandomSource("neverhood");
+
+}
+
+NeverhoodEngine::~NeverhoodEngine() {
+ delete _rnd;
+}
+
+Common::Error NeverhoodEngine::run() {
+ initGraphics(640, 480, true);
+
+ const Common::FSNode gameDataDir(ConfMan.get("path"));
+
+ SearchMan.addSubDirectoryMatching(gameDataDir, "data");
+
+ _isSaveAllowed = false;
+
+ _mouseX = 0;
+ _mouseY = 0;
+
+ _gameState.sceneNum = 0;
+ _gameState.which = 0;
+
+ _staticData = new StaticData();
+ _staticData->load("neverhood.dat");
+ _gameVars = new GameVars();
+ _screen = new Screen(this);
+ _res = new ResourceMan();
+
+ if (isDemo()) {
+ _res->addArchive("a.blb");
+ _res->addArchive("nevdemo.blb");
+ } else {
+ _res->addArchive("a.blb");
+ _res->addArchive("c.blb");
+ _res->addArchive("hd.blb");
+ _res->addArchive("i.blb");
+ _res->addArchive("m.blb");
+ _res->addArchive("s.blb");
+ _res->addArchive("t.blb");
+ }
+
+ CursorMan.showMouse(false);
+
+ _soundMan = new SoundMan(this);
+ _audioResourceMan = new AudioResourceMan(this);
+
+ _gameModule = new GameModule(this);
+
+ _isSaveAllowed = true;
+
+ if (isDemo()) {
+ // Adjust this navigation list for the demo version
+ NavigationList *navigationList = _staticData->getNavigationList(0x004B67E8);
+ (*navigationList)[0].middleSmackerFileHash = 0;
+ (*navigationList)[0].middleFlag = 1;
+ (*navigationList)[2].middleSmackerFileHash = 0;
+ (*navigationList)[2].middleFlag = 1;
+ (*navigationList)[4].middleSmackerFileHash = 0;
+ (*navigationList)[4].middleFlag = 1;
+ (*navigationList)[5].middleSmackerFileHash = 0;
+ (*navigationList)[5].middleFlag = 1;
+ }
+
+ if (ConfMan.hasKey("save_slot"))
+ loadGameState(ConfMan.getInt("save_slot"));
+ else
+ _gameModule->startup();
+
+ mainLoop();
+
+ delete _gameModule;
+ delete _soundMan;
+ delete _audioResourceMan;
+
+ delete _res;
+ delete _screen;
+
+ delete _gameVars;
+ delete _staticData;
+
+ return Common::kNoError;
+}
+
+void NeverhoodEngine::mainLoop() {
+ uint32 nextFrameTime = 0;
+ while (!shouldQuit()) {
+ Common::Event event;
+ Common::EventManager *eventMan = _system->getEventManager();
+ while (eventMan->pollEvent(event)) {
+ switch (event.type) {
+ case Common::EVENT_KEYDOWN:
+ _gameModule->handleKeyDown(event.kbd.keycode);
+ _gameModule->handleAsciiKey(event.kbd.ascii);
+ break;
+ case Common::EVENT_KEYUP:
+ break;
+ case Common::EVENT_MOUSEMOVE:
+ _mouseX = event.mouse.x;
+ _mouseY = event.mouse.y;
+ _gameModule->handleMouseMove(event.mouse.x, event.mouse.y);
+ break;
+ case Common::EVENT_LBUTTONDOWN:
+ case Common::EVENT_RBUTTONDOWN:
+ _gameModule->handleMouseDown(event.mouse.x, event.mouse.y);
+ break;
+ case Common::EVENT_LBUTTONUP:
+ case Common::EVENT_RBUTTONUP:
+ _gameModule->handleMouseUp(event.mouse.x, event.mouse.y);
+ break;
+ case Common::EVENT_QUIT:
+ _system->quit();
+ break;
+ default:
+ break;
+ }
+ }
+ if (_system->getMillis() >= nextFrameTime) {
+ _gameModule->checkRequests();
+ _gameModule->handleUpdate();
+ _gameModule->draw();
+ _screen->update();
+ nextFrameTime = _screen->getNextFrameTime();
+ };
+ _soundMan->update();
+ _audioResourceMan->updateMusic();
+ _system->updateScreen();
+ _system->delayMillis(10);
+ }
+}
+
+NPoint NeverhoodEngine::getMousePos() {
+ NPoint pt;
+ pt.x = _mouseX;
+ pt.y = _mouseY;
+ return pt;
+}
+
+} // End of namespace Neverhood
diff --git a/engines/neverhood/neverhood.h b/engines/neverhood/neverhood.h
new file mode 100644
index 0000000000..577fbd7a66
--- /dev/null
+++ b/engines/neverhood/neverhood.h
@@ -0,0 +1,141 @@
+/* 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_H
+#define NEVERHOOD_H
+
+#include "common/scummsys.h"
+#include "common/events.h"
+#include "common/keyboard.h"
+#include "common/random.h"
+#include "common/savefile.h"
+#include "common/str-array.h"
+#include "common/system.h"
+#include "audio/mixer.h"
+#include "engines/engine.h"
+#include "neverhood/messages.h"
+
+namespace Neverhood {
+
+enum NeverhoodGameFeatures {
+};
+
+struct NeverhoodGameDescription;
+
+class GameModule;
+class GameVars;
+class ResourceMan;
+class Screen;
+class SoundMan;
+class AudioResourceMan;
+class StaticData;
+struct NPoint;
+
+struct GameState {
+ int sceneNum;
+ int which;
+};
+
+class NeverhoodEngine : public ::Engine {
+protected:
+
+ Common::Error run();
+ void mainLoop();
+
+public:
+ NeverhoodEngine(OSystem *syst, const NeverhoodGameDescription *gameDesc);
+ virtual ~NeverhoodEngine();
+
+ // Detection related functions
+ const NeverhoodGameDescription *_gameDescription;
+ const char *getGameId() const;
+ uint32 getFeatures() const;
+ uint16 getVersion() const;
+ Common::Platform getPlatform() const;
+ bool hasFeature(EngineFeature f) const;
+ bool isDemo() const;
+ Common::String getTargetName() { return _targetName; };
+
+ Common::RandomSource *_rnd;
+
+ int16 _mouseX, _mouseY;
+ uint16 _buttonState;
+
+ GameState _gameState;
+ GameVars *_gameVars;
+ Screen *_screen;
+ ResourceMan *_res;
+ GameModule *_gameModule;
+ StaticData *_staticData;
+
+ SoundMan *_soundMan;
+ AudioResourceMan *_audioResourceMan;
+
+public:
+
+ /* Save/load */
+
+ enum kReadSaveHeaderError {
+ kRSHENoError = 0,
+ kRSHEInvalidType = 1,
+ kRSHEInvalidVersion = 2,
+ kRSHEIoError = 3
+ };
+
+ struct SaveHeader {
+ Common::String description;
+ uint32 version;
+ byte gameID;
+ uint32 flags;
+ uint32 saveDate;
+ uint32 saveTime;
+ uint32 playTime;
+ Graphics::Surface *thumbnail;
+ };
+
+ bool _isSaveAllowed;
+
+ bool canLoadGameStateCurrently() { return _isSaveAllowed; }
+ bool canSaveGameStateCurrently() { return _isSaveAllowed; }
+
+ Common::Error loadGameState(int slot);
+ Common::Error saveGameState(int slot, const Common::String &description);
+ Common::Error removeGameState(int slot);
+ void savegame(const char *filename, const char *description);
+ void loadgame(const char *filename);
+ const char *getSavegameFilename(int num);
+ static Common::String getSavegameFilename(const Common::String &target, int num);
+ static kReadSaveHeaderError readSaveHeader(Common::SeekableReadStream *in, bool loadThumbnail, SaveHeader &header);
+
+ GameState& gameState() { return _gameState; }
+ GameModule *gameModule() { return _gameModule; }
+ int16 getMouseX() const { return _mouseX; }
+ int16 getMouseY() const { return _mouseY; }
+ NPoint getMousePos();
+
+public:
+
+};
+
+} // End of namespace Neverhood
+
+#endif /* NEVERHOOD_H */
diff --git a/engines/neverhood/palette.cpp b/engines/neverhood/palette.cpp
new file mode 100644
index 0000000000..d4b9b67f53
--- /dev/null
+++ b/engines/neverhood/palette.cpp
@@ -0,0 +1,201 @@
+/* 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/palette.h"
+#include "neverhood/resource.h"
+#include "neverhood/screen.h"
+
+namespace Neverhood {
+
+// Palette
+
+Palette::Palette(NeverhoodEngine *vm) : Entity(vm, 0) {
+ init();
+ memset(_palette, 0, 1024);
+ SetUpdateHandler(&Palette::update);
+}
+
+Palette::Palette(NeverhoodEngine *vm, byte *palette) : Entity(vm, 0) {
+ init();
+ memcpy(_palette, palette, 1024);
+ SetUpdateHandler(&Palette::update);
+}
+
+Palette::Palette(NeverhoodEngine *vm, const char *filename) : Entity(vm, 0) {
+ PaletteResource paletteResource(_vm);
+ init();
+ paletteResource.load(calcHash(filename));
+ paletteResource.copyPalette(_palette);
+ SetUpdateHandler(&Palette::update);
+}
+
+Palette::Palette(NeverhoodEngine *vm, uint32 fileHash) : Entity(vm, 0) {
+ PaletteResource paletteResource(_vm);
+ init();
+ paletteResource.load(fileHash);
+ paletteResource.copyPalette(_palette);
+ SetUpdateHandler(&Palette::update);
+}
+
+Palette::~Palette() {
+ _vm->_screen->unsetPaletteData(_palette);
+ delete[] _palette;
+ delete[] _basePalette;
+}
+
+void Palette::init() {
+ _status = 0;
+ _palette = new byte[1024];
+ _basePalette = new byte[1024];
+}
+
+void Palette::usePalette() {
+ _vm->_screen->setPaletteData(_palette);
+}
+
+void Palette::addPalette(const char *filename, int toIndex, int count, int fromIndex) {
+ addPalette(calcHash(filename), toIndex, count, fromIndex);
+}
+
+void Palette::addPalette(uint32 fileHash, int toIndex, int count, int fromIndex) {
+ PaletteResource paletteResource(_vm);
+ if (toIndex + count > 256)
+ count = 256 - toIndex;
+ paletteResource.load(fileHash);
+ memcpy(_palette + toIndex * 4, paletteResource.palette() + fromIndex * 4, count * 4);
+ _vm->_screen->testPalette(_palette);
+}
+
+void Palette::addBasePalette(uint32 fileHash, int toIndex, int count, int fromIndex) {
+ PaletteResource paletteResource(_vm);
+ if (toIndex + count > 256)
+ count = 256 - toIndex;
+ paletteResource.load(fileHash);
+ memcpy(_basePalette + toIndex * 4, paletteResource.palette() + fromIndex * 4, count * 4);
+}
+
+void Palette::copyPalette(const byte *palette, int toIndex, int count, int fromIndex) {
+ if (toIndex + count > 256)
+ count = 256 - toIndex;
+ memcpy(_palette + toIndex * 4, palette + fromIndex * 4, count * 4);
+ _vm->_screen->testPalette(_palette);
+}
+
+void Palette::copyBasePalette(int toIndex, int count, int fromIndex) {
+ if (toIndex + count > 256)
+ count = 256 - toIndex;
+ memcpy(_basePalette + toIndex * 4, _palette + fromIndex * 4, count * 4);
+}
+
+void Palette::startFadeToBlack(int counter) {
+ debug(2, "Palette::startFadeToBlack(%d)", counter);
+ if (counter == 0)
+ counter = 1;
+ _fadeToR = 0;
+ _fadeToG = 0;
+ _fadeToB = 0;
+ _palCounter = counter;
+ _fadeStep = 255 / counter;
+ _status = 1;
+}
+
+void Palette::startFadeToWhite(int counter) {
+ debug(2, "Palette::startFadeToWhite(%d)", counter);
+ if (counter == 0)
+ counter = 1;
+ _fadeToR = 255;
+ _fadeToG = 255;
+ _fadeToB = 255;
+ _palCounter = counter;
+ _fadeStep = 255 / counter;
+ _status = 1;
+}
+
+void Palette::startFadeToPalette(int counter) {
+ debug(2, "Palette::startFadeToPalette(%d)", counter);
+ if (counter == 0)
+ counter = 1;
+ _palCounter = counter;
+ _fadeStep = 255 / counter;
+ _status = 2;
+}
+
+void Palette::fillBaseWhite(int index, int count) {
+ if (index + count > 256)
+ count = 256 - index;
+ for (int i = 0; i < count; i++) {
+ _basePalette[(i + index) * 4 + 0] = 0xFF;
+ _basePalette[(i + index) * 4 + 1] = 0xFF;
+ _basePalette[(i + index) * 4 + 2] = 0xFF;
+ _basePalette[(i + index) * 4 + 3] = 0;
+ }
+}
+
+void Palette::fillBaseBlack(int index, int count) {
+ if (index + count > 256)
+ count = 256 - index;
+ for (int i = 0; i < count; i++) {
+ _basePalette[(i + index) * 4 + 0] = 0;
+ _basePalette[(i + index) * 4 + 1] = 0;
+ _basePalette[(i + index) * 4 + 2] = 0;
+ _basePalette[(i + index) * 4 + 3] = 0;
+ }
+}
+
+void Palette::copyToBasePalette(byte *palette) {
+ memcpy(_basePalette, palette, 256 * 4);
+}
+
+void Palette::update() {
+ debug(2, "Palette::update() _status = %d", _status);
+ if (_status == 1) {
+ if (_palCounter > 1) {
+ for (int i = 0; i < 256; i++)
+ fadeColor(_palette + i * 4, _fadeToR, _fadeToG, _fadeToB);
+ _vm->_screen->testPalette(_palette);
+ _palCounter--;
+ } else {
+ memset(_palette, 0, 1024);
+ _status = 0;
+ }
+ } else if (_status == 2) {
+ if (_palCounter > 1) {
+ for (int i = 0; i < 256; i++)
+ fadeColor(_palette + i * 4, _basePalette[i * 4 + 0], _basePalette[i * 4 + 1], _basePalette[i * 4 + 2]);
+ _vm->_screen->testPalette(_palette);
+ _palCounter--;
+ } else {
+ memcpy(_palette, _basePalette, 256 * 4);
+ _status = 0;
+ }
+ }
+}
+
+void Palette::fadeColor(byte *rgb, byte toR, byte toG, byte toB) {
+ #define FADE(color, toColor) color += _fadeStep < toColor - color ? _fadeStep : (-_fadeStep <= toColor - color ? toColor - color : -_fadeStep)
+ FADE(rgb[0], toR);
+ FADE(rgb[1], toG);
+ FADE(rgb[2], toB);
+ #undef FADE
+}
+
+} // End of namespace Neverhood
diff --git a/engines/neverhood/palette.h b/engines/neverhood/palette.h
new file mode 100644
index 0000000000..c83207caae
--- /dev/null
+++ b/engines/neverhood/palette.h
@@ -0,0 +1,68 @@
+/* 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_PALETTE_H
+#define NEVERHOOD_PALETTE_H
+
+#include "neverhood/neverhood.h"
+#include "neverhood/entity.h"
+
+namespace Neverhood {
+
+class Palette : public Entity {
+public:
+ // Default constructor with black palette
+ Palette(NeverhoodEngine *vm);
+ // Create from existing palette
+ Palette(NeverhoodEngine *vm, byte *palette);
+ // Create from resource with filename
+ Palette(NeverhoodEngine *vm, const char *filename);
+ // Create from resource with fileHash
+ Palette(NeverhoodEngine *vm, uint32 fileHash);
+ virtual ~Palette();
+ void init();
+ void usePalette();
+ void addPalette(const char *filename, int toIndex, int count, int fromIndex);
+ void addPalette(uint32 fileHash, int toIndex, int count, int fromIndex);
+ void addBasePalette(uint32 fileHash, int toIndex, int count, int fromIndex);
+ void copyPalette(const byte *palette, int toIndex, int count, int fromIndex);
+ void copyBasePalette(int toIndex, int count, int fromIndex);
+ void startFadeToBlack(int counter);
+ void startFadeToWhite(int counter);
+ void startFadeToPalette(int counter);
+ void fillBaseWhite(int index, int count);
+ void fillBaseBlack(int index, int count);
+ void copyToBasePalette(byte *palette);
+protected:
+ int _status;
+ byte *_palette;
+ byte *_basePalette;
+ int _palCounter;
+ byte _fadeToR, _fadeToG, _fadeToB;
+ int _fadeStep;
+ void update();
+ void fadeColor(byte *rgb, byte toR, byte toG, byte toB);
+};
+
+} // End of namespace Neverhood
+
+#endif /* NEVERHOOD_PALETTE_H */
diff --git a/engines/neverhood/resource.cpp b/engines/neverhood/resource.cpp
new file mode 100644
index 0000000000..442713196e
--- /dev/null
+++ b/engines/neverhood/resource.cpp
@@ -0,0 +1,583 @@
+/* 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 "common/algorithm.h"
+#include "common/memstream.h"
+#include "neverhood/resource.h"
+#include "neverhood/resourceman.h"
+
+namespace Neverhood {
+
+// SpriteResource
+
+SpriteResource::SpriteResource(NeverhoodEngine *vm)
+ : _vm(vm), _pixels(NULL) {
+}
+
+SpriteResource::~SpriteResource() {
+ unload();
+}
+
+void SpriteResource::draw(Graphics::Surface *destSurface, bool flipX, bool flipY) {
+ if (_pixels) {
+ byte *dest = (byte*)destSurface->pixels;
+ const int destPitch = destSurface->pitch;
+ if (_rle)
+ unpackSpriteRle(_pixels, _dimensions.width, _dimensions.height, dest, destPitch, flipX, flipY);
+ else
+ unpackSpriteNormal(_pixels, _dimensions.width, _dimensions.height, dest, destPitch, flipX, flipY);
+ }
+}
+
+bool SpriteResource::load(uint32 fileHash, bool doLoadPosition) {
+ debug(2, "SpriteResource::load(%08X)", fileHash);
+ unload();
+ _vm->_res->queryResource(fileHash, _resourceHandle);
+ if (_resourceHandle.isValid() && _resourceHandle.type() == kResTypeBitmap) {
+ _vm->_res->loadResource(_resourceHandle);
+ const byte *spriteData = _resourceHandle.data();
+ NPoint *position = doLoadPosition ? &_position : NULL;
+ parseBitmapResource(spriteData, &_rle, &_dimensions, position, NULL, &_pixels);
+ }
+ return _pixels != NULL;
+}
+
+void SpriteResource::unload() {
+ _vm->_res->unloadResource(_resourceHandle);
+ _pixels = NULL;
+ _rle = false;
+}
+
+// PaletteResource
+
+PaletteResource::PaletteResource(NeverhoodEngine *vm)
+ : _vm(vm), _palette(NULL) {
+}
+
+PaletteResource::~PaletteResource() {
+ unload();
+}
+
+bool PaletteResource::load(uint32 fileHash) {
+ debug(2, "PaletteResource::load(%08X)", fileHash);
+ unload();
+ _vm->_res->queryResource(fileHash, _resourceHandle);
+ if (_resourceHandle.isValid() &&
+ (_resourceHandle.type() == kResTypeBitmap || _resourceHandle.type() == kResTypePalette)) {
+ _vm->_res->loadResource(_resourceHandle);
+ _palette = _resourceHandle.data();
+ // Check if the palette is stored in a bitmap
+ if (_resourceHandle.type() == kResTypeBitmap)
+ parseBitmapResource(_palette, NULL, NULL, NULL, &_palette, NULL);
+
+ }
+ return _palette != NULL;
+}
+
+void PaletteResource::unload() {
+ _vm->_res->unloadResource(_resourceHandle);
+ _palette = NULL;
+}
+
+void PaletteResource::copyPalette(byte *destPalette) {
+ if (_palette)
+ memcpy(destPalette, _palette, 1024);
+}
+
+// AnimResource
+
+AnimResource::AnimResource(NeverhoodEngine *vm)
+ : _vm(vm), _width(0), _height(0), _currSpriteData(NULL), _fileHash(0), _paletteData(NULL),
+ _spriteData(NULL), _replEnabled(false), _replOldColor(0), _replNewColor(0) {
+}
+
+AnimResource::~AnimResource() {
+ unload();
+}
+
+void AnimResource::draw(uint frameIndex, Graphics::Surface *destSurface, bool flipX, bool flipY) {
+ const AnimFrameInfo frameInfo = _frames[frameIndex];
+ byte *dest = (byte*)destSurface->pixels;
+ const int destPitch = destSurface->pitch;
+ _currSpriteData = _spriteData + frameInfo.spriteDataOffs;
+ _width = frameInfo.drawOffset.width;
+ _height = frameInfo.drawOffset.height;
+ if (_replEnabled && _replOldColor != _replNewColor)
+ unpackSpriteRle(_currSpriteData, _width, _height, dest, destPitch, flipX, flipY, _replOldColor, _replNewColor);
+ else
+ unpackSpriteRle(_currSpriteData, _width, _height, dest, destPitch, flipX, flipY);
+}
+
+bool AnimResource::load(uint32 fileHash) {
+ debug(2, "AnimResource::load(%08X)", fileHash);
+
+ if (fileHash == _fileHash)
+ return true;
+
+ unload();
+
+ _vm->_res->queryResource(fileHash, _resourceHandle);
+ if (!_resourceHandle.isValid() || _resourceHandle.type() != kResTypeAnimation)
+ return false;
+
+ const byte *resourceData, *animList, *frameList;
+ uint16 animInfoStartOfs, animListIndex, animListCount;
+ uint16 frameListStartOfs, frameCount;
+ uint32 spriteDataOfs, paletteDataOfs;
+
+ _vm->_res->loadResource(_resourceHandle);
+ resourceData = _resourceHandle.data();
+
+ animListCount = READ_LE_UINT16(resourceData);
+ animInfoStartOfs = READ_LE_UINT16(resourceData + 2);
+ spriteDataOfs = READ_LE_UINT32(resourceData + 4);
+ paletteDataOfs = READ_LE_UINT32(resourceData + 8);
+
+ animList = resourceData + 12;
+ for (animListIndex = 0; animListIndex < animListCount; animListIndex++) {
+ debug(8, "hash: %08X", READ_LE_UINT32(animList));
+ if (READ_LE_UINT32(animList) == fileHash)
+ break;
+ animList += 8;
+ }
+
+ if (animListIndex >= animListCount) {
+ _vm->_res->unloadResource(_resourceHandle);
+ return false;
+ }
+
+ _spriteData = resourceData + spriteDataOfs;
+ if (paletteDataOfs > 0)
+ _paletteData = resourceData + paletteDataOfs;
+
+ frameCount = READ_LE_UINT16(animList + 4);
+ frameListStartOfs = READ_LE_UINT16(animList + 6);
+
+ debug(8, "frameCount = %d; frameListStartOfs = %04X; animInfoStartOfs = %04X", frameCount, frameListStartOfs, animInfoStartOfs);
+
+ frameList = resourceData + animInfoStartOfs + frameListStartOfs;
+
+ _frames.clear();
+ _frames.reserve(frameCount);
+
+ for (uint16 frameIndex = 0; frameIndex < frameCount; frameIndex++) {
+ AnimFrameInfo frameInfo;
+ frameInfo.frameHash = READ_LE_UINT32(frameList);
+ frameInfo.counter = READ_LE_UINT16(frameList + 4);
+ frameInfo.drawOffset.x = READ_LE_UINT16(frameList + 6);
+ frameInfo.drawOffset.y = READ_LE_UINT16(frameList + 8);
+ frameInfo.drawOffset.width = READ_LE_UINT16(frameList + 10);
+ frameInfo.drawOffset.height = READ_LE_UINT16(frameList + 12);
+ frameInfo.deltaX = READ_LE_UINT16(frameList + 14);
+ frameInfo.deltaY = READ_LE_UINT16(frameList + 16);
+ frameInfo.collisionBoundsOffset.x = READ_LE_UINT16(frameList + 18);
+ frameInfo.collisionBoundsOffset.y = READ_LE_UINT16(frameList + 20);
+ frameInfo.collisionBoundsOffset.width = READ_LE_UINT16(frameList + 22);
+ frameInfo.collisionBoundsOffset.height = READ_LE_UINT16(frameList + 24);
+ frameInfo.spriteDataOffs = READ_LE_UINT32(frameList + 28);
+ debug(8, "frameHash = %08X; counter = %d; rect = (%d,%d,%d,%d); deltaX = %d; deltaY = %d; collisionBoundsOffset = (%d,%d,%d,%d); spriteDataOffs = %08X",
+ frameInfo.frameHash, frameInfo.counter,
+ frameInfo.drawOffset.x, frameInfo.drawOffset.y, frameInfo.drawOffset.width, frameInfo.drawOffset.height,
+ frameInfo.deltaX, frameInfo.deltaY,
+ frameInfo.collisionBoundsOffset.x, frameInfo.collisionBoundsOffset.y, frameInfo.collisionBoundsOffset.width, frameInfo.collisionBoundsOffset.height,
+ frameInfo.spriteDataOffs);
+ frameList += 32;
+ _frames.push_back(frameInfo);
+ }
+
+ _fileHash = fileHash;
+
+ return true;
+
+}
+
+void AnimResource::unload() {
+ _vm->_res->unloadResource(_resourceHandle);
+ _currSpriteData = NULL;
+ _fileHash = 0;
+ _paletteData = NULL;
+ _spriteData = NULL;
+ _replEnabled = true;
+ _replOldColor = 0;
+ _replNewColor = 0;
+}
+
+int16 AnimResource::getFrameIndex(uint32 frameHash) {
+ int16 frameIndex = -1;
+ for (uint i = 0; i < _frames.size(); i++)
+ if (_frames[i].frameHash == frameHash) {
+ frameIndex = (int16)i;
+ break;
+ }
+ debug(2, "AnimResource::getFrameIndex(%08X) -> %d", frameHash, frameIndex);
+ return frameIndex;
+}
+
+void AnimResource::setRepl(byte oldColor, byte newColor) {
+ _replOldColor = oldColor;
+ _replNewColor = newColor;
+}
+
+NDimensions AnimResource::loadSpriteDimensions(uint32 fileHash) {
+ ResourceHandle resourceHandle;
+ NDimensions dimensions;
+ _vm->_res->queryResource(fileHash, resourceHandle);
+ const byte *resDimensions = resourceHandle.extData();
+ if (resDimensions) {
+ dimensions.width = READ_LE_UINT16(resDimensions + 0);
+ dimensions.height = READ_LE_UINT16(resDimensions + 2);
+ } else {
+ dimensions.width = 0;
+ dimensions.height = 0;
+ }
+ return dimensions;
+}
+
+// MouseCursorResource
+
+MouseCursorResource::MouseCursorResource(NeverhoodEngine *vm)
+ : _cursorSprite(vm), _cursorNum(4), _currFileHash(0) {
+
+ _rect.width = 32;
+ _rect.height = 32;
+}
+
+void MouseCursorResource::load(uint32 fileHash) {
+ if (_currFileHash != fileHash) {
+ if (_cursorSprite.load(fileHash) && !_cursorSprite.isRle() &&
+ _cursorSprite.getDimensions().width == 96 && _cursorSprite.getDimensions().height == 224) {
+ _currFileHash = fileHash;
+ } else {
+ unload();
+ }
+ }
+}
+
+void MouseCursorResource::unload() {
+ _cursorSprite.unload();
+ _currFileHash = 0;
+ _cursorNum = 4;
+}
+
+NDrawRect& MouseCursorResource::getRect() {
+ static const NPoint kCursorHotSpots[] = {
+ {-15, -5},
+ {-17, -25},
+ {-17, -30},
+ {-14, -1},
+ {-3, -7},
+ {-30, -18},
+ {-1, -18}
+ };
+ _rect.x = kCursorHotSpots[_cursorNum].x;
+ _rect.y = kCursorHotSpots[_cursorNum].y;
+ return _rect;
+}
+
+void MouseCursorResource::draw(int frameNum, Graphics::Surface *destSurface) {
+ if (_cursorSprite.getPixels()) {
+ const int sourcePitch = (_cursorSprite.getDimensions().width + 3) & 0xFFFC; // 4 byte alignment
+ const int destPitch = destSurface->pitch;
+ const byte *source = _cursorSprite.getPixels() + _cursorNum * (sourcePitch * 32) + frameNum * 32;
+ byte *dest = (byte*)destSurface->pixels;
+ for (int16 yc = 0; yc < 32; yc++) {
+ memcpy(dest, source, 32);
+ source += sourcePitch;
+ dest += destPitch;
+ }
+ }
+}
+
+// TextResource
+
+TextResource::TextResource(NeverhoodEngine *vm)
+ : _vm(vm), _textData(NULL), _count(0) {
+
+}
+
+TextResource::~TextResource() {
+ unload();
+}
+
+void TextResource::load(uint32 fileHash) {
+ debug(2, "TextResource::load(%08X)", fileHash);
+ unload();
+ _vm->_res->queryResource(fileHash, _resourceHandle);
+ if (_resourceHandle.isValid() && _resourceHandle.type() == kResTypeText) {
+ _vm->_res->loadResource(_resourceHandle);
+ _textData = _resourceHandle.data();
+ _count = READ_LE_UINT32(_textData);
+ }
+}
+
+void TextResource::unload() {
+ _vm->_res->unloadResource(_resourceHandle);
+ _textData = NULL;
+ _count = 0;
+}
+
+const char *TextResource::getString(uint index, const char *&textEnd) {
+ const char *textStart = (const char*)(_textData + 4 + _count * 4 + READ_LE_UINT32(_textData + (index + 1) * 4));
+ textEnd = (const char*)(_textData + 4 + _count * 4 + READ_LE_UINT32(_textData + (index + 2) * 4));
+ return textStart;
+}
+
+// DataResource
+
+DataResource::DataResource(NeverhoodEngine *vm)
+ : _vm(vm) {
+}
+
+DataResource::~DataResource() {
+ unload();
+}
+
+void DataResource::load(uint32 fileHash) {
+ if (_resourceHandle.fileHash() == fileHash)
+ return;
+ const byte *data = NULL;
+ uint32 dataSize = 0;
+ unload();
+ _vm->_res->queryResource(fileHash, _resourceHandle);
+ if (_resourceHandle.isValid() && _resourceHandle.type() == kResTypeData) {
+ _vm->_res->loadResource(_resourceHandle);
+ data = _resourceHandle.data();
+ dataSize = _resourceHandle.size();
+ }
+ if (data && dataSize) {
+ Common::MemoryReadStream dataS(data, dataSize);
+ uint itemCount = dataS.readUint16LE();
+ uint32 itemStartOffs = 2 + itemCount * 8;
+ debug(2, "itemCount = %d", itemCount);
+ for (uint i = 0; i < itemCount; i++) {
+ dataS.seek(2 + i * 8);
+ DRDirectoryItem drDirectoryItem;
+ drDirectoryItem.nameHash = dataS.readUint32LE();
+ drDirectoryItem.offset = dataS.readUint16LE();
+ drDirectoryItem.type = dataS.readUint16LE();
+ debug(2, "%03d nameHash = %08X; offset = %04X; type = %d", i, drDirectoryItem.nameHash, drDirectoryItem.offset, drDirectoryItem.type);
+ dataS.seek(itemStartOffs + drDirectoryItem.offset);
+ switch (drDirectoryItem.type) {
+ case 1:
+ {
+ debug(3, "NPoint");
+ NPoint point;
+ point.x = dataS.readUint16LE();
+ point.y = dataS.readUint16LE();
+ debug(3, "(%d, %d)", point.x, point.y);
+ drDirectoryItem.offset = _points.size();
+ _points.push_back(point);
+ break;
+ }
+ case 2:
+ {
+ uint count = dataS.readUint16LE();
+ NPointArray *pointArray = new NPointArray();
+ debug(3, "NPointArray; count = %d", count);
+ for (uint j = 0; j < count; j++) {
+ NPoint point;
+ point.x = dataS.readUint16LE();
+ point.y = dataS.readUint16LE();
+ debug(3, "(%d, %d)", point.x, point.y);
+ pointArray->push_back(point);
+ }
+ drDirectoryItem.offset = _pointArrays.size();
+ _pointArrays.push_back(pointArray);
+ break;
+ }
+ case 3:
+ {
+ uint count = dataS.readUint16LE();
+ HitRectList *hitRectList = new HitRectList();
+ debug(3, "HitRectList; count = %d", count);
+ for (uint j = 0; j < count; j++) {
+ HitRect hitRect;
+ hitRect.rect.x1 = dataS.readUint16LE();
+ hitRect.rect.y1 = dataS.readUint16LE();
+ hitRect.rect.x2 = dataS.readUint16LE();
+ hitRect.rect.y2 = dataS.readUint16LE();
+ hitRect.type = dataS.readUint16LE() + 0x5001;
+ debug(3, "(%d, %d, %d, %d) -> %04d", hitRect.rect.x1, hitRect.rect.y1, hitRect.rect.x2, hitRect.rect.y2, hitRect.type);
+ hitRectList->push_back(hitRect);
+ }
+ drDirectoryItem.offset = _hitRectLists.size();
+ _hitRectLists.push_back(hitRectList);
+ break;
+ }
+ case 4:
+ {
+ uint count = dataS.readUint16LE();
+ MessageList *messageList = new MessageList();
+ debug(3, "MessageList; count = %d", count);
+ for (uint j = 0; j < count; j++) {
+ MessageItem messageItem;
+ messageItem.messageNum = dataS.readUint32LE();
+ messageItem.messageValue = dataS.readUint32LE();
+ debug(3, "(%08X, %08X)", messageItem.messageNum, messageItem.messageValue);
+ messageList->push_back(messageItem);
+ }
+ drDirectoryItem.offset = _messageLists.size();
+ _messageLists.push_back(messageList);
+ break;
+ }
+ case 5:
+ {
+ uint count = dataS.readUint16LE();
+ DRSubRectList *drSubRectList = new DRSubRectList();
+ debug(3, "SubRectList; count = %d", count);
+ for (uint j = 0; j < count; j++) {
+ DRSubRect drSubRect;
+ drSubRect.rect.x1 = dataS.readUint16LE();
+ drSubRect.rect.y1 = dataS.readUint16LE();
+ drSubRect.rect.x2 = dataS.readUint16LE();
+ drSubRect.rect.y2 = dataS.readUint16LE();
+ drSubRect.messageListHash = dataS.readUint32LE();
+ drSubRect.messageListItemIndex = dataS.readUint16LE();
+ debug(3, "(%d, %d, %d, %d) -> %08X (%d)", drSubRect.rect.x1, drSubRect.rect.y1, drSubRect.rect.x2, drSubRect.rect.y2, drSubRect.messageListHash, drSubRect.messageListItemIndex);
+ drSubRectList->push_back(drSubRect);
+ }
+ drDirectoryItem.offset = _drSubRectLists.size();
+ _drSubRectLists.push_back(drSubRectList);
+ break;
+ }
+ case 6:
+ {
+ DRRect drRect;
+ drRect.rect.x1 = dataS.readUint16LE();
+ drRect.rect.y1 = dataS.readUint16LE();
+ drRect.rect.x2 = dataS.readUint16LE();
+ drRect.rect.y2 = dataS.readUint16LE();
+ drRect.subRectIndex = dataS.readUint16LE();
+ debug(3, "(%d, %d, %d, %d) -> %d", drRect.rect.x1, drRect.rect.y1, drRect.rect.x2, drRect.rect.y2, drRect.subRectIndex);
+ drDirectoryItem.offset = _drRects.size();
+ _drRects.push_back(drRect);
+ break;
+ }
+ case 7:
+ {
+ uint count = dataS.readUint16LE();
+ NRectArray *rectArray = new NRectArray();
+ debug(3, "NRectArray; count = %d", count);
+ for (uint j = 0; j < count; j++) {
+ NRect rect;
+ rect.x1 = dataS.readUint16LE();
+ rect.y1 = dataS.readUint16LE();
+ rect.x2 = dataS.readUint16LE();
+ rect.y2 = dataS.readUint16LE();
+ debug(3, "(%d, %d, %d, %d)", rect.x1, rect.y1, rect.x2, rect.y2);
+ rectArray->push_back(rect);
+ }
+ drDirectoryItem.offset = _rectArrays.size();
+ _rectArrays.push_back(rectArray);
+ break;
+ }
+ }
+ _directory.push_back(drDirectoryItem);
+ }
+ }
+}
+
+void DataResource::unload() {
+ _directory.clear();
+ _points.clear();
+ for (Common::Array<NPointArray*>::iterator it = _pointArrays.begin(); it != _pointArrays.end(); ++it)
+ delete (*it);
+ _pointArrays.clear();
+ for (Common::Array<NRectArray*>::iterator it = _rectArrays.begin(); it != _rectArrays.end(); ++it)
+ delete (*it);
+ _rectArrays.clear();
+ for (Common::Array<HitRectList*>::iterator it = _hitRectLists.begin(); it != _hitRectLists.end(); ++it)
+ delete (*it);
+ _hitRectLists.clear();
+ for (Common::Array<MessageList*>::iterator it = _messageLists.begin(); it != _messageLists.end(); ++it)
+ delete (*it);
+ _messageLists.clear();
+ _drRects.clear();
+ for (Common::Array<DRSubRectList*>::iterator it = _drSubRectLists.begin(); it != _drSubRectLists.end(); ++it)
+ delete (*it);
+ _drSubRectLists.clear();
+ _vm->_res->unloadResource(_resourceHandle);
+}
+
+NPoint DataResource::getPoint(uint32 nameHash) {
+ DataResource::DRDirectoryItem *drDirectoryItem = findDRDirectoryItem(nameHash, 1);
+ return drDirectoryItem ? _points[drDirectoryItem->offset] : NPoint();
+}
+
+NPointArray *DataResource::getPointArray(uint32 nameHash) {
+ DataResource::DRDirectoryItem *drDirectoryItem = findDRDirectoryItem(nameHash, 2);
+ return drDirectoryItem ? _pointArrays[drDirectoryItem->offset] : NULL;
+}
+
+NRectArray *DataResource::getRectArray(uint32 nameHash) {
+ DataResource::DRDirectoryItem *drDirectoryItem = findDRDirectoryItem(nameHash, 3);
+ return drDirectoryItem ? _rectArrays[drDirectoryItem->offset] : NULL;
+}
+
+HitRectList *DataResource::getHitRectList() {
+ DataResource::DRDirectoryItem *drDirectoryItem = findDRDirectoryItem(calcHash("HitArray"), 3);
+ return drDirectoryItem ? _hitRectLists[drDirectoryItem->offset] : NULL;
+}
+
+MessageList *DataResource::getMessageListAtPos(int16 klaymenX, int16 klaymenY, int16 mouseX, int16 mouseY) {
+ for (uint i = 0; i < _drRects.size(); i++) {
+ if (klaymenX >= _drRects[i].rect.x1 && klaymenX <= _drRects[i].rect.x2 &&
+ klaymenY >= _drRects[i].rect.y1 && klaymenY <= _drRects[i].rect.y2) {
+ DRSubRectList *drSubRectList = _drSubRectLists[_drRects[i].subRectIndex];
+ for (uint j = 0; j < drSubRectList->size(); j++) {
+ DRSubRect &subRect = (*drSubRectList)[j];
+ if (mouseX >= subRect.rect.x1 && mouseX <= subRect.rect.x2 &&
+ mouseY >= subRect.rect.y1 && mouseY <= subRect.rect.y2) {
+ return _messageLists[subRect.messageListItemIndex];
+ }
+ }
+ }
+ }
+ return NULL;
+}
+
+DataResource::DRDirectoryItem *DataResource::findDRDirectoryItem(uint32 nameHash, uint16 type) {
+ for (Common::Array<DRDirectoryItem>::iterator it = _directory.begin(); it != _directory.end(); it++)
+ if ((*it).nameHash == nameHash && (*it).type == type)
+ return &(*it);
+ return NULL;
+}
+
+uint32 calcHash(const char *value) {
+ uint32 hash = 0, shiftValue = 0;
+ while (*value != 0) {
+ char ch = *value++;
+ if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9')) {
+ if (ch >= 'a' && ch <= 'z')
+ ch -= 32;
+ else if (ch >= '0' && ch <= '9')
+ ch += 22;
+ shiftValue += ch - 64;
+ if (shiftValue >= 32)
+ shiftValue -= 32;
+ hash ^= 1 << shiftValue;
+ }
+ }
+ return hash;
+}
+
+} // End of namespace Neverhood
diff --git a/engines/neverhood/resource.h b/engines/neverhood/resource.h
new file mode 100644
index 0000000000..6a84a69ecd
--- /dev/null
+++ b/engines/neverhood/resource.h
@@ -0,0 +1,205 @@
+/* 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_RESOURCE_H
+#define NEVERHOOD_RESOURCE_H
+
+#include "common/str.h"
+#include "neverhood/neverhood.h"
+#include "neverhood/graphics.h"
+#include "neverhood/staticdata.h"
+#include "neverhood/resourceman.h"
+
+namespace Neverhood {
+
+enum {
+ kResTypeBitmap = 2,
+ kResTypePalette = 3,
+ kResTypeAnimation = 4,
+ kResTypeData = 5,
+ kResTypeText = 6,
+ kResTypeSound = 7,
+ kResTypeMusic = 8,
+ kResTypeVideo = 10
+};
+
+class SpriteResource {
+public:
+ SpriteResource(NeverhoodEngine *vm);
+ ~SpriteResource();
+ void draw(Graphics::Surface *destSurface, bool flipX, bool flipY);
+ bool load(uint32 fileHash, bool doLoadPosition = false);
+ void unload();
+ const NDimensions& getDimensions() { return _dimensions; }
+ NPoint& getPosition() { return _position; }
+ bool isRle() const { return _rle; }
+ const byte *getPixels() const { return _pixels; }
+protected:
+ NeverhoodEngine *_vm;
+ ResourceHandle _resourceHandle;
+ NDimensions _dimensions;
+ NPoint _position;
+ const byte *_pixels;
+ bool _rle;
+};
+
+class PaletteResource {
+public:
+ PaletteResource(NeverhoodEngine *vm);
+ ~PaletteResource();
+ bool load(uint32 fileHash);
+ void unload();
+ void copyPalette(byte *destPalette);
+ const byte *palette() { return _palette; }
+protected:
+ NeverhoodEngine *_vm;
+ ResourceHandle _resourceHandle;
+ const byte *_palette;
+};
+
+struct AnimFrameInfo {
+ uint32 frameHash;
+ int16 counter;
+ NDrawRect drawOffset;
+ int16 deltaX, deltaY;
+ NDrawRect collisionBoundsOffset;
+ uint32 spriteDataOffs;
+};
+
+class AnimResource {
+public:
+ AnimResource(NeverhoodEngine *vm);
+ ~AnimResource();
+ void draw(uint frameIndex, Graphics::Surface *destSurface, bool flipX, bool flipY);
+ bool load(uint32 fileHash);
+ void unload();
+ void clear();
+ uint getFrameCount() const { return _frames.size(); }
+ const AnimFrameInfo& getFrameInfo(int16 index) const { return _frames[index]; }
+ int16 getFrameIndex(uint32 frameHash);
+ void setReplEnabled(bool value) { _replEnabled = value; }
+ void setRepl(byte oldColor, byte newColor);
+ NDimensions loadSpriteDimensions(uint32 fileHash);
+protected:
+ NeverhoodEngine *_vm;
+ ResourceHandle _resourceHandle;
+ int16 _width, _height;
+ const byte *_currSpriteData;
+ uint32 _fileHash;
+ const byte *_paletteData;
+ const byte *_spriteData;
+ bool _replEnabled;
+ byte _replOldColor;
+ byte _replNewColor;
+ Common::Array<AnimFrameInfo> _frames;
+};
+
+class MouseCursorResource {
+public:
+ MouseCursorResource(NeverhoodEngine *vm);
+ void load(uint32 fileHash);
+ void unload();
+ NDrawRect& getRect();
+ void draw(int frameNum, Graphics::Surface *destSurface);
+ int getCursorNum() const { return _cursorNum; }
+ void setCursorNum(int cursorNum) { _cursorNum = cursorNum; }
+protected:
+ int _cursorNum;
+ SpriteResource _cursorSprite;
+ NDrawRect _rect;
+ uint32 _currFileHash;
+};
+
+class TextResource {
+public:
+ TextResource(NeverhoodEngine *vm);
+ ~TextResource();
+ void load(uint32 fileHash);
+ void unload();
+ const char *getString(uint index, const char *&textEnd);
+ uint getCount() const { return _count;}
+protected:
+ NeverhoodEngine *_vm;
+ ResourceHandle _resourceHandle;
+ const byte *_textData;
+ uint _count;
+};
+
+/* DataResource
+ 1 Single NPoint
+ 2 Array of NPoints
+ 3 Array of NRects
+ 4 MessageList
+ 5 SubRectList
+ 6 RectList
+*/
+
+class DataResource {
+public:
+ DataResource(NeverhoodEngine *vm);
+ ~DataResource();
+ void load(uint32 fileHash);
+ void unload();
+ NPoint getPoint(uint32 nameHash);
+ NPointArray *getPointArray(uint32 nameHash);
+ NRectArray *getRectArray(uint32 nameHash);
+ HitRectList *getHitRectList();
+ MessageList *getMessageListAtPos(int16 klaymenX, int16 klaymenY, int16 mouseX, int16 mouseY);
+protected:
+
+ struct DRDirectoryItem {
+ uint32 nameHash;
+ uint16 offset;
+ uint16 type;
+ };
+
+ struct DRRect {
+ NRect rect;
+ uint16 subRectIndex;
+ };
+
+ struct DRSubRect {
+ NRect rect;
+ uint32 messageListHash;
+ uint16 messageListItemIndex;
+ };
+
+ typedef Common::Array<DRSubRect> DRSubRectList;
+
+ NeverhoodEngine *_vm;
+ ResourceHandle _resourceHandle;
+ Common::Array<DRDirectoryItem> _directory;
+ Common::Array<NPoint> _points;
+ Common::Array<NPointArray*> _pointArrays;
+ Common::Array<NRectArray*> _rectArrays;
+ Common::Array<HitRectList*> _hitRectLists;
+ Common::Array<MessageList*> _messageLists;
+ Common::Array<DRRect> _drRects;
+ Common::Array<DRSubRectList*> _drSubRectLists;
+ DataResource::DRDirectoryItem *findDRDirectoryItem(uint32 nameHash, uint16 type);
+};
+
+uint32 calcHash(const char *value);
+
+} // End of namespace Neverhood
+
+#endif /* NEVERHOOD_RESOURCE_H */
diff --git a/engines/neverhood/resourceman.cpp b/engines/neverhood/resourceman.cpp
new file mode 100644
index 0000000000..d5e7786c17
--- /dev/null
+++ b/engines/neverhood/resourceman.cpp
@@ -0,0 +1,128 @@
+/* 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/resourceman.h"
+
+namespace Neverhood {
+
+ResourceHandle::ResourceHandle()
+ : _resourceFileEntry(NULL), _data(NULL) {
+}
+
+ResourceHandle::~ResourceHandle() {
+}
+
+ResourceMan::ResourceMan() {
+}
+
+ResourceMan::~ResourceMan() {
+}
+
+void ResourceMan::addArchive(const Common::String &filename) {
+ BlbArchive *archive = new BlbArchive();
+ archive->open(filename);
+ _archives.push_back(archive);
+ debug(3, "ResourceMan::addArchive(%s) %d files", filename.c_str(), archive->getCount());
+ for (uint archiveEntryIndex = 0; archiveEntryIndex < archive->getCount(); archiveEntryIndex++) {
+ BlbArchiveEntry *archiveEntry = archive->getEntry(archiveEntryIndex);
+ ResourceFileEntry *entry = findEntrySimple(archiveEntry->fileHash);
+ if (entry) {
+ if (archiveEntry->timeStamp > entry->archiveEntry->timeStamp) {
+ entry->archive = archive;
+ entry->archiveEntry = archiveEntry;
+ }
+ } else {
+ ResourceFileEntry newEntry;
+ newEntry.resourceHandle = -1;
+ newEntry.archive = archive;
+ newEntry.archiveEntry = archiveEntry;
+ _entries[archiveEntry->fileHash] = newEntry;
+ }
+ }
+}
+
+ResourceFileEntry *ResourceMan::findEntrySimple(uint32 fileHash) {
+ EntriesMap::iterator p = _entries.find(fileHash);
+ return p != _entries.end() ? &(*p)._value : NULL;
+}
+
+ResourceFileEntry *ResourceMan::findEntry(uint32 fileHash, ResourceFileEntry **firstEntry) {
+ ResourceFileEntry *entry = findEntrySimple(fileHash);
+ if (firstEntry)
+ *firstEntry = entry;
+ for (; entry && entry->archiveEntry->comprType == 0x65; fileHash = entry->archiveEntry->diskSize)
+ entry = findEntrySimple(fileHash);
+ return entry;
+}
+
+Common::SeekableReadStream *ResourceMan::createStream(uint32 fileHash) {
+ ResourceFileEntry *entry = findEntry(fileHash);
+ return entry ? entry->archive->createStream(entry->archiveEntry) : NULL;
+}
+
+void ResourceMan::queryResource(uint32 fileHash, ResourceHandle &resourceHandle) {
+ ResourceFileEntry *firstEntry;
+ resourceHandle._resourceFileEntry = findEntry(fileHash, &firstEntry);
+ resourceHandle._extData = firstEntry ? firstEntry->archiveEntry->extData : NULL;
+}
+
+void ResourceMan::loadResource(ResourceHandle &resourceHandle) {
+ resourceHandle._data = NULL;
+ if (resourceHandle.isValid()) {
+ const uint32 fileHash = resourceHandle.fileHash();
+ ResourceData *resourceData = _data[fileHash];
+ if (!resourceData) {
+ resourceData = new ResourceData();
+ _data[fileHash] = resourceData;
+ }
+ if (resourceData->data != NULL) {
+ resourceData->dataRefCount++;
+ } else {
+ resourceData->data = new byte[resourceHandle._resourceFileEntry->archiveEntry->size];
+ resourceHandle._resourceFileEntry->archive->load(resourceHandle._resourceFileEntry->archiveEntry, resourceData->data, 0);
+ resourceData->dataRefCount = 1;
+ }
+ resourceHandle._data = resourceData->data;
+ }
+}
+
+void ResourceMan::unloadResource(ResourceHandle &resourceHandle) {
+ if (resourceHandle.isValid()) {
+ ResourceData *resourceData = _data[resourceHandle.fileHash()];
+ if (resourceData && resourceData->dataRefCount > 0)
+ --resourceData->dataRefCount;
+ resourceHandle._resourceFileEntry = NULL;
+ resourceHandle._data = NULL;
+ }
+}
+
+void ResourceMan::purgeResources() {
+ for (Common::HashMap<uint32, ResourceData*>::iterator it = _data.begin(); it != _data.end(); ++it) {
+ ResourceData *resourceData = (*it)._value;
+ if (resourceData->dataRefCount == 0) {
+ delete[] resourceData->data;
+ resourceData->data = NULL;
+ }
+ }
+}
+
+} // End of namespace Neverhood
diff --git a/engines/neverhood/resourceman.h b/engines/neverhood/resourceman.h
new file mode 100644
index 0000000000..5a3697fe0d
--- /dev/null
+++ b/engines/neverhood/resourceman.h
@@ -0,0 +1,94 @@
+/* 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_RESOURCEMAN_H
+#define NEVERHOOD_RESOURCEMAN_H
+
+#include "common/array.h"
+#include "common/file.h"
+#include "common/hashmap.h"
+#include "neverhood/neverhood.h"
+#include "neverhood/blbarchive.h"
+
+namespace Neverhood {
+
+struct ResourceFileEntry {
+ int resourceHandle;
+ BlbArchive *archive;
+ BlbArchiveEntry *archiveEntry;
+};
+
+struct Resource {
+ ResourceFileEntry *entry;
+ int useRefCount;
+};
+
+struct ResourceData {
+ byte *data;
+ int dataRefCount;
+ ResourceData() : data(NULL), dataRefCount() {}
+};
+
+class ResourceMan;
+
+struct ResourceHandle {
+friend class ResourceMan;
+public:
+ ResourceHandle();
+ ~ResourceHandle();
+ bool isValid() const { return _resourceFileEntry != NULL && _resourceFileEntry->archiveEntry != NULL; }
+ byte type() const { return isValid() ? _resourceFileEntry->archiveEntry->type : 0; };
+ const byte *data() const { return _data; }
+ uint32 size() const { return isValid() ? _resourceFileEntry->archiveEntry->size : 0; };
+ const byte *extData() const { return _extData; };
+ uint32 fileHash() const { return isValid() ? _resourceFileEntry->archiveEntry->fileHash : 0; };
+protected:
+ ResourceFileEntry *_resourceFileEntry;
+ const byte *_extData;
+ const byte *_data;
+};
+
+class ResourceMan {
+public:
+ ResourceMan();
+ ~ResourceMan();
+ void addArchive(const Common::String &filename);
+ ResourceFileEntry *findEntrySimple(uint32 fileHash);
+ ResourceFileEntry *findEntry(uint32 fileHash, ResourceFileEntry **firstEntry = NULL);
+ Common::SeekableReadStream *createStream(uint32 fileHash);
+ const ResourceFileEntry& getEntry(uint index) { return _entries[index]; }
+ uint getEntryCount() { return _entries.size(); }
+ void queryResource(uint32 fileHash, ResourceHandle &resourceHandle);
+ void loadResource(ResourceHandle &resourceHandle);
+ void unloadResource(ResourceHandle &resourceHandle);
+ void purgeResources();
+protected:
+ typedef Common::HashMap<uint32, ResourceFileEntry> EntriesMap;
+ Common::Array<BlbArchive*> _archives;
+ EntriesMap _entries;
+ Common::HashMap<uint32, ResourceData*> _data;
+ Common::Array<Resource*> _resources;
+};
+
+} // End of namespace Neverhood
+
+#endif /* NEVERHOOD_RESOURCEMAN_H */
diff --git a/engines/neverhood/saveload.cpp b/engines/neverhood/saveload.cpp
new file mode 100644
index 0000000000..578d9858ff
--- /dev/null
+++ b/engines/neverhood/saveload.cpp
@@ -0,0 +1,164 @@
+/* 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 "common/savefile.h"
+
+#include "graphics/thumbnail.h"
+
+#include "neverhood/neverhood.h"
+#include "neverhood/gamemodule.h"
+#include "neverhood/gamevars.h"
+
+namespace Neverhood {
+
+#define NEVERHOOD_SAVEGAME_VERSION 0
+
+NeverhoodEngine::kReadSaveHeaderError NeverhoodEngine::readSaveHeader(Common::SeekableReadStream *in, bool loadThumbnail, SaveHeader &header) {
+
+ header.version = in->readUint32LE();
+ if (header.version > NEVERHOOD_SAVEGAME_VERSION)
+ return kRSHEInvalidVersion;
+
+ byte descriptionLen = in->readByte();
+ header.description = "";
+ while (descriptionLen--)
+ header.description += (char)in->readByte();
+
+ if (loadThumbnail) {
+ header.thumbnail = Graphics::loadThumbnail(*in);
+ } else {
+ Graphics::skipThumbnail(*in);
+ }
+
+ // Not used yet, reserved for future usage
+ header.gameID = in->readByte();
+ header.flags = in->readUint32LE();
+
+ header.saveDate = in->readUint32LE();
+ header.saveTime = in->readUint32LE();
+ header.playTime = in->readUint32LE();
+
+ return ((in->eos() || in->err()) ? kRSHEIoError : kRSHENoError);
+}
+
+void NeverhoodEngine::savegame(const char *filename, const char *description) {
+
+ Common::OutSaveFile *out;
+ if (!(out = g_system->getSavefileManager()->openForSaving(filename))) {
+ warning("Can't create file '%s', game not saved", filename);
+ return;
+ }
+
+ TimeDate curTime;
+ g_system->getTimeAndDate(curTime);
+
+ // Header start
+ out->writeUint32LE(NEVERHOOD_SAVEGAME_VERSION);
+
+ byte descriptionLen = strlen(description);
+ out->writeByte(descriptionLen);
+ out->write(description, descriptionLen);
+
+ Graphics::saveThumbnail(*out);
+
+ // Not used yet, reserved for future usage
+ out->writeByte(0);
+ out->writeUint32LE(0);
+ uint32 saveDate = ((curTime.tm_mday & 0xFF) << 24) | (((curTime.tm_mon + 1) & 0xFF) << 16) | ((curTime.tm_year + 1900) & 0xFFFF);
+ uint32 saveTime = ((curTime.tm_hour & 0xFF) << 16) | (((curTime.tm_min) & 0xFF) << 8) | ((curTime.tm_sec) & 0xFF);
+ uint32 playTime = g_engine->getTotalPlayTime() / 1000;
+ out->writeUint32LE(saveDate);
+ out->writeUint32LE(saveTime);
+ out->writeUint32LE(playTime);
+ // Header end
+
+ _gameVars->setGlobalVar(V_CURRENT_SCENE, _gameState.sceneNum);
+ _gameVars->setGlobalVar(V_CURRENT_SCENE_WHICH, _gameState.which);
+
+ _gameVars->saveState(out);
+
+ out->finalize();
+ delete out;
+}
+
+void NeverhoodEngine::loadgame(const char *filename) {
+ Common::InSaveFile *in;
+ if (!(in = g_system->getSavefileManager()->openForLoading(filename))) {
+ warning("Can't open file '%s', game not loaded", filename);
+ return;
+ }
+
+ SaveHeader header;
+
+ kReadSaveHeaderError errorCode = readSaveHeader(in, false, header);
+
+ if (errorCode != kRSHENoError) {
+ warning("Error loading savegame '%s'", filename);
+ delete in;
+ return;
+ }
+
+ g_engine->setTotalPlayTime(header.playTime * 1000);
+
+ _gameVars->loadState(in);
+
+ _gameState.sceneNum = _gameVars->getGlobalVar(V_CURRENT_SCENE);
+ _gameState.which = _gameVars->getGlobalVar(V_CURRENT_SCENE_WHICH);
+
+ _gameModule->requestRestoreGame();
+
+ delete in;
+
+}
+
+Common::Error NeverhoodEngine::loadGameState(int slot) {
+ const char *fileName = getSavegameFilename(slot);
+ loadgame(fileName);
+ return Common::kNoError;
+}
+
+Common::Error NeverhoodEngine::saveGameState(int slot, const Common::String &description) {
+ const char *fileName = getSavegameFilename(slot);
+ savegame(fileName, description.c_str());
+ return Common::kNoError;
+}
+
+Common::Error NeverhoodEngine::removeGameState(int slot) {
+ Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
+ Common::String filename = Neverhood::NeverhoodEngine::getSavegameFilename(_targetName, slot);
+ saveFileMan->removeSavefile(filename.c_str());
+ return Common::kNoError;
+}
+
+const char *NeverhoodEngine::getSavegameFilename(int num) {
+ static Common::String filename;
+ filename = getSavegameFilename(_targetName, num);
+ return filename.c_str();
+}
+
+Common::String NeverhoodEngine::getSavegameFilename(const Common::String &target, int num) {
+ assert(num >= 0 && num <= 999);
+ return Common::String::format("%s.%03d", target.c_str(), num);
+}
+
+} // End of namespace Neverhood
diff --git a/engines/neverhood/scene.cpp b/engines/neverhood/scene.cpp
new file mode 100644
index 0000000000..07d41754c9
--- /dev/null
+++ b/engines/neverhood/scene.cpp
@@ -0,0 +1,581 @@
+/* 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/scene.h"
+
+namespace Neverhood {
+
+Scene::Scene(NeverhoodEngine *vm, Module *parentModule)
+ : Entity(vm, 0), _parentModule(parentModule), _dataResource(vm), _hitRects(NULL),
+ _mouseCursorWasVisible(true) {
+
+ _isKlaymenBusy = false;
+ _doConvertMessages = false;
+ _messageList = NULL;
+ _rectType = 0;
+ _mouseClickPos.x = 0;
+ _mouseClickPos.y = 0;
+ _mouseClicked = false;
+ _rectList = NULL;
+ _klaymen = NULL;
+ _mouseCursor = NULL;
+ _palette = NULL;
+ _background = NULL;
+ clearHitRects();
+ clearCollisionSprites();
+ _vm->_screen->setFps(24);
+ _vm->_screen->setSmackerDecoder(NULL);
+ _canAcceptInput = true;
+ _messageList2 = NULL;
+ _smackerPlayer = NULL;
+ _isMessageListBusy = false;
+ _messageValue = -1;
+
+ SetUpdateHandler(&Scene::update);
+ SetMessageHandler(&Scene::handleMessage);
+
+ _vm->_screen->clearRenderQueue();
+}
+
+Scene::~Scene() {
+
+ if (_palette) {
+ removeEntity(_palette);
+ delete _palette;
+ }
+
+ // Delete all entities
+ for (Common::Array<Entity*>::iterator iter = _entities.begin(); iter != _entities.end(); iter++)
+ delete *iter;
+
+ // Don't delete surfaces since they always belong to an entity
+
+ // Purge the resources after each scene
+ _vm->_res->purgeResources();
+
+}
+
+void Scene::draw() {
+ if (_smackerPlayer) {
+ if (_smackerPlayer->getSurface())
+ _smackerPlayer->getSurface()->draw();
+ } else {
+ for (Common::Array<BaseSurface*>::iterator iter = _surfaces.begin(); iter != _surfaces.end(); iter++)
+ (*iter)->draw();
+ }
+}
+
+void Scene::addEntity(Entity *entity) {
+ int index = 0, insertIndex = -1;
+ for (Common::Array<Entity*>::iterator iter = _entities.begin(); iter != _entities.end(); iter++) {
+ if ((*iter)->getPriority() > entity->getPriority()) {
+ insertIndex = index;
+ break;
+ }
+ index++;
+ }
+ if (insertIndex >= 0)
+ _entities.insert_at(insertIndex, entity);
+ else
+ _entities.push_back(entity);
+}
+
+bool Scene::removeEntity(Entity *entity) {
+ for (uint index = 0; index < _entities.size(); index++)
+ if (_entities[index] == entity) {
+ _entities.remove_at(index);
+ return true;
+ }
+ return false;
+}
+
+void Scene::addSurface(BaseSurface *surface) {
+ if (surface) {
+ int index = 0, insertIndex = -1;
+ for (Common::Array<BaseSurface*>::iterator iter = _surfaces.begin(); iter != _surfaces.end(); iter++) {
+ if ((*iter)->getPriority() > surface->getPriority()) {
+ insertIndex = index;
+ break;
+ }
+ index++;
+ }
+ if (insertIndex >= 0)
+ _surfaces.insert_at(insertIndex, surface);
+ else
+ _surfaces.push_back(surface);
+ }
+}
+
+bool Scene::removeSurface(BaseSurface *surface) {
+ for (uint index = 0; index < _surfaces.size(); index++) {
+ if (_surfaces[index] == surface) {
+ _surfaces.remove_at(index);
+ return true;
+ }
+ }
+ return false;
+}
+
+Sprite *Scene::addSprite(Sprite *sprite) {
+ addEntity(sprite);
+ addSurface(sprite->getSurface());
+ return sprite;
+}
+
+void Scene::removeSprite(Sprite *sprite) {
+ removeSurface(sprite->getSurface());
+ removeEntity(sprite);
+}
+
+void Scene::setSurfacePriority(BaseSurface *surface, int priority) {
+ surface->setPriority(priority);
+ if (removeSurface(surface))
+ addSurface(surface);
+}
+
+void Scene::setSpriteSurfacePriority(Sprite *sprite, int priority) {
+ if (sprite)
+ setSurfacePriority(sprite->getSurface(), priority);
+}
+
+void Scene::deleteSprite(Sprite **sprite) {
+ removeCollisionSprite(*sprite);
+ removeSurface((*sprite)->getSurface());
+ removeEntity(*sprite);
+ delete *sprite;
+ *sprite = NULL;
+}
+
+Background *Scene::addBackground(Background *background) {
+ addEntity(background);
+ addSurface(background->getSurface());
+ return background;
+}
+
+void Scene::setBackground(uint32 fileHash) {
+ _background = addBackground(new Background(_vm, fileHash, 0, 0));
+}
+
+void Scene::changeBackground(uint32 fileHash) {
+ _background->load(fileHash);
+}
+
+void Scene::setPalette(uint32 fileHash) {
+ _palette = fileHash ? new Palette(_vm, fileHash) : new Palette(_vm);
+ _palette->usePalette();
+}
+
+void Scene::setHitRects(uint32 id) {
+ setHitRects(_vm->_staticData->getHitRectList(id));
+}
+
+void Scene::setHitRects(HitRectList *hitRects) {
+ _hitRects = hitRects;
+}
+
+Sprite *Scene::insertStaticSprite(uint32 fileHash, int surfacePriority) {
+ return addSprite(new StaticSprite(_vm, fileHash, surfacePriority));
+}
+
+void Scene::insertScreenMouse(uint32 fileHash, const NRect *mouseRect) {
+ NRect rect(-1, -1, -1, -1);
+ if (mouseRect)
+ rect = *mouseRect;
+ insertMouse(new Mouse(_vm, fileHash, rect));
+}
+
+void Scene::insertPuzzleMouse(uint32 fileHash, int16 x1, int16 x2) {
+ insertMouse(new Mouse(_vm, fileHash, x1, x2));
+}
+
+void Scene::insertNavigationMouse(uint32 fileHash, int type) {
+ insertMouse(new Mouse(_vm, fileHash, type));
+}
+
+void Scene::showMouse(bool visible) {
+ _mouseCursor->getSurface()->setVisible(visible);
+}
+
+void Scene::changeMouseCursor(uint32 fileHash) {
+ _mouseCursor->load(fileHash);
+ _mouseCursor->updateCursor();
+}
+
+SmackerPlayer *Scene::addSmackerPlayer(SmackerPlayer *smackerPlayer) {
+ addEntity(smackerPlayer);
+ addSurface(smackerPlayer->getSurface());
+ return smackerPlayer;
+}
+
+void Scene::update() {
+
+ if (_mouseClicked) {
+ if (_klaymen) {
+ if (_canAcceptInput &&
+ _klaymen->hasMessageHandler() &&
+ sendMessage(_klaymen, 0x1008, 0) != 0 &&
+ queryPositionSprite(_mouseClickPos.x, _mouseClickPos.y)) {
+ _mouseClicked = false;
+ } else if (_canAcceptInput &&
+ _klaymen->hasMessageHandler() &&
+ sendMessage(_klaymen, 0x1008, 0) != 0) {
+ _mouseClicked = !queryPositionRectList(_mouseClickPos.x, _mouseClickPos.y);
+ }
+ } else if (queryPositionSprite(_mouseClickPos.x, _mouseClickPos.y)) {
+ _mouseClicked = false;
+ }
+ }
+
+ processMessageList();
+
+ // Update all entities
+ for (Common::Array<Entity*>::iterator iter = _entities.begin(); iter != _entities.end(); iter++)
+ (*iter)->handleUpdate();
+
+}
+
+void Scene::leaveScene(uint32 result) {
+ sendMessage(_parentModule, 0x1009, result);
+}
+
+uint32 Scene::handleMessage(int messageNum, const MessageParam &param, Entity *sender) {
+ switch (messageNum) {
+ case 0x0000: // mouse moved
+ if (_mouseCursor && _mouseCursor->hasMessageHandler())
+ sendMessage(_mouseCursor, 0x4002, param);
+ break;
+ case 0x0001: // mouse clicked
+ _mouseClicked = true;
+ _mouseClickPos = param.asPoint();
+ break;
+ case 0x0006:
+ sendMessage(_parentModule, 0x1009, param);
+ break;
+ case 0x1006:
+ // Sent by Klaymen when its animation sequence has finished
+ if (_isKlaymenBusy) {
+ _isKlaymenBusy = false;
+ if (_messageListIndex == _messageListCount) {
+ // If the current message list was processed completely,
+ // sent Klaymen into the idle state.
+ sendMessage(_klaymen, 0x4004, 0);
+ } else {
+ // Else continue with the next message in the current message list
+ processMessageList();
+ }
+ }
+ break;
+ case 0x1007:
+ // This isn't sent by any code, check if it's in a message list
+ // This cancels the current message list and sets Klaymen into the idle state.
+ if (_isKlaymenBusy) {
+ _isKlaymenBusy = false;
+ _messageList = NULL;
+ sendMessage(_klaymen, 0x4004, 0);
+ }
+ break;
+ case 0x101D:
+ // Hide the mouse cursor
+ if (_mouseCursor) {
+ _mouseCursorWasVisible = _mouseCursor->getSurface()->getVisible();
+ _mouseCursor->getSurface()->setVisible(false);
+ }
+ break;
+ case 0x101E:
+ // Show the mouse cursor
+ if (_mouseCursorWasVisible && _mouseCursor) {
+ _mouseCursor->getSurface()->setVisible(true);
+ }
+ break;
+ case 0x1022:
+ // Set the sender's surface priority
+ setSurfacePriority(((Sprite*)sender)->getSurface(), param.asInteger());
+ break;
+ }
+ return 0;
+}
+
+bool Scene::queryPositionSprite(int16 mouseX, int16 mouseY) {
+ for (uint i = 0; i < _collisionSprites.size(); i++) {
+ Sprite *sprite = _collisionSprites[i];
+ if (sprite->hasMessageHandler() && sprite->isPointInside(mouseX, mouseY) &&
+ sendPointMessage(sprite, 0x1011, _mouseClickPos) != 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool Scene::queryPositionRectList(int16 mouseX, int16 mouseY) {
+ int16 klaymenX = _klaymen->getX();
+ int16 klaymenY = _klaymen->getY();
+ if (_rectType == 1) {
+ RectList &rectList = *_rectList;
+ for (uint i = 0; i < rectList.size(); i++) {
+ debug(2, "(%d, %d) ? (%d, %d, %d, %d)", klaymenX, klaymenY, rectList[i].rect.x1, rectList[i].rect.y1, rectList[i].rect.x2, rectList[i].rect.y2);
+ if (rectList[i].rect.contains(klaymenX, klaymenY)) {
+ for (uint j = 0; j < rectList[i].subRects.size(); j++) {
+ debug(2, " (%d, %d) ? (%d, %d, %d, %d)", mouseX, mouseY, rectList[i].subRects[j].rect.x1, rectList[i].subRects[j].rect.y1, rectList[i].subRects[j].rect.x2, rectList[i].subRects[j].rect.y2);
+ if (rectList[i].subRects[j].rect.contains(mouseX, mouseY)) {
+ return setMessageList2(rectList[i].subRects[j].messageListId);
+ }
+ }
+ }
+ }
+ } else if (_rectType == 2) {
+ MessageList *messageList = _dataResource.getMessageListAtPos(klaymenX, klaymenY, mouseX, mouseY);
+ if (messageList && messageList->size())
+ setMessageList2(messageList, true, true);
+ }
+ return true;
+}
+
+void Scene::setMessageList(uint32 id, bool canAcceptInput, bool doConvertMessages) {
+ setMessageList(_vm->_staticData->getMessageList(id), canAcceptInput, doConvertMessages);
+}
+
+void Scene::setMessageList(MessageList *messageList, bool canAcceptInput, bool doConvertMessages) {
+ _messageList = messageList;
+ _messageListCount = _messageList ? _messageList->size() : 0;
+ _messageListIndex = 0;
+ _isKlaymenBusy = false;
+ _canAcceptInput = canAcceptInput;
+ _doConvertMessages = doConvertMessages;
+ _messageListStatus = 1;
+ sendMessage(_klaymen, 0x101C, 0);
+}
+
+bool Scene::setMessageList2(uint32 id, bool canAcceptInput, bool doConvertMessages) {
+ return setMessageList2(_vm->_staticData->getMessageList(id), canAcceptInput, doConvertMessages);
+}
+
+bool Scene::setMessageList2(MessageList *messageList, bool canAcceptInput, bool doConvertMessages) {
+ if (_messageListStatus == 0 ||
+ (_messageListStatus == 1 && messageList != _messageList2) ||
+ (_messageListStatus == 2 && messageList == _messageList2)) {
+ // NOTE Skipped unneeded resource preloading code
+ _messageValue = -1;
+ _messageList2 = messageList;
+ setMessageList(messageList, canAcceptInput, doConvertMessages);
+ return true;
+ }
+ return false;
+}
+
+bool Scene::isMessageList2(uint32 id) {
+ return _messageList2 == _vm->_staticData->getMessageList(id);
+}
+
+void Scene::processMessageList() {
+ debug(7, "Scene::processMessageList() _isMessageListBusy = %d; _isKlaymenBusy = %d", _isMessageListBusy, _isKlaymenBusy);
+
+ if (_isMessageListBusy || _isKlaymenBusy)
+ return;
+
+ _isMessageListBusy = true;
+
+ if (!_messageList) {
+ _messageList2 = NULL;
+ _messageListStatus = 0;
+ }
+
+ if (_messageList && _klaymen) {
+
+#if 0
+ debug("MessageList: %p, %d", (void*)_messageList, _messageList->size());
+ for (uint i = 0; i < _messageList->size(); ++i) {
+ if (i == _messageListIndex) debugN("**"); else debugN(" ");
+ debug("(%08X, %08X)", (*_messageList)[i].messageNum, (*_messageList)[i].messageValue);
+ }
+ debug("--------------------------------");
+#endif
+
+ while (_messageList && _messageListIndex < _messageListCount && !_isKlaymenBusy) {
+ uint32 messageNum = (*_messageList)[_messageListIndex].messageNum;
+ uint32 messageParam = (*_messageList)[_messageListIndex].messageValue;
+
+ ++_messageListIndex;
+ if (_messageListIndex == _messageListCount)
+ sendMessage(_klaymen, 0x1021, 0);
+ if (_doConvertMessages)
+ messageNum = convertMessageNum(messageNum);
+ if (messageNum == 0x1009 || messageNum == 0x1024) {
+ sendMessage(_parentModule, messageNum, messageParam);
+ } else if (messageNum == 0x100A) {
+ _messageValue = messageParam;
+ sendMessage(_parentModule, messageNum, messageParam);
+ } else if (messageNum == 0x4001) {
+ _isKlaymenBusy = true;
+ sendPointMessage(_klaymen, 0x4001, _mouseClickPos);
+ } else if (messageNum == 0x100D) {
+ if (this->hasMessageHandler() && sendMessage(this, 0x100D, messageParam) != 0)
+ continue;
+ } else if (messageNum == 0x101A) {
+ _messageListStatus = 0;
+ } else if (messageNum == 0x101B) {
+ _messageListStatus = 2;
+ } else if (messageNum == 0x1020) {
+ _canAcceptInput = false;
+ } else if (messageNum >= 0x2000 && messageNum <= 0x2FFF) {
+ if (this->hasMessageHandler() && sendMessage(this, messageNum, messageParam) != 0) {
+ _isMessageListBusy = false;
+ return;
+ }
+ } else if (messageNum != 0x4003) {
+ _isKlaymenBusy = true;
+ if (_klaymen->hasMessageHandler() && sendMessage(_klaymen, messageNum, messageParam) != 0) {
+ _isKlaymenBusy = false;
+ }
+ }
+ if (_messageListIndex == _messageListCount) {
+ _canAcceptInput = true;
+ _messageList = NULL;
+ }
+ }
+ }
+
+ _isMessageListBusy = false;
+
+}
+
+void Scene::cancelMessageList() {
+ _isKlaymenBusy = false;
+ _messageList = NULL;
+ _canAcceptInput = true;
+ sendMessage(_klaymen, 0x4004, 0);
+}
+
+void Scene::setRectList(uint32 id) {
+ setRectList(_vm->_staticData->getRectList(id));
+}
+
+void Scene::setRectList(RectList *rectList) {
+ _rectList = rectList;
+ _rectType = 1;
+}
+
+void Scene::clearRectList() {
+ _rectList = NULL;
+ _rectType = 0;
+}
+
+void Scene::loadHitRectList() {
+ HitRectList *hitRectList = _dataResource.getHitRectList();
+ if (hitRectList) {
+ _hitRectList = *hitRectList;
+ setHitRects(&_hitRectList);
+ }
+}
+
+void Scene::loadDataResource(uint32 fileHash) {
+ _dataResource.load(fileHash);
+ _rectType = 2;
+ if (_klaymen)
+ _klaymen->loadDataResource(fileHash);
+}
+
+uint16 Scene::convertMessageNum(uint32 messageNum) {
+ switch (messageNum) {
+ case 0x00004004:
+ return 0x4001;
+ case 0x00000083:
+ return 0x100A;
+ case 0x044001C8:
+ return 0x481C;
+ case 0x02420480:
+ return 0x4818;
+ case 0x08004025:
+ return 0x100D;
+ case 0x04404281:
+ return 0x4824;
+ case 0x08400880:
+ return 0x4825;
+ case 0x08209081:
+ return 0x4823;
+ case 0x24000060:
+ return 0x1009;
+ case 0x42002200:
+ return 0x4004;
+ case 0x428D4894:
+ return 0x101A;
+ }
+ return 0x1000;
+}
+
+void Scene::clearHitRects() {
+ _hitRects = NULL;
+}
+
+HitRect *Scene::findHitRectAtPos(int16 x, int16 y) {
+ static HitRect kDefaultHitRect = {NRect(), 0x5000};
+ if (_hitRects)
+ for (HitRectList::iterator it = _hitRects->begin(); it != _hitRects->end(); it++)
+ if ((*it).rect.contains(x, y))
+ return &(*it);
+ return &kDefaultHitRect;
+}
+
+void Scene::addCollisionSprite(Sprite *sprite) {
+ int index = 0, insertIndex = -1;
+ for (Common::Array<Sprite*>::iterator iter = _collisionSprites.begin(); iter != _collisionSprites.end(); iter++) {
+ if ((*iter)->getPriority() > sprite->getPriority()) {
+ insertIndex = index;
+ break;
+ }
+ index++;
+ }
+ if (insertIndex >= 0)
+ _collisionSprites.insert_at(insertIndex, sprite);
+ else
+ _collisionSprites.push_back(sprite);
+}
+
+void Scene::removeCollisionSprite(Sprite *sprite) {
+ for (uint index = 0; index < _collisionSprites.size(); index++) {
+ if (_collisionSprites[index] == sprite) {
+ _collisionSprites.remove_at(index);
+ break;
+ }
+ }
+}
+
+void Scene::clearCollisionSprites() {
+ _collisionSprites.clear();
+}
+
+void Scene::checkCollision(Sprite *sprite, uint16 flags, int messageNum, uint32 messageParam) {
+ for (Common::Array<Sprite*>::iterator iter = _collisionSprites.begin(); iter != _collisionSprites.end(); iter++) {
+ Sprite *collSprite = *iter;
+ if ((sprite->getFlags() & flags) && collSprite->checkCollision(sprite->getCollisionBounds())) {
+ sprite->sendMessage(collSprite, messageNum, messageParam);
+ }
+ }
+}
+
+void Scene::insertMouse(Mouse *mouseCursor) {
+ if (_mouseCursor)
+ deleteSprite((Sprite**)&_mouseCursor);
+ _mouseCursor = mouseCursor;
+ addEntity(_mouseCursor);
+}
+
+} // End of namespace Neverhood
diff --git a/engines/neverhood/scene.h b/engines/neverhood/scene.h
new file mode 100644
index 0000000000..1abcbfb964
--- /dev/null
+++ b/engines/neverhood/scene.h
@@ -0,0 +1,226 @@
+/* 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_SCENE_H
+#define NEVERHOOD_SCENE_H
+
+#include "common/array.h"
+#include "neverhood/neverhood.h"
+#include "neverhood/background.h"
+#include "neverhood/entity.h"
+#include "neverhood/graphics.h"
+#include "neverhood/klaymen.h"
+#include "neverhood/module.h"
+#include "neverhood/palette.h"
+#include "neverhood/smackerplayer.h"
+#include "neverhood/sprite.h"
+#include "neverhood/staticdata.h"
+
+namespace Neverhood {
+
+class Scene : public Entity {
+public:
+ Scene(NeverhoodEngine *vm, Module *parentModule);
+ virtual ~Scene();
+ virtual void draw();
+ void addEntity(Entity *entity);
+ bool removeEntity(Entity *entity);
+ void addSurface(BaseSurface *surface);
+ bool removeSurface(BaseSurface *surface);
+ Sprite *addSprite(Sprite *sprite);
+ void removeSprite(Sprite *sprite);
+ void setSurfacePriority(BaseSurface *surface, int priority);
+ void setSpriteSurfacePriority(Sprite *sprite, int priority);
+ void deleteSprite(Sprite **sprite);
+ Background *addBackground(Background *background);
+ void setBackground(uint32 fileHash);
+ void changeBackground(uint32 fileHash);
+ void setBackgroundY(int16 y) { _background->getSurface()->getDrawRect().y = y; }
+ int16 getBackgroundY() { return _background->getSurface()->getDrawRect().y; }
+ void setPalette(uint32 fileHash = 0);
+ void setHitRects(uint32 id);
+ Sprite *insertStaticSprite(uint32 fileHash, int surfacePriority);
+ void insertScreenMouse(uint32 fileHash, const NRect *mouseRect = NULL);
+ void insertPuzzleMouse(uint32 fileHash, int16 x1, int16 x2);
+ void insertNavigationMouse(uint32 fileHash, int type);
+ void showMouse(bool visible);
+ void changeMouseCursor(uint32 fileHash);
+ SmackerPlayer *addSmackerPlayer(SmackerPlayer *smackerPlayer);
+ void update();
+ void leaveScene(uint32 result);
+ HitRect *findHitRectAtPos(int16 x, int16 y);
+ void addCollisionSprite(Sprite *sprite);
+ void removeCollisionSprite(Sprite *sprite);
+ void checkCollision(Sprite *sprite, uint16 flags, int messageNum, uint32 messageParam);
+ // Some crazy templated functions to make the logic code smaller/simpler (imo!)
+ // insertKlaymen
+ template<class T>
+ void insertKlaymen() {
+ _klaymen = (T*)addSprite(new T(_vm, this));
+ }
+ template<class T, class Arg1>
+ void insertKlaymen(Arg1 arg1) {
+ _klaymen = (T*)addSprite(new T(_vm, this, arg1));
+ }
+ template<class T, class Arg1, class Arg2>
+ void insertKlaymen(Arg1 arg1, Arg2 arg2) {
+ _klaymen = (T*)addSprite(new T(_vm, this, arg1, arg2));
+ }
+ template<class T, class Arg1, class Arg2, class Arg3>
+ void insertKlaymen(Arg1 arg1, Arg2 arg2, Arg3 arg3) {
+ _klaymen = (T*)addSprite(new T(_vm, this, arg1, arg2, arg3));
+ }
+ template<class T, class Arg1, class Arg2, class Arg3, class Arg4>
+ void insertKlaymen(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) {
+ _klaymen = (T*)addSprite(new T(_vm, this, arg1, arg2, arg3, arg4));
+ }
+ template<class T, class Arg1, class Arg2, class Arg3, class Arg4, class Arg5>
+ void insertKlaymen(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) {
+ _klaymen = (T*)addSprite(new T(_vm, this, arg1, arg2, arg3, arg4, arg5));
+ }
+ template<class T, class Arg1, class Arg2, class Arg3, class Arg4, class Arg5, class Arg6>
+ void insertKlaymen(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5, Arg6 arg6) {
+ _klaymen = (T*)addSprite(new T(_vm, this, arg1, arg2, arg3, arg4, arg5, arg6));
+ }
+ // insertSprite
+ template<class T>
+ T* insertSprite() {
+ return (T*)addSprite(new T(_vm));
+ }
+ template<class T, class Arg1>
+ T* insertSprite(Arg1 arg1) {
+ return (T*)addSprite(new T(_vm, arg1));
+ }
+ template<class T, class Arg1, class Arg2>
+ T* insertSprite(Arg1 arg1, Arg2 arg2) {
+ return (T*)addSprite(new T(_vm, arg1, arg2));
+ }
+ template<class T, class Arg1, class Arg2, class Arg3>
+ T* insertSprite(Arg1 arg1, Arg2 arg2, Arg3 arg3) {
+ return (T*)addSprite(new T(_vm, arg1, arg2, arg3));
+ }
+ template<class T, class Arg1, class Arg2, class Arg3, class Arg4>
+ T* insertSprite(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) {
+ return (T*)addSprite(new T(_vm, arg1, arg2, arg3, arg4));
+ }
+ template<class T, class Arg1, class Arg2, class Arg3, class Arg4, class Arg5>
+ T* insertSprite(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) {
+ return (T*)addSprite(new T(_vm, arg1, arg2, arg3, arg4, arg5));
+ }
+ template<class T, class Arg1, class Arg2, class Arg3, class Arg4, class Arg5, class Arg6>
+ T* insertSprite(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5, Arg6 arg6) {
+ return (T*)addSprite(new T(_vm, arg1, arg2, arg3, arg4, arg5, arg6));
+ }
+ // createSprite
+ template<class T>
+ T* createSprite() {
+ return new T(_vm);
+ }
+ template<class T, class Arg1>
+ T* createSprite(Arg1 arg1) {
+ return new T(_vm, arg1);
+ }
+ template<class T, class Arg1, class Arg2>
+ T* createSprite(Arg1 arg1, Arg2 arg2) {
+ return new T(_vm, arg1, arg2);
+ }
+ template<class T, class Arg1, class Arg2, class Arg3>
+ T* createSprite(Arg1 arg1, Arg2 arg2, Arg3 arg3) {
+ return new T(_vm, arg1, arg2, arg3);
+ }
+ template<class T, class Arg1, class Arg2, class Arg3, class Arg4>
+ T* createSprite(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) {
+ return new T(_vm, arg1, arg2, arg3, arg4);
+ }
+ template<class T, class Arg1, class Arg2, class Arg3, class Arg4, class Arg5>
+ T* createSprite(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) {
+ return new T(_vm, arg1, arg2, arg3, arg4, arg5);
+ }
+ template<class T, class Arg1, class Arg2, class Arg3, class Arg4, class Arg5, class Arg6>
+ T* createSprite(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5, Arg6 arg6) {
+ return new T(_vm, arg1, arg2, arg3, arg4, arg5, arg6);
+ }
+protected:
+ Module *_parentModule;
+ Common::Array<Entity*> _entities;
+ Common::Array<BaseSurface*> _surfaces;
+
+ Klaymen *_klaymen;
+ Background *_background;
+ Palette *_palette;
+ SmackerPlayer *_smackerPlayer;
+
+ MessageList *_messageList;
+ MessageList *_messageList2;
+ int _messageListStatus;
+ uint _messageListCount;
+ uint _messageListIndex;
+ bool _doConvertMessages;
+
+ bool _canAcceptInput;
+ bool _isKlaymenBusy;
+ bool _isMessageListBusy;
+
+ Mouse *_mouseCursor;
+ NPoint _mouseClickPos;
+ bool _mouseClicked;
+ bool _mouseCursorWasVisible;
+
+ int _rectType;
+ RectList *_rectList;
+ DataResource _dataResource;
+
+ HitRectList _hitRectList;
+
+ HitRectList *_hitRects;
+ Common::Array<Sprite*> _collisionSprites;
+
+ void (Entity::*_savedUpdateHandlerCb)();
+ uint32 (Entity::*_savedMessageHandlerCb)(int messageNum, const MessageParam &param, Entity *sender);
+ int _messageValue;
+ uint32 handleMessage(int messageNum, const MessageParam &param, Entity *sender);
+ bool queryPositionSprite(int16 mouseX, int16 mouseY);
+ bool queryPositionRectList(int16 mouseX, int16 mouseY);
+ void setMessageList(uint32 id, bool canAcceptInput = true, bool doConvertMessages = false);
+ void setMessageList(MessageList *messageList, bool canAcceptInput = true, bool doConvertMessages = false);
+ bool setMessageList2(uint32 id, bool canAcceptInput = true, bool doConvertMessages = false);
+ bool setMessageList2(MessageList *messageList, bool canAcceptInput = true, bool doConvertMessages = false);
+ bool isMessageList2(uint32 id);
+ void processMessageList();
+ void setRectList(uint32 id);
+ void setRectList(RectList *rectList);
+ void clearRectList();
+ void loadHitRectList();
+ void cancelMessageList();
+ void loadDataResource(uint32 fileHash);
+ uint16 convertMessageNum(uint32 messageNum);
+
+ void setHitRects(HitRectList *hitRects);
+ void clearHitRects();
+ void clearCollisionSprites();
+
+ void insertMouse(Mouse *mouseCursor);
+};
+
+} // End of namespace Neverhood
+
+#endif /* NEVERHOOD_SCENE_H */
diff --git a/engines/neverhood/screen.cpp b/engines/neverhood/screen.cpp
new file mode 100644
index 0000000000..5a748cfab4
--- /dev/null
+++ b/engines/neverhood/screen.cpp
@@ -0,0 +1,420 @@
+/* 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 "graphics/palette.h"
+#include "neverhood/screen.h"
+
+namespace Neverhood {
+
+Screen::Screen(NeverhoodEngine *vm)
+ : _vm(vm), _paletteData(NULL), _paletteChanged(false), _smackerDecoder(NULL),
+ _yOffset(0), _fullRefresh(false) {
+
+ _ticks = _vm->_system->getMillis();
+
+ _backScreen = new Graphics::Surface();
+ _backScreen->create(640, 480, Graphics::PixelFormat::createFormatCLUT8());
+
+ _renderQueue = new RenderQueue();
+ _prevRenderQueue = new RenderQueue();
+ _microTiles = new MicroTileArray(640, 480);
+
+}
+
+Screen::~Screen() {
+ delete _microTiles;
+ delete _renderQueue;
+ delete _prevRenderQueue;
+ _backScreen->free();
+ delete _backScreen;
+}
+
+void Screen::update() {
+ _ticks = _vm->_system->getMillis();
+ updatePalette();
+
+ if (_fullRefresh) {
+ // NOTE When playing a fullscreen/doubled Smacker video usually a full screen refresh is needed
+ _vm->_system->copyRectToScreen((const byte*)_backScreen->pixels, _backScreen->pitch, 0, 0, 640, 480);
+ _fullRefresh = false;
+ return;
+ }
+
+ _microTiles->clear();
+
+ for (RenderQueue::iterator it = _renderQueue->begin(); it != _renderQueue->end(); ++it) {
+ RenderItem &renderItem = (*it);
+ renderItem._refresh = true;
+ for (RenderQueue::iterator jt = _prevRenderQueue->begin(); jt != _prevRenderQueue->end(); ++jt) {
+ RenderItem &prevRenderItem = (*jt);
+ if (prevRenderItem == renderItem) {
+ prevRenderItem._refresh = false;
+ renderItem._refresh = false;
+ }
+ }
+ }
+
+ for (RenderQueue::iterator jt = _prevRenderQueue->begin(); jt != _prevRenderQueue->end(); ++jt) {
+ RenderItem &prevRenderItem = (*jt);
+ if (prevRenderItem._refresh)
+ _microTiles->addRect(Common::Rect(prevRenderItem._destX, prevRenderItem._destY, prevRenderItem._destX + prevRenderItem._width, prevRenderItem._destY + prevRenderItem._height));
+ }
+
+ for (RenderQueue::iterator it = _renderQueue->begin(); it != _renderQueue->end(); ++it) {
+ RenderItem &renderItem = (*it);
+ if (renderItem._refresh)
+ _microTiles->addRect(Common::Rect(renderItem._destX, renderItem._destY, renderItem._destX + renderItem._width, renderItem._destY + renderItem._height));
+ renderItem._refresh = true;
+ }
+
+ RectangleList *updateRects = _microTiles->getRectangles();
+
+ for (RenderQueue::iterator it = _renderQueue->begin(); it != _renderQueue->end(); ++it) {
+ RenderItem &renderItem = (*it);
+ for (RectangleList::iterator ri = updateRects->begin(); ri != updateRects->end(); ++ri)
+ blitRenderItem(renderItem, *ri);
+ }
+
+ SWAP(_renderQueue, _prevRenderQueue);
+ _renderQueue->clear();
+
+ for (RectangleList::iterator ri = updateRects->begin(); ri != updateRects->end(); ++ri) {
+ Common::Rect &r = *ri;
+ _vm->_system->copyRectToScreen((const byte*)_backScreen->getBasePtr(r.left, r.top), _backScreen->pitch, r.left, r.top, r.width(), r.height());
+ }
+
+ delete updateRects;
+
+}
+
+uint32 Screen::getNextFrameTime() {
+ int32 frameDelay = _frameDelay;
+ if (_smackerDecoder && _smackerDecoder->isVideoLoaded() && !_smackerDecoder->endOfVideo())
+ frameDelay = _smackerDecoder->getTimeToNextFrame();
+ int32 waitTicks = frameDelay - (_vm->_system->getMillis() - _ticks);
+ return _vm->_system->getMillis() + waitTicks;
+}
+
+void Screen::saveParams() {
+ _savedSmackerDecoder = _smackerDecoder;
+ _savedFrameDelay = _frameDelay;
+ _savedYOffset = _yOffset;
+}
+
+void Screen::restoreParams() {
+ _smackerDecoder = _savedSmackerDecoder;
+ _frameDelay = _savedFrameDelay;
+ _yOffset = _savedYOffset;
+}
+
+void Screen::setFps(int fps) {
+ _frameDelay = 1000 / fps;
+}
+
+int Screen::getFps() {
+ return 1000 / _frameDelay;
+}
+
+void Screen::setYOffset(int16 yOffset) {
+ _yOffset = yOffset;
+}
+
+int16 Screen::getYOffset() {
+ return _yOffset;
+}
+
+void Screen::setPaletteData(byte *paletteData) {
+ _paletteChanged = true;
+ _paletteData = paletteData;
+}
+
+void Screen::unsetPaletteData(byte *paletteData) {
+ if (_paletteData == paletteData) {
+ _paletteChanged = false;
+ _paletteData = NULL;
+ }
+}
+
+void Screen::testPalette(byte *paletteData) {
+ if (_paletteData == paletteData)
+ _paletteChanged = true;
+}
+
+void Screen::updatePalette() {
+ if (_paletteChanged && _paletteData) {
+ byte *tempPalette = new byte[768];
+ for (int i = 0; i < 256; i++) {
+ tempPalette[i * 3 + 0] = _paletteData[i * 4 + 0];
+ tempPalette[i * 3 + 1] = _paletteData[i * 4 + 1];
+ tempPalette[i * 3 + 2] = _paletteData[i * 4 + 2];
+ }
+ _vm->_system->getPaletteManager()->setPalette(tempPalette, 0, 256);
+ delete[] tempPalette;
+ _paletteChanged = false;
+ }
+}
+
+void Screen::clear() {
+ memset(_backScreen->pixels, 0, _backScreen->pitch * _backScreen->h);
+ _fullRefresh = true;
+ clearRenderQueue();
+}
+
+void Screen::clearRenderQueue() {
+ _renderQueue->clear();
+ _prevRenderQueue->clear();
+}
+
+void Screen::drawSurface2(const Graphics::Surface *surface, NDrawRect &drawRect, NRect &clipRect, bool transparent, byte version,
+ const Graphics::Surface *shadowSurface) {
+
+ int16 destX, destY;
+ NRect ddRect;
+
+ if (drawRect.x + drawRect.width >= clipRect.x2)
+ ddRect.x2 = clipRect.x2 - drawRect.x;
+ else
+ ddRect.x2 = drawRect.width;
+
+ if (drawRect.x < clipRect.x1) {
+ destX = clipRect.x1;
+ ddRect.x1 = clipRect.x1 - drawRect.x;
+ } else {
+ destX = drawRect.x;
+ ddRect.x1 = 0;
+ }
+
+ if (drawRect.y + drawRect.height >= clipRect.y2)
+ ddRect.y2 = clipRect.y2 - drawRect.y;
+ else
+ ddRect.y2 = drawRect.height;
+
+ if (drawRect.y < clipRect.y1) {
+ destY = clipRect.y1;
+ ddRect.y1 = clipRect.y1 - drawRect.y;
+ } else {
+ destY = drawRect.y;
+ ddRect.y1 = 0;
+ }
+
+ queueBlit(surface, destX, destY, ddRect, transparent, version, shadowSurface);
+
+}
+
+void Screen::drawSurface3(const Graphics::Surface *surface, int16 x, int16 y, NDrawRect &drawRect, NRect &clipRect, bool transparent, byte version) {
+
+ int16 destX, destY;
+ NRect ddRect;
+
+ if (x + drawRect.width >= clipRect.x2)
+ ddRect.x2 = clipRect.x2 - drawRect.x - x;
+ else
+ ddRect.x2 = drawRect.x + drawRect.width;
+
+ if (x < clipRect.x1) {
+ destX = clipRect.x1;
+ ddRect.x1 = clipRect.x1 + drawRect.x - x;
+ } else {
+ destX = x;
+ ddRect.x1 = drawRect.x;
+ }
+
+ if (y + drawRect.height >= clipRect.y2)
+ ddRect.y2 = clipRect.y2 + drawRect.y - y;
+ else
+ ddRect.y2 = drawRect.y + drawRect.height;
+
+ if (y < clipRect.y1) {
+ destY = clipRect.y1;
+ ddRect.y1 = clipRect.y1 + drawRect.y - y;
+ } else {
+ destY = y;
+ ddRect.y1 = drawRect.y;
+ }
+
+ queueBlit(surface, destX, destY, ddRect, transparent, version);
+
+}
+
+void Screen::drawDoubleSurface2(const Graphics::Surface *surface, NDrawRect &drawRect) {
+
+ const byte *source = (const byte*)surface->getBasePtr(0, 0);
+ byte *dest = (byte*)_backScreen->getBasePtr(drawRect.x, drawRect.y);
+
+ for (int16 yc = 0; yc < surface->h; yc++) {
+ byte *row = dest;
+ for (int16 xc = 0; xc < surface->w; xc++) {
+ *row++ = *source;
+ *row++ = *source++;
+ }
+ memcpy(dest + _backScreen->pitch, dest, surface->w * 2);
+ dest += _backScreen->pitch;
+ dest += _backScreen->pitch;
+ }
+
+ _fullRefresh = true; // See Screen::update
+
+}
+
+void Screen::drawUnk(const Graphics::Surface *surface, NDrawRect &drawRect, NDrawRect &sysRect, NRect &clipRect, bool transparent, byte version) {
+
+ int16 x, y;
+ bool xflag, yflag;
+ NDrawRect newDrawRect;
+
+ x = sysRect.x;
+ if (sysRect.width <= x || -sysRect.width >= x)
+ x = x % sysRect.width;
+ if (x < 0)
+ x += sysRect.width;
+
+ y = sysRect.y;
+ if (y >= sysRect.height || -sysRect.height >= y)
+ y = y % sysRect.height;
+ if (y < 0)
+ y += sysRect.height;
+
+ xflag = x <= 0;
+ yflag = y <= 0;
+
+ newDrawRect.x = x;
+ newDrawRect.width = sysRect.width - x;
+ if (drawRect.width < newDrawRect.width) {
+ xflag = true;
+ newDrawRect.width = drawRect.width;
+ }
+
+ newDrawRect.y = y;
+ newDrawRect.height = sysRect.height - y;
+ if (drawRect.height < newDrawRect.height) {
+ yflag = true;
+ newDrawRect.height = drawRect.height;
+ }
+
+ drawSurface3(surface, drawRect.x, drawRect.y, newDrawRect, clipRect, transparent, version);
+
+ if (!xflag) {
+ newDrawRect.x = 0;
+ newDrawRect.y = y;
+ newDrawRect.width = x + drawRect.width - sysRect.width;
+ newDrawRect.height = sysRect.height - y;
+ if (drawRect.height < newDrawRect.height)
+ newDrawRect.height = drawRect.height;
+ drawSurface3(surface, sysRect.width + drawRect.x - x, drawRect.y, newDrawRect, clipRect, transparent, version);
+ }
+
+ if (!yflag) {
+ newDrawRect.x = x;
+ newDrawRect.y = 0;
+ newDrawRect.width = sysRect.width - x;
+ newDrawRect.height = y + drawRect.height - sysRect.height;
+ if (drawRect.width < newDrawRect.width)
+ newDrawRect.width = drawRect.width;
+ drawSurface3(surface, drawRect.x, sysRect.height + drawRect.y - y, newDrawRect, clipRect, transparent, version);
+ }
+
+ if (!xflag && !yflag) {
+ newDrawRect.x = 0;
+ newDrawRect.y = 0;
+ newDrawRect.width = x + drawRect.width - sysRect.width;
+ newDrawRect.height = y + drawRect.height - sysRect.height;
+ drawSurface3(surface, sysRect.width + drawRect.x - x, sysRect.height + drawRect.y - y, newDrawRect, clipRect, transparent, version);
+ }
+
+}
+
+void Screen::drawSurfaceClipRects(const Graphics::Surface *surface, NDrawRect &drawRect, NRect *clipRects, uint clipRectsCount, bool transparent, byte version) {
+ NDrawRect clipDrawRect(0, 0, drawRect.width, drawRect.height);
+ for (uint i = 0; i < clipRectsCount; i++)
+ drawSurface3(surface, drawRect.x, drawRect.y, clipDrawRect, clipRects[i], transparent, version);
+}
+
+void Screen::queueBlit(const Graphics::Surface *surface, int16 destX, int16 destY, NRect &ddRect, bool transparent, byte version,
+ const Graphics::Surface *shadowSurface) {
+
+ const int width = ddRect.x2 - ddRect.x1;
+ const int height = ddRect.y2 - ddRect.y1;
+
+ if (width <= 0 || height <= 0)
+ return;
+
+ RenderItem renderItem;
+ renderItem._surface = surface;
+ renderItem._shadowSurface = shadowSurface;
+ renderItem._destX = destX;
+ renderItem._destY = destY;
+ renderItem._srcX = ddRect.x1;
+ renderItem._srcY = ddRect.y1;
+ renderItem._width = width;
+ renderItem._height = height;
+ renderItem._transparent = transparent;
+ renderItem._version = version;
+ _renderQueue->push_back(renderItem);
+
+}
+
+void Screen::blitRenderItem(const RenderItem &renderItem, const Common::Rect &clipRect) {
+
+ const Graphics::Surface *surface = renderItem._surface;
+ const Graphics::Surface *shadowSurface = renderItem._shadowSurface;
+ const int16 x0 = MAX<int16>(clipRect.left, renderItem._destX);
+ const int16 y0 = MAX<int16>(clipRect.top, renderItem._destY);
+ const int16 x1 = MIN<int16>(clipRect.right, renderItem._destX + renderItem._width);
+ const int16 y1 = MIN<int16>(clipRect.bottom, renderItem._destY + renderItem._height);
+ const int16 width = x1 - x0;
+ int16 height = y1 - y0;
+
+ if (width < 0 || height < 0)
+ return;
+
+ const byte *source = (const byte*)surface->getBasePtr(renderItem._srcX + x0 - renderItem._destX, renderItem._srcY + y0 - renderItem._destY);
+ byte *dest = (byte*)_backScreen->getBasePtr(x0, y0);
+
+ if (shadowSurface) {
+ const byte *shadowSource = (const byte*)shadowSurface->getBasePtr(x0, y0);
+ while (height--) {
+ for (int xc = 0; xc < width; xc++)
+ if (source[xc] != 0)
+ dest[xc] = shadowSource[xc];
+ source += surface->pitch;
+ shadowSource += shadowSurface->pitch;
+ dest += _backScreen->pitch;
+ }
+ } else if (!renderItem._transparent) {
+ while (height--) {
+ memcpy(dest, source, width);
+ source += surface->pitch;
+ dest += _backScreen->pitch;
+ }
+ } else {
+ while (height--) {
+ for (int xc = 0; xc < width; xc++)
+ if (source[xc] != 0)
+ dest[xc] = source[xc];
+ source += surface->pitch;
+ dest += _backScreen->pitch;
+ }
+ }
+
+}
+
+} // End of namespace Neverhood
diff --git a/engines/neverhood/screen.h b/engines/neverhood/screen.h
new file mode 100644
index 0000000000..c778066152
--- /dev/null
+++ b/engines/neverhood/screen.h
@@ -0,0 +1,106 @@
+/* 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_SCREEN_H
+#define NEVERHOOD_SCREEN_H
+
+#include "common/array.h"
+#include "graphics/surface.h"
+#include "video/smk_decoder.h"
+#include "neverhood/neverhood.h"
+#include "neverhood/microtiles.h"
+#include "neverhood/graphics.h"
+
+namespace Neverhood {
+
+struct RenderItem {
+ const Graphics::Surface *_surface;
+ const Graphics::Surface *_shadowSurface;
+ int16 _destX, _destY;
+ int16 _srcX, _srcY, _width, _height;
+ bool _transparent;
+ byte _version;
+ bool _refresh;
+ bool operator==(const RenderItem &second) const {
+ return
+ _surface == second._surface &&
+ _shadowSurface == second._shadowSurface &&
+ _destX == second._destX &&
+ _destY == second._destY &&
+ _srcX == second._srcX &&
+ _srcY == second._srcY &&
+ _width == second._width &&
+ _height == second._height &&
+ _transparent == second._transparent &&
+ _version == second._version;
+ }
+};
+
+typedef Common::Array<RenderItem> RenderQueue;
+
+class Screen {
+public:
+ Screen(NeverhoodEngine *vm);
+ ~Screen();
+ void update();
+ uint32 getNextFrameTime();
+ void saveParams();
+ void restoreParams();
+ void setFps(int fps);
+ int getFps();
+ void setYOffset(int16 yOffset);
+ int16 getYOffset();
+ void setPaletteData(byte *paletteData);
+ void unsetPaletteData(byte *paletteData);
+ byte *getPaletteData() { return _paletteData; }
+ void testPalette(byte *paletteData);
+ void updatePalette();
+ void clear();
+ void clearRenderQueue();
+ void drawSurface2(const Graphics::Surface *surface, NDrawRect &drawRect, NRect &clipRect, bool transparent, byte version,
+ const Graphics::Surface *shadowSurface = NULL);
+ void drawSurface3(const Graphics::Surface *surface, int16 x, int16 y, NDrawRect &drawRect, NRect &clipRect, bool transparent, byte version);
+ void drawShadowSurface(const Graphics::Surface *surface, const Graphics::Surface *shadowSurface, int16 x, int16 y, NDrawRect &drawRect, NRect &clipRect);
+ void drawDoubleSurface2(const Graphics::Surface *surface, NDrawRect &drawRect);
+ void drawUnk(const Graphics::Surface *surface, NDrawRect &drawRect, NDrawRect &sysRect, NRect &clipRect, bool transparent, byte version);
+ void drawSurfaceClipRects(const Graphics::Surface *surface, NDrawRect &drawRect, NRect *clipRects, uint clipRectsCount, bool transparent, byte version);
+ void setSmackerDecoder(Video::SmackerDecoder *smackerDecoder) { _smackerDecoder = smackerDecoder; }
+ void queueBlit(const Graphics::Surface *surface, int16 destX, int16 destY, NRect &ddRect, bool transparent, byte version,
+ const Graphics::Surface *shadowSurface = NULL);
+ void blitRenderItem(const RenderItem &renderItem, const Common::Rect &clipRect);
+protected:
+ NeverhoodEngine *_vm;
+ MicroTileArray *_microTiles;
+ Graphics::Surface *_backScreen;
+ Video::SmackerDecoder *_smackerDecoder, *_savedSmackerDecoder;
+ int32 _ticks;
+ int32 _frameDelay, _savedFrameDelay;
+ byte *_paletteData;
+ bool _paletteChanged;
+ int16 _yOffset, _savedYOffset;
+ bool _fullRefresh;
+ RenderQueue *_renderQueue, *_prevRenderQueue;
+};
+
+} // End of namespace Neverhood
+
+#endif /* NEVERHOOD_SCREEN_H */
diff --git a/engines/neverhood/smackerplayer.cpp b/engines/neverhood/smackerplayer.cpp
new file mode 100644
index 0000000000..b67c8db9fc
--- /dev/null
+++ b/engines/neverhood/smackerplayer.cpp
@@ -0,0 +1,237 @@
+/* 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 "graphics/palette.h"
+#include "neverhood/smackerplayer.h"
+#include "neverhood/palette.h"
+#include "neverhood/resourceman.h"
+#include "neverhood/scene.h"
+
+namespace Neverhood {
+
+// SmackerSurface
+
+SmackerSurface::SmackerSurface(NeverhoodEngine *vm)
+ : BaseSurface(vm, 0, 0, 0), _smackerFrame(NULL) {
+}
+
+void SmackerSurface::draw() {
+ if (_smackerFrame && _visible && _drawRect.width > 0 && _drawRect.height > 0)
+ _vm->_screen->drawSurface2(_smackerFrame, _drawRect, _clipRect, false, ++_version);
+}
+
+void SmackerSurface::setSmackerFrame(const Graphics::Surface *smackerFrame) {
+ _drawRect.x = 0;
+ _drawRect.y = 0;
+ _drawRect.width = smackerFrame->w;
+ _drawRect.height = smackerFrame->h;
+ _sysRect.x = 0;
+ _sysRect.y = 0;
+ _sysRect.width = (smackerFrame->w + 3) & 0xFFFC; // align by 4 bytes
+ _sysRect.height = smackerFrame->h;
+ _smackerFrame = smackerFrame;
+}
+
+// SmackerDoubleSurface
+
+SmackerDoubleSurface::SmackerDoubleSurface(NeverhoodEngine *vm)
+ : SmackerSurface(vm) {
+}
+
+void SmackerDoubleSurface::draw() {
+ if (_smackerFrame && _visible && _drawRect.width > 0 && _drawRect.height > 0)
+ _vm->_screen->drawDoubleSurface2(_smackerFrame, _drawRect);
+}
+
+void NeverhoodSmackerDecoder::forceSeekToFrame(uint frame) {
+ if (!isVideoLoaded())
+ return;
+
+ if (frame >= getFrameCount())
+ error("Can't force Smacker seek to invalid frame %d", frame);
+
+ if (_header.audioInfo[0].hasAudio)
+ error("Can't force Smacker frame seek with audio");
+ if (!rewind())
+ error("Failed to rewind");
+
+ SmackerVideoTrack *videoTrack = (SmackerVideoTrack *)getTrack(0);
+ uint32 offset = 0;
+ for (uint32 i = 0; i < frame; i++) {
+ videoTrack->increaseCurFrame();
+ offset += _frameSizes[i] & ~3;
+ }
+
+ _fileStream->seek(offset, SEEK_CUR);
+}
+
+// SmackerPlayer
+
+SmackerPlayer::SmackerPlayer(NeverhoodEngine *vm, Scene *scene, uint32 fileHash, bool doubleSurface, bool flag, bool paused)
+ : Entity(vm, 0), _scene(scene), _doubleSurface(doubleSurface), _videoDone(false), _paused(paused),
+ _palette(NULL), _smackerDecoder(NULL), _smackerSurface(NULL), _stream(NULL), _smackerFirst(true),
+ _drawX(-1), _drawY(-1) {
+
+ SetUpdateHandler(&SmackerPlayer::update);
+ open(fileHash, flag);
+}
+
+SmackerPlayer::~SmackerPlayer() {
+ close();
+}
+
+void SmackerPlayer::open(uint32 fileHash, bool keepLastFrame) {
+ debug(0, "SmackerPlayer::open(%08X)", fileHash);
+
+ _fileHash = fileHash;
+ _keepLastFrame = keepLastFrame;
+
+ close();
+
+ if (_doubleSurface) {
+ _smackerSurface = new SmackerDoubleSurface(_vm);
+ } else {
+ _smackerSurface = new SmackerSurface(_vm);
+ }
+
+ _smackerFirst = true;
+
+ _stream = _vm->_res->createStream(fileHash);
+
+ _smackerDecoder = new NeverhoodSmackerDecoder();
+ _smackerDecoder->loadStream(_stream);
+
+ _palette = new Palette(_vm);
+ _palette->usePalette();
+
+ if (!_paused)
+ _smackerDecoder->start();
+
+}
+
+void SmackerPlayer::close() {
+ if (_smackerDecoder)
+ _smackerDecoder->stop();
+ delete _smackerDecoder;
+ delete _palette;
+ // NOTE The SmackerDecoder deletes the _stream
+ delete _smackerSurface;
+ _smackerDecoder = NULL;
+ _palette = NULL;
+ _stream = NULL;
+ _smackerSurface = NULL;
+}
+
+void SmackerPlayer::gotoFrame(int frameNumber) {
+ if (_smackerDecoder) {
+ _smackerDecoder->forceSeekToFrame(frameNumber);
+ _smackerDecoder->decodeNextFrame();
+ }
+}
+
+uint32 SmackerPlayer::getFrameCount() {
+ return _smackerDecoder ? _smackerDecoder->getFrameCount() : 0;
+}
+
+uint32 SmackerPlayer::getFrameNumber() {
+ return _smackerDecoder ? _smackerDecoder->getCurFrame() : 0;
+}
+
+uint SmackerPlayer::getStatus() {
+ return 0;
+}
+
+void SmackerPlayer::setDrawPos(int16 x, int16 y) {
+ _drawX = x;
+ _drawY = y;
+ if (_smackerSurface) {
+ _smackerSurface->getDrawRect().x = _drawX;
+ _smackerSurface->getDrawRect().y = _drawY;
+ }
+}
+
+void SmackerPlayer::rewind() {
+ if (_smackerDecoder)
+ _smackerDecoder->rewind();
+}
+
+void SmackerPlayer::update() {
+
+ if (!_smackerDecoder)
+ return;
+
+ if (_paused) {
+ if (_smackerFirst)
+ updateFrame();
+ } else {
+ if (!_smackerDecoder->endOfVideo()) {
+ updateFrame();
+ } else if (!_keepLastFrame) {
+ // Inform the scene about the end of the video playback
+ if (_scene)
+ sendMessage(_scene, 0x3002, 0);
+ _videoDone = true;
+ } else {
+ rewind();
+ updateFrame();
+ _videoDone = false;
+ }
+ }
+
+}
+
+void SmackerPlayer::updateFrame() {
+ const Graphics::Surface *smackerFrame = _smackerDecoder->decodeNextFrame();
+
+ if (_smackerFirst) {
+ _smackerSurface->setSmackerFrame(smackerFrame);
+ if (_drawX < 0 || _drawY < 0) {
+ if (_doubleSurface) {
+ _drawX = 320 - _smackerDecoder->getWidth();
+ _drawY = 240 - _smackerDecoder->getHeight();
+ } else {
+ _drawX = (640 - _smackerDecoder->getWidth()) / 2;
+ _drawY = (480 - _smackerDecoder->getHeight()) / 2;
+ }
+ }
+ _smackerSurface->getDrawRect().x = _drawX;
+ _smackerSurface->getDrawRect().y = _drawY;
+ _smackerFirst = false;
+ }
+
+ if (_smackerDecoder->hasDirtyPalette())
+ updatePalette();
+
+}
+
+void SmackerPlayer::updatePalette() {
+ byte tempPalette[1024];
+ const byte *smackerPalette = _smackerDecoder->getPalette();
+ for (int i = 0; i < 256; i++) {
+ tempPalette[i * 4 + 0] = smackerPalette[i * 3 + 0];
+ tempPalette[i * 4 + 1] = smackerPalette[i * 3 + 1];
+ tempPalette[i * 4 + 2] = smackerPalette[i * 3 + 2];
+ }
+ _palette->copyPalette(tempPalette, 0, 256, 0);
+}
+
+} // End of namespace Neverhood
diff --git a/engines/neverhood/smackerplayer.h b/engines/neverhood/smackerplayer.h
new file mode 100644
index 0000000000..26ebff5d33
--- /dev/null
+++ b/engines/neverhood/smackerplayer.h
@@ -0,0 +1,90 @@
+/* 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_SMACKERPLAYER_H
+#define NEVERHOOD_SMACKERPLAYER_H
+
+#include "video/smk_decoder.h"
+#include "neverhood/neverhood.h"
+#include "neverhood/entity.h"
+
+namespace Neverhood {
+
+class Scene;
+class Palette;
+
+class SmackerSurface : public BaseSurface {
+public:
+ SmackerSurface(NeverhoodEngine *vm);
+ virtual void draw();
+ void setSmackerFrame(const Graphics::Surface *smackerFrame);
+protected:
+ const Graphics::Surface *_smackerFrame;
+};
+
+class SmackerDoubleSurface : public SmackerSurface {
+public:
+ SmackerDoubleSurface(NeverhoodEngine *vm);
+ virtual void draw();
+};
+
+class NeverhoodSmackerDecoder : public Video::SmackerDecoder {
+public:
+ void forceSeekToFrame(uint frame);
+};
+
+class SmackerPlayer : public Entity {
+public:
+ SmackerPlayer(NeverhoodEngine *vm, Scene *scene, uint32 fileHash, bool doubleSurface, bool flag, bool paused = false);
+ ~SmackerPlayer();
+ BaseSurface *getSurface() { return _smackerSurface; }
+ void open(uint32 fileHash, bool keepLastFrame);
+ void close();
+ void gotoFrame(int frameNumber);
+ uint32 getFrameCount();
+ uint32 getFrameNumber();
+ uint getStatus();
+ void setDrawPos(int16 x, int16 y);
+ void rewind();
+ bool isDone() { return getFrameNumber() + 1 == getFrameCount(); }
+ NeverhoodSmackerDecoder *getSmackerDecoder() const { return _smackerDecoder; }
+protected:
+ Scene *_scene;
+ Palette *_palette;
+ NeverhoodSmackerDecoder *_smackerDecoder;
+ SmackerSurface *_smackerSurface;
+ uint32 _fileHash;
+ bool _smackerFirst;
+ bool _doubleSurface;
+ Common::SeekableReadStream *_stream;
+ bool _keepLastFrame;
+ bool _videoDone;
+ bool _paused;
+ int _drawX, _drawY;
+ void update();
+ void updateFrame();
+ void updatePalette();
+};
+
+} // End of namespace Neverhood
+
+#endif /* NEVERHOOD_SMACKERPLAYER_H */
diff --git a/engines/neverhood/smackerscene.cpp b/engines/neverhood/smackerscene.cpp
new file mode 100644
index 0000000000..115aafe5be
--- /dev/null
+++ b/engines/neverhood/smackerscene.cpp
@@ -0,0 +1,122 @@
+/* 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/smackerscene.h"
+
+namespace Neverhood {
+
+SmackerScene::SmackerScene(NeverhoodEngine *vm, Module *parentModule, bool doubleSurface, bool canSkip, bool canAbort)
+ : Scene(vm, parentModule), _doubleSurface(doubleSurface), _canSkip(canSkip), _canAbort(canAbort), _videoPlayedBefore(false),
+ _fileHashListIndex(-1), _fileHashList(NULL), _playNextVideoFlag(false) {
+
+ debug(0, "SmackerScene::SmackerScene(%d, %d, %d)", doubleSurface, canSkip, canAbort);
+
+ // NOTE: Merged from SmackerScene::init, maybe split again if needed (incl. parameter flags)
+
+ if (getGlobalVar(V_SMACKER_CAN_ABORT)) {
+ _canSkip = true;
+ _canAbort = true;
+ }
+
+ if (!_doubleSurface)
+ _vm->_screen->clear();
+
+ _fileHash[0] = 0;
+ _fileHash[1] = 0;
+
+ SetUpdateHandler(&SmackerScene::update);
+ SetMessageHandler(&SmackerScene::handleMessage);
+
+}
+
+SmackerScene::~SmackerScene() {
+
+}
+
+void SmackerScene::setFileHash(uint32 fileHash) {
+ debug(0, "SmackerScene::setFileHash(%08X)", fileHash);
+ _fileHash[0] = fileHash;
+ _fileHashList = _fileHash;
+}
+
+void SmackerScene::setFileHashList(const uint32 *fileHashList) {
+ debug(0, "SmackerScene::setFileHashList(...)");
+ _fileHashList = fileHashList;
+}
+
+void SmackerScene::nextVideo() {
+ debug(0, "SmackerScene::nextVideo()");
+
+ _fileHashListIndex++;
+
+ if (_fileHashList && _fileHashList[_fileHashListIndex] != 0) {
+ uint32 smackerFileHash = _fileHashList[_fileHashListIndex];
+ ResourceHandle resourceHandle;
+ _vm->_res->queryResource(smackerFileHash, resourceHandle);
+ if (resourceHandle.type() != kResTypeVideo) {
+ // Not a Smacker file
+ _vm->_screen->setSmackerDecoder(NULL);
+ sendMessage(_parentModule, 0x1009, 0);
+ return;
+ }
+ _videoPlayedBefore = getSubVar(VA_SMACKER_PLAYED, smackerFileHash);
+ if (!_videoPlayedBefore)
+ setSubVar(VA_SMACKER_PLAYED, smackerFileHash, 1);
+ if (_fileHashListIndex == 0)
+ _smackerPlayer = addSmackerPlayer(new SmackerPlayer(_vm, this, smackerFileHash, _doubleSurface, false));
+ else
+ _smackerPlayer->open(smackerFileHash, false);
+ _vm->_screen->setSmackerDecoder(_smackerPlayer->getSmackerDecoder());
+ } else {
+ _vm->_screen->setSmackerDecoder(NULL);
+ sendMessage(_parentModule, 0x1009, 0);
+ }
+
+}
+
+void SmackerScene::update() {
+ if (_playNextVideoFlag) {
+ nextVideo();
+ _playNextVideoFlag = false;
+ }
+ Scene::update();
+}
+
+uint32 SmackerScene::handleMessage(int messageNum, const MessageParam &param, Entity *sender) {
+ uint32 messageResult = Scene::handleMessage(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x0009:
+ if ((_videoPlayedBefore && _canSkip) || (_canAbort && _canSkip))
+ _playNextVideoFlag = true;
+ break;
+ case 0x000C:
+ if (_canAbort)
+ sendMessage(_parentModule, 0x1009, 0);
+ break;
+ case 0x3002:
+ _playNextVideoFlag = true;
+ break;
+ }
+ return messageResult;
+}
+
+} // End of namespace Neverhood
diff --git a/engines/neverhood/smackerscene.h b/engines/neverhood/smackerscene.h
new file mode 100644
index 0000000000..7ed2e0262b
--- /dev/null
+++ b/engines/neverhood/smackerscene.h
@@ -0,0 +1,54 @@
+/* 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_SMACKERSCENE_H
+#define NEVERHOOD_SMACKERSCENE_H
+
+#include "neverhood/neverhood.h"
+#include "neverhood/resourceman.h"
+#include "neverhood/scene.h"
+
+namespace Neverhood {
+
+class SmackerScene : public Scene {
+public:
+ SmackerScene(NeverhoodEngine *vm, Module *parentModule, bool doubleSurface, bool canSkip, bool canAbort);
+ virtual ~SmackerScene();
+ void setFileHash(uint32 fileHash);
+ void setFileHashList(const uint32 *fileHashList);
+ void nextVideo();
+protected:
+ bool _doubleSurface;
+ bool _canSkip;
+ bool _canAbort;
+ bool _videoPlayedBefore;
+ bool _playNextVideoFlag;
+ int _fileHashListIndex;
+ const uint32 *_fileHashList;
+ uint32 _fileHash[2];
+ void update();
+ uint32 handleMessage(int messageNum, const MessageParam &param, Entity *sender);
+};
+
+} // End of namespace Neverhood
+
+#endif /* NEVERHOOD_SMACKERSCENE_H */
diff --git a/engines/neverhood/sound.cpp b/engines/neverhood/sound.cpp
new file mode 100644
index 0000000000..c84b751e44
--- /dev/null
+++ b/engines/neverhood/sound.cpp
@@ -0,0 +1,788 @@
+/* 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 "common/memstream.h"
+#include "graphics/palette.h"
+#include "neverhood/sound.h"
+#include "neverhood/resourceman.h"
+
+namespace Neverhood {
+
+SoundResource::SoundResource(NeverhoodEngine *vm)
+ : _vm(vm), _soundIndex(-1) {
+}
+
+SoundResource::~SoundResource() {
+ unload();
+}
+
+bool SoundResource::isPlaying() {
+ AudioResourceManSoundItem *soundItem = getSoundItem();
+ return soundItem ? soundItem->isPlaying() : false;
+}
+
+void SoundResource::load(uint32 fileHash) {
+ unload();
+ _soundIndex = _vm->_audioResourceMan->addSound(fileHash);
+ AudioResourceManSoundItem *soundItem = getSoundItem();
+ if (soundItem)
+ soundItem->loadSound();
+}
+
+void SoundResource::unload() {
+ if (_soundIndex >= 0) {
+ _vm->_audioResourceMan->removeSound(_soundIndex);
+ _soundIndex = -1;
+ }
+}
+
+void SoundResource::play(uint32 fileHash) {
+ load(fileHash);
+ play();
+}
+
+void SoundResource::play() {
+ AudioResourceManSoundItem *soundItem = getSoundItem();
+ if (soundItem)
+ soundItem->playSound(false);
+}
+
+void SoundResource::stop() {
+ AudioResourceManSoundItem *soundItem = getSoundItem();
+ if (soundItem)
+ soundItem->stopSound();
+}
+
+void SoundResource::setVolume(int16 volume) {
+ AudioResourceManSoundItem *soundItem = getSoundItem();
+ if (soundItem)
+ soundItem->setVolume(volume);
+}
+
+void SoundResource::setPan(int16 pan) {
+ AudioResourceManSoundItem *soundItem = getSoundItem();
+ if (soundItem)
+ soundItem->setPan(pan);
+}
+
+AudioResourceManSoundItem *SoundResource::getSoundItem() {
+ return _vm->_audioResourceMan->getSoundItem(_soundIndex);
+}
+
+MusicResource::MusicResource(NeverhoodEngine *vm)
+ : _vm(vm), _musicIndex(-1) {
+}
+
+bool MusicResource::isPlaying() {
+ AudioResourceManMusicItem *musicItem = getMusicItem();
+ return musicItem && musicItem->isPlaying();
+}
+
+void MusicResource::load(uint32 fileHash) {
+ unload();
+ _musicIndex = _vm->_audioResourceMan->loadMusic(fileHash);
+}
+
+void MusicResource::unload() {
+ AudioResourceManMusicItem *musicItem = getMusicItem();
+ if (musicItem) {
+ musicItem->unloadMusic();
+ _musicIndex = -1;
+ }
+}
+
+void MusicResource::play(int16 fadeVolumeStep) {
+ AudioResourceManMusicItem *musicItem = getMusicItem();
+ if (musicItem)
+ musicItem->playMusic(fadeVolumeStep);
+}
+
+void MusicResource::stop(int16 fadeVolumeStep) {
+ AudioResourceManMusicItem *musicItem = getMusicItem();
+ if (musicItem)
+ musicItem->stopMusic(fadeVolumeStep);
+}
+
+void MusicResource::setVolume(int16 volume) {
+ AudioResourceManMusicItem *musicItem = getMusicItem();
+ if (musicItem)
+ musicItem->setVolume(volume);
+}
+
+AudioResourceManMusicItem *MusicResource::getMusicItem() {
+ return _vm->_audioResourceMan->getMusicItem(_musicIndex);
+}
+
+MusicItem::MusicItem(NeverhoodEngine *vm, uint32 groupNameHash, uint32 musicFileHash)
+ : _vm(vm), _musicResource(NULL) {
+
+ _groupNameHash = groupNameHash;
+ _fileHash = musicFileHash;
+ _play = false;
+ _stop = false;
+ _fadeVolumeStep = 0;
+ _countdown = 24;
+ _musicResource = new MusicResource(_vm);
+ _musicResource->load(musicFileHash);
+}
+
+MusicItem::~MusicItem() {
+ if (_musicResource)
+ _musicResource->unload();
+ delete _musicResource;
+}
+
+void MusicItem::startMusic(int16 countdown, int16 fadeVolumeStep) {
+ _play = true;
+ _stop = false;
+ _countdown = countdown;
+ _fadeVolumeStep = fadeVolumeStep;
+}
+
+void MusicItem::stopMusic(int16 countdown, int16 fadeVolumeStep) {
+ _play = false;
+ _stop = true;
+ _countdown = countdown;
+ _fadeVolumeStep = fadeVolumeStep;
+}
+
+void MusicItem::update() {
+ if (_countdown) {
+ --_countdown;
+ } else if (_play && !_musicResource->isPlaying()) {
+ debug(1, "MusicItem: play music %08X (fade %d)", _fileHash, _fadeVolumeStep);
+ _musicResource->play(_fadeVolumeStep);
+ _fadeVolumeStep = 0;
+ } else if (_stop) {
+ debug(1, "MusicItem: stop music %08X (fade %d)", _fileHash, _fadeVolumeStep);
+ _musicResource->stop(_fadeVolumeStep);
+ _fadeVolumeStep = 0;
+ _stop = false;
+ }
+}
+
+SoundItem::SoundItem(NeverhoodEngine *vm, uint32 groupNameHash, uint32 soundFileHash,
+ bool playOnceAfterRandomCountdown, int16 minCountdown, int16 maxCountdown,
+ bool playOnceAfterCountdown, int16 initialCountdown, bool playLooping, int16 currCountdown)
+ : _vm(vm), _soundResource(NULL), _groupNameHash(groupNameHash), _fileHash(soundFileHash),
+ _playOnceAfterRandomCountdown(false), _minCountdown(0), _maxCountdown(0),
+ _playOnceAfterCountdown(playOnceAfterCountdown), _initialCountdown(initialCountdown),
+ _playLooping(false), _currCountdown(currCountdown) {
+
+ _soundResource = new SoundResource(vm);
+ _soundResource->load(soundFileHash);
+}
+
+SoundItem::~SoundItem() {
+ if (_soundResource)
+ _soundResource->unload();
+ delete _soundResource;
+}
+
+void SoundItem::setSoundParams(bool playOnceAfterRandomCountdown, int16 minCountdown, int16 maxCountdown,
+ int16 firstMinCountdown, int16 firstMaxCountdown) {
+
+ _playOnceAfterCountdown = false;
+ _playLooping = false;
+ _playOnceAfterRandomCountdown = playOnceAfterRandomCountdown;
+ if (minCountdown > 0)
+ _minCountdown = minCountdown;
+ if (maxCountdown > 0)
+ _maxCountdown = maxCountdown;
+ if (firstMinCountdown >= firstMaxCountdown)
+ _currCountdown = firstMinCountdown;
+ else if (firstMinCountdown > 0 && firstMaxCountdown > 0 && firstMinCountdown < firstMaxCountdown)
+ _currCountdown = _vm->_rnd->getRandomNumberRng(firstMinCountdown, firstMaxCountdown);
+}
+
+void SoundItem::playSoundLooping() {
+ _playOnceAfterRandomCountdown = false;
+ _playOnceAfterCountdown = false;
+ _playLooping = true;
+}
+
+void SoundItem::stopSound() {
+ _playOnceAfterRandomCountdown = false;
+ _playOnceAfterCountdown = false;
+ _playLooping = false;
+ _soundResource->stop();
+}
+
+void SoundItem::setVolume(int volume) {
+ _soundResource->setVolume(volume);
+}
+
+void SoundItem::update() {
+ if (_playOnceAfterCountdown) {
+ if (_currCountdown == 0)
+ _currCountdown = _initialCountdown;
+ else if (--_currCountdown == 0)
+ _soundResource->play();
+ } else if (_playOnceAfterRandomCountdown) {
+ if (_currCountdown == 0) {
+ if (_minCountdown > 0 && _maxCountdown > 0 && _minCountdown < _maxCountdown)
+ _currCountdown = _vm->_rnd->getRandomNumberRng(_minCountdown, _maxCountdown);
+ } else if (--_currCountdown == 0)
+ _soundResource->play();
+ } else if (_playLooping && !_soundResource->isPlaying())
+ _soundResource->play();
+}
+
+// SoundMan
+
+SoundMan::SoundMan(NeverhoodEngine *vm)
+ : _vm(vm), _soundIndex1(-1), _soundIndex2(-1), _soundIndex3(-1) {
+}
+
+SoundMan::~SoundMan() {
+ for (uint i = 0; i < _soundItems.size(); ++i)
+ delete _soundItems[i];
+ for (uint i = 0; i < _musicItems.size(); ++i)
+ delete _musicItems[i];
+}
+
+void SoundMan::addMusic(uint32 groupNameHash, uint32 musicFileHash) {
+ addMusicItem(new MusicItem(_vm, groupNameHash, musicFileHash));
+}
+
+void SoundMan::deleteMusic(uint32 musicFileHash) {
+ MusicItem *musicItem = getMusicItemByHash(musicFileHash);
+ if (musicItem) {
+ delete musicItem;
+ for (uint i = 0; i < _musicItems.size(); ++i)
+ if (_musicItems[i] == musicItem) {
+ _musicItems[i] = NULL;
+ break;
+ }
+ }
+}
+
+void SoundMan::startMusic(uint32 musicFileHash, int16 countdown, int16 fadeVolumeStep) {
+ MusicItem *musicItem = getMusicItemByHash(musicFileHash);
+ if (musicItem)
+ musicItem->startMusic(countdown, fadeVolumeStep);
+}
+
+void SoundMan::stopMusic(uint32 musicFileHash, int16 countdown, int16 fadeVolumeStep) {
+ MusicItem *musicItem = getMusicItemByHash(musicFileHash);
+ if (musicItem)
+ musicItem->stopMusic(countdown, fadeVolumeStep);
+}
+
+void SoundMan::addSound(uint32 groupNameHash, uint32 soundFileHash) {
+ addSoundItem(new SoundItem(_vm, groupNameHash, soundFileHash, false, 50, 600, false, 0, false, 0));
+}
+
+void SoundMan::addSoundList(uint32 groupNameHash, const uint32 *soundFileHashList) {
+ while (*soundFileHashList)
+ addSound(groupNameHash, *soundFileHashList++);
+}
+
+void SoundMan::deleteSound(uint32 soundFileHash) {
+ SoundItem *soundItem = getSoundItemByHash(soundFileHash);
+ if (soundItem) {
+ delete soundItem;
+ for (uint i = 0; i < _soundItems.size(); ++i)
+ if (_soundItems[i] == soundItem) {
+ _soundItems[i] = NULL;
+ break;
+ }
+ }
+}
+
+void SoundMan::setSoundParams(uint32 soundFileHash, bool playOnceAfterRandomCountdown,
+ int16 minCountdown, int16 maxCountdown, int16 firstMinCountdown, int16 firstMaxCountdown) {
+
+ SoundItem *soundItem = getSoundItemByHash(soundFileHash);
+ if (soundItem)
+ soundItem->setSoundParams(playOnceAfterRandomCountdown, minCountdown, maxCountdown,
+ firstMinCountdown, firstMaxCountdown);
+}
+
+void SoundMan::setSoundListParams(const uint32 *soundFileHashList, bool playOnceAfterRandomCountdown,
+ int16 minCountdown, int16 maxCountdown, int16 firstMinCountdown, int16 firstMaxCountdown) {
+
+ while (*soundFileHashList)
+ setSoundParams(*soundFileHashList++, playOnceAfterRandomCountdown,
+ minCountdown, maxCountdown, firstMinCountdown, firstMaxCountdown);
+}
+
+void SoundMan::playSoundLooping(uint32 soundFileHash) {
+ SoundItem *soundItem = getSoundItemByHash(soundFileHash);
+ if (soundItem)
+ soundItem->playSoundLooping();
+}
+
+void SoundMan::stopSound(uint32 soundFileHash) {
+ SoundItem *soundItem = getSoundItemByHash(soundFileHash);
+ if (soundItem)
+ soundItem->stopSound();
+}
+
+void SoundMan::setSoundVolume(uint32 soundFileHash, int volume) {
+ SoundItem *soundItem = getSoundItemByHash(soundFileHash);
+ if (soundItem)
+ soundItem->setVolume(volume);
+}
+
+void SoundMan::update() {
+
+ for (uint i = 0; i < _soundItems.size(); ++i) {
+ SoundItem *soundItem = _soundItems[i];
+ if (soundItem)
+ soundItem->update();
+ }
+
+ for (uint i = 0; i < _musicItems.size(); ++i) {
+ MusicItem *musicItem = _musicItems[i];
+ if (musicItem)
+ musicItem->update();
+ }
+
+}
+
+void SoundMan::deleteGroup(uint32 groupNameHash) {
+ deleteMusicGroup(groupNameHash);
+ deleteSoundGroup(groupNameHash);
+}
+
+void SoundMan::deleteMusicGroup(uint32 groupNameHash) {
+ for (uint index = 0; index < _musicItems.size(); ++index) {
+ MusicItem *musicItem = _musicItems[index];
+ if (musicItem && musicItem->getGroupNameHash() == groupNameHash) {
+ delete musicItem;
+ _musicItems[index] = NULL;
+ }
+ }
+}
+
+void SoundMan::deleteSoundGroup(uint32 groupNameHash) {
+
+ if (_soundIndex1 != -1 && _soundItems[_soundIndex1]->getGroupNameHash() == groupNameHash) {
+ deleteSoundByIndex(_soundIndex1);
+ _soundIndex1 = -1;
+ }
+
+ if (_soundIndex2 != -1 && _soundItems[_soundIndex2]->getGroupNameHash() == groupNameHash) {
+ deleteSoundByIndex(_soundIndex2);
+ _soundIndex2 = -1;
+ }
+
+ for (uint index = 0; index < _soundItems.size(); ++index)
+ if (_soundItems[index] && _soundItems[index]->getGroupNameHash() == groupNameHash)
+ deleteSoundByIndex(index);
+
+}
+
+void SoundMan::playTwoSounds(uint32 groupNameHash, uint32 soundFileHash1, uint32 soundFileHash2, int16 initialCountdown) {
+
+ int16 currCountdown1 = _initialCountdown;
+ int16 currCountdown2 = _initialCountdown / 2;
+
+ if (_soundIndex1 != -1) {
+ currCountdown1 = _soundItems[_soundIndex1]->getCurrCountdown();
+ deleteSoundByIndex(_soundIndex1);
+ _soundIndex1 = -1;
+ }
+
+ if (_soundIndex2 != -1) {
+ currCountdown2 = _soundItems[_soundIndex2]->getCurrCountdown();
+ deleteSoundByIndex(_soundIndex2);
+ _soundIndex2 = -1;
+ }
+
+ if (initialCountdown > 0)
+ _initialCountdown = initialCountdown;
+
+ if (soundFileHash1 != 0) {
+ SoundItem *soundItem = new SoundItem(_vm, groupNameHash, soundFileHash1, false, 0, 0,
+ _playOnceAfterCountdown, _initialCountdown, false, currCountdown1);
+ soundItem->setVolume(80);
+ _soundIndex1 = addSoundItem(soundItem);
+ }
+
+ if (soundFileHash2 != 0) {
+ SoundItem *soundItem = new SoundItem(_vm, groupNameHash, soundFileHash2, false, 0, 0,
+ _playOnceAfterCountdown, _initialCountdown, false, currCountdown2);
+ soundItem->setVolume(80);
+ _soundIndex2 = addSoundItem(soundItem);
+ }
+
+}
+
+void SoundMan::playSoundThree(uint32 groupNameHash, uint32 soundFileHash) {
+
+ if (_soundIndex3 != -1) {
+ deleteSoundByIndex(_soundIndex3);
+ _soundIndex3 = -1;
+ }
+
+ if (soundFileHash != 0) {
+ SoundItem *soundItem = new SoundItem(_vm, groupNameHash, soundFileHash, false, 0, 0, false, _initialCountdown3, false, 0);
+ _soundIndex3 = addSoundItem(soundItem);
+ }
+
+}
+
+void SoundMan::setTwoSoundsPlayFlag(bool playOnceAfterCountdown) {
+ if (_soundIndex1 != -1)
+ _soundItems[_soundIndex1]->setPlayOnceAfterCountdown(playOnceAfterCountdown);
+ if (_soundIndex2 != -1)
+ _soundItems[_soundIndex2]->setPlayOnceAfterCountdown(playOnceAfterCountdown);
+ _playOnceAfterCountdown = playOnceAfterCountdown;
+}
+
+void SoundMan::setSoundThreePlayFlag(bool playOnceAfterCountdown) {
+ if (_soundIndex3 != -1)
+ _soundItems[_soundIndex3]->setPlayOnceAfterCountdown(playOnceAfterCountdown);
+ _playOnceAfterCountdown3 = playOnceAfterCountdown;
+}
+
+MusicItem *SoundMan::getMusicItemByHash(uint32 musicFileHash) {
+ for (uint i = 0; i < _musicItems.size(); ++i)
+ if (_musicItems[i] && _musicItems[i]->getFileHash() == musicFileHash)
+ return _musicItems[i];
+ return NULL;
+}
+
+SoundItem *SoundMan::getSoundItemByHash(uint32 soundFileHash) {
+ for (uint i = 0; i < _soundItems.size(); ++i)
+ if (_soundItems[i] && _soundItems[i]->getFileHash() == soundFileHash)
+ return _soundItems[i];
+ return NULL;
+}
+
+int16 SoundMan::addMusicItem(MusicItem *musicItem) {
+ for (uint i = 0; i < _musicItems.size(); ++i)
+ if (!_musicItems[i]) {
+ _musicItems[i] = musicItem;
+ return i;
+ }
+ int16 musicIndex = _musicItems.size();
+ _musicItems.push_back(musicItem);
+ return musicIndex;
+}
+
+int16 SoundMan::addSoundItem(SoundItem *soundItem) {
+ for (uint i = 0; i < _soundItems.size(); ++i)
+ if (!_soundItems[i]) {
+ _soundItems[i] = soundItem;
+ return i;
+ }
+ int16 soundIndex = _soundItems.size();
+ _soundItems.push_back(soundItem);
+ return soundIndex;
+}
+
+void SoundMan::deleteSoundByIndex(int index) {
+ delete _soundItems[index];
+ _soundItems[index] = NULL;
+}
+
+// NeverhoodAudioStream
+
+NeverhoodAudioStream::NeverhoodAudioStream(int rate, byte shiftValue, bool isLooping, DisposeAfterUse::Flag disposeStream, Common::SeekableReadStream *stream)
+ : _rate(rate), _shiftValue(shiftValue), _isLooping(isLooping), _isStereo(false), _stream(stream, disposeStream), _endOfData(false), _buffer(0),
+ _isCompressed(_shiftValue != 0xFF), _prevValue(0) {
+ // Setup our buffer for readBuffer
+ _buffer = new byte[kSampleBufferLength * (_isCompressed ? 1 : 2)];
+ assert(_buffer);
+}
+
+NeverhoodAudioStream::~NeverhoodAudioStream() {
+ delete[] _buffer;
+}
+
+int NeverhoodAudioStream::readBuffer(int16 *buffer, const int numSamples) {
+ int samplesLeft = numSamples;
+
+ while (samplesLeft > 0 && !_endOfData) {
+
+ const int maxSamples = MIN<int>(kSampleBufferLength, samplesLeft);
+ const int bytesToRead = maxSamples * (_isCompressed ? 1 : 2);
+ int bytesRead = _stream->read(_buffer, bytesToRead);
+ int samplesRead = bytesRead / (_isCompressed ? 1 : 2);
+
+ samplesLeft -= samplesRead;
+
+ const byte *src = _buffer;
+ if (_isCompressed) {
+ while (samplesRead--) {
+ _prevValue += (int8)(*src++);
+ *buffer++ = _prevValue << _shiftValue;
+ }
+ } else {
+ memcpy(buffer, _buffer, bytesRead);
+ buffer += bytesRead;
+ }
+
+ if (bytesRead < bytesToRead || _stream->pos() >= _stream->size() || _stream->err() || _stream->eos()) {
+ if (_isLooping)
+ _stream->seek(0);
+ else
+ _endOfData = true;
+ }
+
+ }
+
+ return numSamples - samplesLeft;
+}
+
+AudioResourceManSoundItem::AudioResourceManSoundItem(NeverhoodEngine *vm, uint32 fileHash)
+ : _vm(vm), _fileHash(fileHash), _data(NULL), _isLoaded(false), _isPlaying(false),
+ _volume(100), _panning(50) {
+
+ _vm->_res->queryResource(_fileHash, _resourceHandle);
+}
+
+void AudioResourceManSoundItem::loadSound() {
+ if (!_data && _resourceHandle.isValid() &&
+ (_resourceHandle.type() == kResTypeSound || _resourceHandle.type() == kResTypeMusic)) {
+ _vm->_res->loadResource(_resourceHandle);
+ _data = _resourceHandle.data();
+ }
+}
+
+void AudioResourceManSoundItem::unloadSound() {
+ if (_vm->_mixer->isSoundHandleActive(_soundHandle))
+ _vm->_mixer->stopHandle(_soundHandle);
+ _vm->_res->unloadResource(_resourceHandle);
+ _data = NULL;
+}
+
+void AudioResourceManSoundItem::setVolume(int16 volume) {
+ _volume = MIN<int16>(volume, 100);
+ if (_isPlaying && _vm->_mixer->isSoundHandleActive(_soundHandle))
+ _vm->_mixer->setChannelVolume(_soundHandle, VOLUME(_volume));
+}
+
+void AudioResourceManSoundItem::setPan(int16 pan) {
+ _panning = MIN<int16>(pan, 100);
+ if (_isPlaying && _vm->_mixer->isSoundHandleActive(_soundHandle))
+ _vm->_mixer->setChannelVolume(_soundHandle, PANNING(_panning));
+}
+
+void AudioResourceManSoundItem::playSound(bool looping) {
+ if (!_data)
+ loadSound();
+ if (_data) {
+ const byte *shiftValue = _resourceHandle.extData();
+ Common::MemoryReadStream *stream = new Common::MemoryReadStream(_data, _resourceHandle.size(), DisposeAfterUse::NO);
+ NeverhoodAudioStream *audioStream = new NeverhoodAudioStream(22050, *shiftValue, false, DisposeAfterUse::YES, stream);
+ _vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, &_soundHandle,
+ audioStream, -1, VOLUME(_volume), PANNING(_panning));
+ debug(1, "playing sound %08X", _fileHash);
+ _isPlaying = true;
+ }
+}
+
+void AudioResourceManSoundItem::stopSound() {
+ if (_vm->_mixer->isSoundHandleActive(_soundHandle))
+ _vm->_mixer->stopHandle(_soundHandle);
+ _isPlaying = false;
+}
+
+bool AudioResourceManSoundItem::isPlaying() {
+ return _vm->_mixer->isSoundHandleActive(_soundHandle);
+}
+
+AudioResourceManMusicItem::AudioResourceManMusicItem(NeverhoodEngine *vm, uint32 fileHash)
+ : _vm(vm), _fileHash(fileHash), _terminate(false), _canRestart(false),
+ _volume(100), _panning(50), _start(false), _isFadingIn(false), _isFadingOut(false), _isPlaying(false) {
+
+}
+
+void AudioResourceManMusicItem::playMusic(int16 fadeVolumeStep) {
+ if (!_isPlaying) {
+ _isFadingIn = false;
+ _isFadingOut = false;
+ if (fadeVolumeStep != 0) {
+ _isFadingIn = true;
+ _fadeVolume = 0;
+ _fadeVolumeStep = fadeVolumeStep;
+ }
+ _start = true;
+ _terminate = false;
+ }
+}
+
+void AudioResourceManMusicItem::stopMusic(int16 fadeVolumeStep) {
+ if (_vm->_mixer->isSoundHandleActive(_soundHandle)) {
+ if (fadeVolumeStep != 0) {
+ if (_isFadingIn)
+ _isFadingIn = false;
+ else
+ _fadeVolume = _volume;
+ _isFadingOut = true;
+ _fadeVolumeStep = fadeVolumeStep;
+ } else {
+ _vm->_mixer->stopHandle(_soundHandle);
+ }
+ _isPlaying = false;
+ }
+}
+
+void AudioResourceManMusicItem::unloadMusic() {
+ if (_isFadingOut) {
+ _canRestart = true;
+ } else {
+ if (_vm->_mixer->isSoundHandleActive(_soundHandle))
+ _vm->_mixer->stopHandle(_soundHandle);
+ _isPlaying = false;
+ _terminate = true;
+ }
+}
+
+void AudioResourceManMusicItem::setVolume(int16 volume) {
+ _volume = MIN<int16>(volume, 100);
+ if (_isPlaying && _vm->_mixer->isSoundHandleActive(_soundHandle))
+ _vm->_mixer->setChannelVolume(_soundHandle, VOLUME(_volume));
+}
+
+void AudioResourceManMusicItem::restart() {
+ _canRestart = false;
+ _isFadingOut = false;
+ _isFadingIn = true;
+}
+
+void AudioResourceManMusicItem::update() {
+
+ if (_start && !_vm->_mixer->isSoundHandleActive(_soundHandle)) {
+ ResourceHandle resourceHandle;
+ _vm->_res->queryResource(_fileHash, resourceHandle);
+ Common::SeekableReadStream *stream = _vm->_res->createStream(_fileHash);
+ const byte *shiftValue = resourceHandle.extData();
+ NeverhoodAudioStream *audioStream = new NeverhoodAudioStream(22050, *shiftValue, true, DisposeAfterUse::YES, stream);
+ _vm->_mixer->playStream(Audio::Mixer::kMusicSoundType, &_soundHandle,
+ audioStream, -1, VOLUME(_isFadingIn ? _fadeVolume : _volume),
+ PANNING(_panning));
+ _start = false;
+ _isPlaying = true;
+ }
+
+ if (_vm->_mixer->isSoundHandleActive(_soundHandle)) {
+ if (_isFadingIn) {
+ _fadeVolume += _fadeVolumeStep;
+ if (_fadeVolume >= _volume) {
+ _fadeVolume = _volume;
+ _isFadingIn = false;
+ }
+ _vm->_mixer->setChannelVolume(_soundHandle, VOLUME(_fadeVolume));
+ }
+ if (_isFadingOut) {
+ _fadeVolume -= _fadeVolumeStep;
+ if (_fadeVolume < 0)
+ _fadeVolume = 0;
+ _vm->_mixer->setChannelVolume(_soundHandle, VOLUME(_fadeVolume));
+ if (_fadeVolume == 0) {
+ _isFadingOut = false;
+ stopMusic(0);
+ if (_canRestart)
+ unloadMusic();
+ }
+ }
+ }
+
+}
+
+AudioResourceMan::AudioResourceMan(NeverhoodEngine *vm)
+ : _vm(vm) {
+}
+
+AudioResourceMan::~AudioResourceMan() {
+ for (uint i = 0; i < _soundItems.size(); ++i)
+ delete _soundItems[i];
+ for (uint i = 0; i < _musicItems.size(); ++i)
+ delete _musicItems[i];
+}
+
+int16 AudioResourceMan::addSound(uint32 fileHash) {
+ AudioResourceManSoundItem *soundItem = new AudioResourceManSoundItem(_vm, fileHash);
+
+ for (uint i = 0; i < _soundItems.size(); ++i)
+ if (!_soundItems[i]) {
+ _soundItems[i] = soundItem;
+ return i;
+ }
+
+ int16 soundIndex = (int16)_soundItems.size();
+ _soundItems.push_back(soundItem);
+ return soundIndex;
+}
+
+void AudioResourceMan::removeSound(int16 soundIndex) {
+ AudioResourceManSoundItem *soundItem = getSoundItem(soundIndex);
+ if (soundItem) {
+ soundItem->unloadSound();
+ delete soundItem;
+ _soundItems[soundIndex] = NULL;
+ }
+}
+
+int16 AudioResourceMan::loadMusic(uint32 fileHash) {
+ AudioResourceManMusicItem *musicItem;
+
+ for (uint i = 0; i < _musicItems.size(); ++i) {
+ musicItem = _musicItems[i];
+ if (musicItem && musicItem->getFileHash() == fileHash && musicItem->canRestart()) {
+ musicItem->restart();
+ return i;
+ }
+ }
+
+ musicItem = new AudioResourceManMusicItem(_vm, fileHash);
+
+ for (uint i = 0; i < _musicItems.size(); ++i) {
+ if (!_musicItems[i]) {
+ _musicItems[i] = musicItem;
+ return i;
+ }
+ }
+
+ int16 musicIndex = _musicItems.size();
+ _musicItems.push_back(musicItem);
+ return musicIndex;
+
+}
+
+void AudioResourceMan::updateMusic() {
+ for (uint musicIndex = 0; musicIndex < _musicItems.size(); ++musicIndex) {
+ AudioResourceManMusicItem *musicItem = _musicItems[musicIndex];
+ if (musicItem) {
+ musicItem->update();
+ if (musicItem->isTerminated()) {
+ delete musicItem;
+ _musicItems[musicIndex] = NULL;
+ }
+ }
+ }
+}
+
+AudioResourceManSoundItem *AudioResourceMan::getSoundItem(int16 index) {
+ return (index >= 0 && index < (int16)_soundItems.size()) ? _soundItems[index] : NULL;
+}
+
+AudioResourceManMusicItem *AudioResourceMan::getMusicItem(int16 index) {
+ return (index >= 0 && index < (int16)_musicItems.size()) ? _musicItems[index] : NULL;
+}
+
+} // End of namespace Neverhood
diff --git a/engines/neverhood/sound.h b/engines/neverhood/sound.h
new file mode 100644
index 0000000000..d3318998db
--- /dev/null
+++ b/engines/neverhood/sound.h
@@ -0,0 +1,286 @@
+/* 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_SOUND_H
+#define NEVERHOOD_SOUND_H
+
+#include "audio/audiostream.h"
+#include "common/array.h"
+#include "graphics/surface.h"
+#include "neverhood/neverhood.h"
+#include "neverhood/resource.h"
+
+namespace Neverhood {
+
+// Convert volume from percent to 0..255
+#define VOLUME(volume) (Audio::Mixer::kMaxChannelVolume / 100 * (volume))
+
+// Convert panning from percent (50% equals center) to -127..0..+127
+#define PANNING(panning) (254 / 100 * (panning) - 127)
+
+class AudioResourceManSoundItem;
+class AudioResourceManMusicItem;
+class AudioResourceMan;
+
+class SoundResource {
+public:
+ SoundResource(NeverhoodEngine *vm);
+ ~SoundResource();
+ bool isPlaying();
+ void load(uint32 fileHash);
+ void unload();
+ void play(uint32 fileHash);
+ void play();
+ void stop();
+ void setVolume(int16 volume);
+ void setPan(int16 pan);
+protected:
+ NeverhoodEngine *_vm;
+ int16 _soundIndex;
+ AudioResourceManSoundItem *getSoundItem();
+};
+
+class MusicResource {
+public:
+ MusicResource(NeverhoodEngine *vm);
+ bool isPlaying();
+ void load(uint32 fileHash);
+ void unload();
+ void play(int16 fadeVolumeStep);
+ void stop(int16 fadeVolumeStep);
+ void setVolume(int16 volume);
+protected:
+ NeverhoodEngine *_vm;
+ int16 _musicIndex;
+ AudioResourceManMusicItem *getMusicItem();
+};
+
+class MusicItem {
+public:
+ MusicItem(NeverhoodEngine *vm, uint32 groupNameHash, uint32 musicFileHash);
+ ~MusicItem();
+ void startMusic(int16 countdown, int16 fadeVolumeStep);
+ void stopMusic(int16 countdown, int16 fadeVolumeStep);
+ void update();
+ uint32 getGroupNameHash() const { return _groupNameHash; }
+ uint32 getFileHash() const { return _fileHash; }
+protected:
+ NeverhoodEngine *_vm;
+ uint32 _groupNameHash;
+ uint32 _fileHash;
+ bool _play;
+ bool _stop;
+ int16 _fadeVolumeStep;
+ int16 _countdown;
+ MusicResource *_musicResource;
+};
+
+class SoundItem {
+public:
+ SoundItem(NeverhoodEngine *vm, uint32 groupNameHash, uint32 soundFileHash,
+ bool playOnceAfterRandomCountdown, int16 minCountdown, int16 maxCountdown,
+ bool playOnceAfterCountdown, int16 initialCountdown, bool playLooping, int16 currCountdown);
+ ~SoundItem();
+ void setSoundParams(bool playOnceAfterRandomCountdown, int16 minCountdown, int16 maxCountdown,
+ int16 firstMinCountdown, int16 firstMaxCountdown);
+ void playSoundLooping();
+ void stopSound();
+ void setVolume(int volume);
+ void update();
+ void setPlayOnceAfterCountdown(bool playOnceAfterCountdown) { _playOnceAfterCountdown = playOnceAfterCountdown; }
+ uint32 getGroupNameHash() const { return _groupNameHash; }
+ uint32 getFileHash() const { return _fileHash; }
+ int16 getCurrCountdown() const { return _currCountdown; }
+protected:
+ NeverhoodEngine *_vm;
+ uint32 _groupNameHash;
+ uint32 _fileHash;
+ bool _playOnceAfterRandomCountdown;
+ int16 _minCountdown;
+ int16 _maxCountdown;
+ bool _playOnceAfterCountdown;
+ int16 _initialCountdown;
+ bool _playLooping;
+ int16 _currCountdown;
+ SoundResource *_soundResource;
+};
+
+class SoundMan {
+public:
+ SoundMan(NeverhoodEngine *vm);
+ ~SoundMan();
+
+ // Music
+ void addMusic(uint32 groupNameHash, uint32 musicFileHash);
+ void deleteMusic(uint32 musicFileHash);
+ void startMusic(uint32 musicFileHash, int16 countdown, int16 fadeVolumeStep);
+ void stopMusic(uint32 musicFileHash, int16 countdown, int16 fadeVolumeStep);
+
+ // Sound
+ void addSound(uint32 groupNameHash, uint32 soundFileHash);
+ void addSoundList(uint32 groupNameHash, const uint32 *soundFileHashList);
+ void deleteSound(uint32 soundFileHash);
+ void setSoundParams(uint32 soundFileHash, bool playOnceAfterRandomCountdown,
+ int16 minCountdown, int16 maxCountdown, int16 firstMinCountdown, int16 firstMaxCountdown);
+ void setSoundListParams(const uint32 *soundFileHashList, bool playOnceAfterRandomCountdown,
+ int16 minCountdown, int16 maxCountdown, int16 firstMinCountdown, int16 firstMaxCountdown);
+ void playSoundLooping(uint32 soundFileHash);
+ void stopSound(uint32 soundFileHash);
+ void setSoundVolume(uint32 soundFileHash, int volume);
+
+ // Misc
+ void update();
+ void deleteGroup(uint32 groupNameHash);
+ void deleteMusicGroup(uint32 groupNameHash);
+ void deleteSoundGroup(uint32 groupNameHash);
+ void playTwoSounds(uint32 groupNameHash, uint32 soundFileHash1, uint32 soundFileHash2, int16 initialCountdown);
+ void playSoundThree(uint32 groupNameHash, uint32 soundFileHash);
+ void setTwoSoundsPlayFlag(bool playOnceAfterCountdown);
+ void setSoundThreePlayFlag(bool playOnceAfterCountdown);
+
+protected:
+ NeverhoodEngine *_vm;
+
+ // TODO Find out what these special sounds are used for (door sounds?)
+ int _soundIndex1, _soundIndex2;
+ int16 _initialCountdown;
+ bool _playOnceAfterCountdown;
+
+ int _soundIndex3;
+ int16 _initialCountdown3;
+ bool _playOnceAfterCountdown3;
+
+ Common::Array<MusicItem*> _musicItems;
+ Common::Array<SoundItem*> _soundItems;
+
+ MusicItem *getMusicItemByHash(uint32 musicFileHash);
+ SoundItem *getSoundItemByHash(uint32 soundFileHash);
+ int16 addMusicItem(MusicItem *musicItem);
+ int16 addSoundItem(SoundItem *soundItem);
+ void deleteSoundByIndex(int index);
+
+};
+
+class NeverhoodAudioStream : public Audio::AudioStream {
+public:
+ NeverhoodAudioStream(int rate, byte shiftValue, bool isLooping, DisposeAfterUse::Flag disposeStream, Common::SeekableReadStream *stream);
+ ~NeverhoodAudioStream();
+ int readBuffer(int16 *buffer, const int numSamples);
+ bool isStereo() const { return _isStereo; }
+ bool endOfData() const { return _endOfData; }
+ int getRate() const { return _rate; }
+private:
+ const int _rate;
+ const bool _isLooping;
+ const bool _isStereo;
+ const byte _shiftValue;
+ const bool _isCompressed;
+ int16 _prevValue;
+ Common::DisposablePtr<Common::SeekableReadStream> _stream;
+ bool _endOfData;
+ byte *_buffer;
+ enum {
+ kSampleBufferLength = 2048
+ };
+ int fillBuffer(int maxSamples);
+};
+
+// TODO Rename these
+
+class AudioResourceManSoundItem {
+public:
+ AudioResourceManSoundItem(NeverhoodEngine *vm, uint32 fileHash);
+ void loadSound();
+ void unloadSound();
+ void setVolume(int16 volume);
+ void setPan(int16 pan);
+ void playSound(bool looping);
+ void stopSound();
+ bool isPlaying();
+protected:
+ NeverhoodEngine *_vm;
+ uint32 _fileHash;
+ ResourceHandle _resourceHandle;
+ const byte *_data;
+ bool _isLoaded;
+ bool _isPlaying;
+ int16 _volume;
+ int16 _panning;
+ Audio::SoundHandle _soundHandle;
+};
+
+class AudioResourceManMusicItem {
+public:
+ AudioResourceManMusicItem(NeverhoodEngine *vm, uint32 fileHash);
+ void playMusic(int16 fadeVolumeStep);
+ void stopMusic(int16 fadeVolumeStep);
+ void unloadMusic();
+ void setVolume(int16 volume);
+ void restart();
+ void update();
+ bool isPlaying() const { return _isPlaying; }
+ bool canRestart() const { return _canRestart; }
+ bool isTerminated() const { return _terminate; }
+ uint32 getFileHash() const { return _fileHash; }
+protected:
+ NeverhoodEngine *_vm;
+ uint32 _fileHash;
+ bool _isPlaying;
+ bool _canRestart;
+ bool _terminate;
+ int16 _volume;
+ int16 _panning;
+ bool _start;
+ bool _isFadingIn;
+ bool _isFadingOut;
+ int16 _fadeVolume;
+ int16 _fadeVolumeStep;
+ Audio::SoundHandle _soundHandle;
+};
+
+class AudioResourceMan {
+public:
+ AudioResourceMan(NeverhoodEngine *vm);
+ ~AudioResourceMan();
+
+ int16 addSound(uint32 fileHash);
+ void removeSound(int16 soundIndex);
+
+ int16 loadMusic(uint32 fileHash);
+ void updateMusic();
+
+ AudioResourceManSoundItem *getSoundItem(int16 index);
+ AudioResourceManMusicItem *getMusicItem(int16 index);
+
+protected:
+ NeverhoodEngine *_vm;
+
+ Common::Array<AudioResourceManMusicItem*> _musicItems;
+ Common::Array<AudioResourceManSoundItem*> _soundItems;
+
+ int16 addSoundItem(AudioResourceManSoundItem *soundItem);
+
+};
+
+} // End of namespace Neverhood
+
+#endif /* NEVERHOOD_SOUND_H */
diff --git a/engines/neverhood/sprite.cpp b/engines/neverhood/sprite.cpp
new file mode 100644
index 0000000000..45d131fd3c
--- /dev/null
+++ b/engines/neverhood/sprite.cpp
@@ -0,0 +1,486 @@
+/* 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/sprite.h"
+#include "neverhood/screen.h"
+
+namespace Neverhood {
+
+// Sprite
+
+Sprite::Sprite(NeverhoodEngine *vm, int objectPriority)
+ : Entity(vm, objectPriority), _x(0), _y(0), _spriteUpdateCb(NULL), _filterXCb(NULL), _filterYCb(NULL),
+ _dataResource(vm), _doDeltaX(false), _doDeltaY(false), _needRefresh(false), _flags(0), _surface(NULL) {
+
+ SetMessageHandler(&Sprite::handleMessage);
+
+}
+
+Sprite::~Sprite() {
+ delete _surface;
+}
+
+void Sprite::updateBounds() {
+ if (_doDeltaX) {
+ _collisionBounds.x1 = _x - _collisionBoundsOffset.x - _collisionBoundsOffset.width + 1;
+ _collisionBounds.x2 = _x - _collisionBoundsOffset.x;
+ } else {
+ _collisionBounds.x1 = _x + _collisionBoundsOffset.x;
+ _collisionBounds.x2 = _x + _collisionBoundsOffset.x + _collisionBoundsOffset.width - 1;
+ }
+ if (_doDeltaY) {
+ _collisionBounds.y1 = _y - _collisionBoundsOffset.y - _collisionBoundsOffset.height + 1;
+ _collisionBounds.y2 = _y - _collisionBoundsOffset.y;
+ } else {
+ _collisionBounds.y1 = _y + _collisionBoundsOffset.y;
+ _collisionBounds.y2 = _y + _collisionBoundsOffset.y + _collisionBoundsOffset.height - 1;
+ }
+}
+
+void Sprite::setDoDeltaX(int type) {
+ // Clear, set or toggle
+ _doDeltaX = type == 2 ? !_doDeltaX : type == 1;
+}
+
+void Sprite::setDoDeltaY(int type) {
+ // Clear, set or toggle
+ _doDeltaY = type == 2 ? !_doDeltaY : type == 1;
+}
+
+bool Sprite::isPointInside(int16 x, int16 y) {
+ return x >= _collisionBounds.x1 && x <= _collisionBounds.x2 && y >= _collisionBounds.y1 && y <= _collisionBounds.y2;
+}
+
+bool Sprite::checkCollision(NRect &rect) {
+ return (_collisionBounds.x1 < rect.x2) && (rect.x1 < _collisionBounds.x2) && (_collisionBounds.y1 < rect.y2) && (rect.y1 < _collisionBounds.y2);
+}
+
+uint32 Sprite::handleMessage(int messageNum, const MessageParam &param, Entity *sender) {
+ return 0;
+}
+
+void Sprite::loadDataResource(uint32 fileHash) {
+ _dataResource.load(fileHash);
+}
+
+void Sprite::createSurface(int surfacePriority, int16 width, int16 height) {
+ _surface = new BaseSurface(_vm, surfacePriority, width, height);
+}
+
+int16 Sprite::defFilterY(int16 y) {
+ return y - _vm->_screen->getYOffset();
+}
+
+void Sprite::setClipRect(int16 x1, int16 y1, int16 x2, int16 y2) {
+ NRect &clipRect = _surface->getClipRect();
+ clipRect.x1 = x1;
+ clipRect.y1 = y1;
+ clipRect.x2 = x2;
+ clipRect.y2 = y2;
+}
+
+void Sprite::setClipRect(NRect& clipRect) {
+ _surface->getClipRect() = clipRect;
+}
+
+void Sprite::setClipRect(NDrawRect& drawRect) {
+ setClipRect(drawRect.x, drawRect.y, drawRect.x2(), drawRect.y2());
+}
+
+// StaticSprite
+
+StaticSprite::StaticSprite(NeverhoodEngine *vm, int objectPriority)
+ : Sprite(vm, objectPriority), _spriteResource(vm) {
+
+}
+
+StaticSprite::StaticSprite(NeverhoodEngine *vm, uint32 fileHash, int surfacePriority, int16 x, int16 y)
+ : Sprite(vm, 0), _spriteResource(vm) {
+
+ _spriteResource.load(fileHash, true);
+ createSurface(surfacePriority, _spriteResource.getDimensions().width, _spriteResource.getDimensions().height);
+ _x = x == kDefPosition ? _spriteResource.getPosition().x : x;
+ _y = y == kDefPosition ? _spriteResource.getPosition().y : y;
+ _drawOffset.set(0, 0, _spriteResource.getDimensions().width, _spriteResource.getDimensions().height);
+ _needRefresh = true;
+ updatePosition();
+}
+
+void StaticSprite::loadSprite(uint32 fileHash, uint flags, int surfacePriority, int16 x, int16 y) {
+ _spriteResource.load(fileHash, true);
+ if (!_surface)
+ createSurface(surfacePriority, _spriteResource.getDimensions().width, _spriteResource.getDimensions().height);
+ if (flags & kSLFDefDrawOffset)
+ _drawOffset.set(0, 0, _spriteResource.getDimensions().width, _spriteResource.getDimensions().height);
+ else if (flags & kSLFCenteredDrawOffset)
+ _drawOffset.set(-(_spriteResource.getDimensions().width / 2), -(_spriteResource.getDimensions().height / 2),
+ _spriteResource.getDimensions().width, _spriteResource.getDimensions().height);
+ if (flags & kSLFDefPosition) {
+ _x = _spriteResource.getPosition().x;
+ _y = _spriteResource.getPosition().y;
+ } else if (flags & kSLFSetPosition) {
+ _x = x;
+ _y = y;
+ }
+ if (flags & kSLFDefCollisionBoundsOffset) {
+ _collisionBoundsOffset = _drawOffset;
+ updateBounds();
+ }
+ _needRefresh = true;
+ updatePosition();
+}
+
+void StaticSprite::updatePosition() {
+
+ if (!_surface)
+ return;
+
+ if (_doDeltaX) {
+ _surface->getDrawRect().x = filterX(_x - _drawOffset.x - _drawOffset.width + 1);
+ } else {
+ _surface->getDrawRect().x = filterX(_x + _drawOffset.x);
+ }
+
+ if (_doDeltaY) {
+ _surface->getDrawRect().y = filterY(_y - _drawOffset.y - _drawOffset.height + 1);
+ } else {
+ _surface->getDrawRect().y = filterY(_y + _drawOffset.y);
+ }
+
+ if (_needRefresh) {
+ _surface->drawSpriteResourceEx(_spriteResource, _doDeltaX, _doDeltaY, _drawOffset.width, _drawOffset.height);
+ _needRefresh = false;
+ }
+
+}
+
+// AnimatedSprite
+
+AnimatedSprite::AnimatedSprite(NeverhoodEngine *vm, int objectPriority)
+ : Sprite(vm, objectPriority), _animResource(vm) {
+
+ init();
+}
+
+AnimatedSprite::AnimatedSprite(NeverhoodEngine *vm, uint32 fileHash, int surfacePriority, int16 x, int16 y)
+ : Sprite(vm, 1100), _animResource(vm) {
+
+ init();
+ SetUpdateHandler(&AnimatedSprite::update);
+ createSurface1(fileHash, surfacePriority);
+ _x = x;
+ _y = y;
+ startAnimation(fileHash, 0, -1);
+}
+
+void AnimatedSprite::init() {
+ _currFrameTicks = 0;
+ _newAnimFileHash = 0;
+ _deltaX = 0;
+ _deltaY = 0;
+ _nextAnimFileHash = 0;
+ _plFirstFrameIndex = 0;
+ _currFrameIndex = 0;
+ _currStickFrameIndex = -1;
+ _finalizeStateCb = NULL;
+ _currStateCb = NULL;
+ _nextStateCb = NULL;
+ _newStickFrameIndex = -1;
+ _newStickFrameHash = 0;
+ _frameChanged = false;
+ _replOldColor = 0;
+ _replNewColor = 0;
+ _animResource.setReplEnabled(false);
+ _playBackwards = false;
+}
+
+void AnimatedSprite::update() {
+ updateAnim();
+ handleSpriteUpdate();
+ updatePosition();
+}
+
+void AnimatedSprite::updateDeltaXY() {
+ if (_doDeltaX) {
+ _x -= _deltaX;
+ } else {
+ _x += _deltaX;
+ }
+ if (_doDeltaY) {
+ _y -= _deltaY;
+ } else {
+ _y += _deltaY;
+ }
+ _deltaX = 0;
+ _deltaY = 0;
+ updateBounds();
+}
+
+void AnimatedSprite::setRepl(byte oldColor, byte newColor) {
+ _replOldColor = oldColor;
+ _replNewColor = newColor;
+ _animResource.setReplEnabled(true);
+}
+
+void AnimatedSprite::clearRepl() {
+ _replOldColor = 0;
+ _replNewColor = 0;
+ _animResource.setReplEnabled(false);
+}
+
+void AnimatedSprite::updateAnim() {
+
+ _frameChanged = false;
+
+ if (_newAnimFileHash == 0) {
+ if (_newStickFrameIndex != -1) {
+ _currStickFrameIndex = _newStickFrameIndex == STICK_LAST_FRAME ? _animResource.getFrameCount() - 1 : _newStickFrameIndex;
+ _newStickFrameIndex = -1;
+ } else if (_newStickFrameHash != 0) {
+ _currStickFrameIndex = MAX<int16>(0, _animResource.getFrameIndex(_newStickFrameHash));
+ _newStickFrameHash = 0;
+ }
+ if (_newAnimFileHash == 0 && _currFrameIndex != _currStickFrameIndex) {
+ if (_currFrameTicks != 0 && (--_currFrameTicks == 0) && _animResource.getFrameCount() != 0) {
+
+ if (_nextAnimFileHash != 0) {
+ if (_animResource.load(_nextAnimFileHash)) {
+ _currAnimFileHash = _nextAnimFileHash;
+ } else {
+ _animResource.load(calcHash("sqDefault"));
+ _currAnimFileHash = 0;
+ }
+ if (_replOldColor != _replNewColor) {
+ _animResource.setRepl(_replOldColor, _replNewColor);
+ }
+ _nextAnimFileHash = 0;
+ if (_animStatus != 0) {
+ _currFrameIndex = _plFirstFrameHash != 0 ? MAX<int16>(0, _animResource.getFrameIndex(_plFirstFrameHash)) : 0;
+ _lastFrameIndex = _plLastFrameHash != 0 ? MAX<int16>(0, _animResource.getFrameIndex(_plLastFrameHash)) : _animResource.getFrameCount() - 1;
+ } else {
+ _currFrameIndex = _plFirstFrameIndex != -1 ? _plFirstFrameIndex : _animResource.getFrameCount() - 1;
+ _lastFrameIndex = _plLastFrameIndex != -1 ? _plLastFrameIndex : _animResource.getFrameCount() - 1;
+ }
+ } else {
+ updateFrameIndex();
+ }
+ if (_newAnimFileHash == 0)
+ updateFrameInfo();
+ }
+ }
+ }
+
+ if (_newAnimFileHash != 0) {
+ if (_animStatus == 2) {
+ _currStickFrameIndex = _currFrameIndex;
+ } else {
+ if (_animStatus == 1) {
+ if (_animResource.load(_newAnimFileHash)) {
+ _currAnimFileHash = _newAnimFileHash;
+ } else {
+ _animResource.load(calcHash("sqDefault"));
+ _currAnimFileHash = 0;
+ }
+ if (_replOldColor != _replNewColor) {
+ _animResource.setRepl(_replOldColor, _replNewColor);
+ }
+ _newAnimFileHash = 0;
+ _currFrameIndex = _plFirstFrameHash != 0 ? MAX<int16>(0, _animResource.getFrameIndex(_plFirstFrameHash)) : 0;
+ _lastFrameIndex = _plLastFrameHash != 0 ? MAX<int16>(0, _animResource.getFrameIndex(_plLastFrameHash)) : _animResource.getFrameCount() - 1;
+ } else {
+ if (_animResource.load(_newAnimFileHash)) {
+ _currAnimFileHash = _newAnimFileHash;
+ } else {
+ _animResource.load(calcHash("sqDefault"));
+ _currAnimFileHash = 0;
+ }
+ if (_replOldColor != _replNewColor) {
+ _animResource.setRepl(_replOldColor, _replNewColor);
+ }
+ _newAnimFileHash = 0;
+ _currFrameIndex = _plFirstFrameIndex != -1 ? _plFirstFrameIndex : _animResource.getFrameCount() - 1;
+ _lastFrameIndex = _plLastFrameIndex != -1 ? _plLastFrameIndex : _animResource.getFrameCount() - 1;
+ }
+ updateFrameInfo();
+ }
+
+ if (_newStickFrameIndex != -1) {
+ _currStickFrameIndex = _newStickFrameIndex == STICK_LAST_FRAME ? _animResource.getFrameCount() - 1 : _newStickFrameIndex;
+ _newStickFrameIndex = -1;
+ } else if (_newStickFrameHash != 0) {
+ _currStickFrameIndex = MAX<int16>(0, _animResource.getFrameIndex(_newStickFrameHash));
+ _newStickFrameHash = 0;
+ }
+
+ }
+
+}
+
+void AnimatedSprite::updatePosition() {
+
+ if (!_surface)
+ return;
+
+ if (_doDeltaX) {
+ _surface->getDrawRect().x = filterX(_x - _drawOffset.x - _drawOffset.width + 1);
+ } else {
+ _surface->getDrawRect().x = filterX(_x + _drawOffset.x);
+ }
+
+ if (_doDeltaY) {
+ _surface->getDrawRect().y = filterY(_y - _drawOffset.y - _drawOffset.height + 1);
+ } else {
+ _surface->getDrawRect().y = filterY(_y + _drawOffset.y);
+ }
+
+ if (_needRefresh) {
+ _surface->drawAnimResource(_animResource, _currFrameIndex, _doDeltaX, _doDeltaY, _drawOffset.width, _drawOffset.height);
+ _needRefresh = false;
+ }
+
+}
+
+void AnimatedSprite::updateFrameIndex() {
+ if (!_playBackwards) {
+ if (_currFrameIndex < _lastFrameIndex) {
+ _currFrameIndex++;
+ } else {
+ // Inform self about end of current animation
+ // The caller can then e.g. set a new animation fileHash
+ sendMessage(this, 0x3002, 0);
+ if (_newAnimFileHash == 0)
+ _currFrameIndex = 0;
+ }
+ } else {
+ if (_currFrameIndex > 0) {
+ _currFrameIndex--;
+ } else {
+ sendMessage(this, 0x3002, 0);
+ if (_newAnimFileHash == 0)
+ _currFrameIndex = _lastFrameIndex;
+ }
+ }
+}
+
+void AnimatedSprite::updateFrameInfo() {
+ debug(8, "AnimatedSprite::updateFrameInfo()");
+ const AnimFrameInfo &frameInfo = _animResource.getFrameInfo(_currFrameIndex);
+ _frameChanged = true;
+ _drawOffset = frameInfo.drawOffset;
+ _deltaX = frameInfo.deltaX;
+ _deltaY = frameInfo.deltaY;
+ _collisionBoundsOffset = frameInfo.collisionBoundsOffset;
+ _currFrameTicks = frameInfo.counter;
+ updateBounds();
+ _needRefresh = true;
+ if (frameInfo.frameHash != 0)
+ sendMessage(this, 0x100D, frameInfo.frameHash);
+}
+
+void AnimatedSprite::createSurface1(uint32 fileHash, int surfacePriority) {
+ NDimensions dimensions = _animResource.loadSpriteDimensions(fileHash);
+ _surface = new BaseSurface(_vm, surfacePriority, dimensions.width, dimensions.height);
+}
+
+void AnimatedSprite::createShadowSurface1(BaseSurface *shadowSurface, uint32 fileHash, int surfacePriority) {
+ NDimensions dimensions = _animResource.loadSpriteDimensions(fileHash);
+ _surface = new ShadowSurface(_vm, surfacePriority, dimensions.width, dimensions.height, shadowSurface);
+}
+
+void AnimatedSprite::createShadowSurface(BaseSurface *shadowSurface, int16 width, int16 height, int surfacePriority) {
+ _surface = new ShadowSurface(_vm, surfacePriority, width, height, shadowSurface);
+}
+
+void AnimatedSprite::startAnimation(uint32 fileHash, int16 plFirstFrameIndex, int16 plLastFrameIndex) {
+ debug(2, "AnimatedSprite::startAnimation(%08X, %d, %d)", fileHash, plFirstFrameIndex, plLastFrameIndex);
+ _newAnimFileHash = fileHash;
+ _plFirstFrameIndex = plFirstFrameIndex;
+ _plLastFrameIndex = plLastFrameIndex;
+ _newStickFrameHash = 0;
+ _animStatus = 0;
+ _playBackwards = false;
+ _newStickFrameIndex = -1;
+ _currStickFrameIndex = -1;
+}
+
+void AnimatedSprite::stopAnimation() {
+ _newAnimFileHash = 1;
+ _animStatus = 2;
+}
+
+void AnimatedSprite::startAnimationByHash(uint32 fileHash, uint32 plFirstFrameHash, uint32 plLastFrameHash) {
+ debug(2, "AnimatedSprite::startAnimationByHash(%08X, %08X, %08X)", fileHash, plFirstFrameHash, plLastFrameHash);
+ _newAnimFileHash = fileHash;
+ _plFirstFrameHash = plFirstFrameHash;
+ _plLastFrameHash = plLastFrameHash;
+ _newStickFrameHash = 0;
+ _animStatus = 1;
+ _playBackwards = false;
+ _newStickFrameIndex = -1;
+ _currStickFrameIndex = -1;
+}
+
+void AnimatedSprite::nextAnimationByHash(uint32 fileHash2, uint32 plFirstFrameHash, uint32 plLastFrameHash) {
+ _nextAnimFileHash = fileHash2;
+ _plFirstFrameHash = plFirstFrameHash;
+ _plLastFrameHash = plLastFrameHash;
+ _newStickFrameHash = 0;
+ _animStatus = 1;
+ _playBackwards = false;
+ _newStickFrameIndex = -1;
+ _currStickFrameIndex = -1;
+}
+
+void AnimatedSprite::setFinalizeState(AnimationCb finalizeStateCb) {
+ if (_finalizeStateCb)
+ (this->*_finalizeStateCb)();
+ _finalizeStateCb = finalizeStateCb;
+}
+
+void AnimatedSprite::gotoState(AnimationCb currStateCb) {
+ if (_finalizeStateCb) {
+ AnimationCb cb = _finalizeStateCb;
+ _finalizeStateCb = NULL;
+ (this->*cb)();
+ }
+ _nextStateCb = NULL;
+ _currStateCb = currStateCb;
+ if (_currStateCb)
+ (this->*_currStateCb)();
+}
+
+void AnimatedSprite::gotoNextState() {
+ if (_finalizeStateCb) {
+ AnimationCb cb = _finalizeStateCb;
+ _finalizeStateCb = NULL;
+ (this->*cb)();
+ }
+ if (_nextStateCb) {
+ _currStateCb = _nextStateCb;
+ _nextStateCb = NULL;
+ (this->*_currStateCb)();
+ } else {
+ _currStateCb = NULL;
+ }
+}
+
+} // End of namespace Neverhood
diff --git a/engines/neverhood/sprite.h b/engines/neverhood/sprite.h
new file mode 100644
index 0000000000..1d17bf0e70
--- /dev/null
+++ b/engines/neverhood/sprite.h
@@ -0,0 +1,192 @@
+/* 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_SPRITE_H
+#define NEVERHOOD_SPRITE_H
+
+#include "neverhood/neverhood.h"
+#include "neverhood/entity.h"
+#include "neverhood/graphics.h"
+#include "neverhood/resource.h"
+
+namespace Neverhood {
+
+#define SetSpriteUpdate(callback) \
+ do { \
+ _spriteUpdateCb = static_cast <void (Sprite::*)(void)> (callback); \
+ debug(2, "SetSpriteUpdate(" #callback ")"); \
+ _spriteUpdateCbName = #callback; \
+ } while (0)
+
+#define SetFilterX(callback) \
+ do { \
+ _filterXCb = static_cast <int16 (Sprite::*)(int16)> (callback); \
+ debug(2, "SetFilterX(" #callback ")"); \
+ } while (0)
+
+#define SetFilterY(callback) \
+ do { \
+ _filterYCb = static_cast <int16 (Sprite::*)(int16)> (callback); \
+ debug(2, "SetFilterY(" #callback ")"); \
+ } while (0)
+
+const int16 kDefPosition = -32768;
+
+class Sprite : public Entity {
+public:
+ Sprite(NeverhoodEngine *vm, int objectPriority);
+ ~Sprite();
+ void init() {}
+ BaseSurface *getSurface() { return _surface; }
+ void updateBounds();
+ void setDoDeltaX(int type);
+ void setDoDeltaY(int type);
+ bool isPointInside(int16 x, int16 y);
+ bool checkCollision(NRect &rect);
+ int16 getX() const { return _x; }
+ int16 getY() const { return _y; }
+ void setX(int16 value) { _x = value; }
+ void setY(int16 value) { _y = value; }
+ uint16 getFlags() const { return _flags; }
+ bool isDoDeltaX() const { return _doDeltaX; }
+ bool isDoDeltaY() const { return _doDeltaY; }
+ NRect& getCollisionBounds() { return _collisionBounds; }
+ uint32 handleMessage(int messageNum, const MessageParam &param, Entity *sender);
+ void loadDataResource(uint32 fileHash);
+ int16 defFilterY(int16 y);
+ bool getVisible() const { return _surface->getVisible(); }
+ void setVisible(bool value) { _surface->setVisible(value); }
+ NDrawRect& getDrawRect() { return _surface->getDrawRect(); }
+ // Some shortcuts to set the clipRect
+ NRect& getClipRect() { return _surface->getClipRect(); }
+ void setClipRect(int16 x1, int16 y1, int16 x2, int16 y2);
+ void setClipRect(NRect& clipRect);
+ void setClipRect(NDrawRect& drawRect);
+protected:
+ void (Sprite::*_spriteUpdateCb)();
+ Common::String _spriteUpdateCbName; // For debugging purposes
+ int16 (Sprite::*_filterXCb)(int16);
+ int16 (Sprite::*_filterYCb)(int16);
+ BaseSurface *_surface;
+ int16 _x, _y;
+ bool _doDeltaX, _doDeltaY;
+ bool _needRefresh;
+ NDrawRect _drawOffset;
+ NRect _collisionBounds;
+ NDrawRect _collisionBoundsOffset;
+ uint16 _flags;
+ DataResource _dataResource;
+ void createSurface(int surfacePriority, int16 width, int16 height);
+ void handleSpriteUpdate() {
+ if (_spriteUpdateCb)
+ (this->*_spriteUpdateCb)();
+ }
+ int16 filterX(int16 x) {
+ return _filterXCb ? (this->*_filterXCb)(x) : x;
+ }
+ int16 filterY(int16 y) {
+ return _filterYCb ? (this->*_filterYCb)(y) : y;
+ }
+};
+
+enum {
+ kSLFDefDrawOffset = 1 << 0,
+ kSLFCenteredDrawOffset = 1 << 1,
+ kSLFDefPosition = 1 << 2,
+ kSLFSetPosition = 1 << 3,
+ kSLFDefCollisionBoundsOffset = 1 << 4
+};
+
+class StaticSprite : public Sprite {
+public:
+ StaticSprite(NeverhoodEngine *vm, int objectPriority);
+ StaticSprite(NeverhoodEngine *vm, uint32 fileHash, int surfacePriority, int16 x = kDefPosition, int16 y = kDefPosition);
+ void loadSprite(uint32 fileHash, uint flags = 0, int surfacePriority = 0, int16 x = kDefPosition, int16 y = kDefPosition);
+ void updatePosition();
+protected:
+ SpriteResource _spriteResource;
+};
+
+#define AnimationCallback(callback) static_cast <void (AnimatedSprite::*)()> (callback)
+#define GotoState(callback) gotoState(static_cast <void (AnimatedSprite::*)()> (callback))
+#define NextState(callback) \
+ do { \
+ _nextStateCb = static_cast <void (AnimatedSprite::*)(void)> (callback); \
+ debug(2, "NextState(" #callback ")"); _nextStateCbName = #callback; \
+ } while (0)
+#define FinalizeState(callback) setFinalizeState(static_cast <void (AnimatedSprite::*)()> (callback));
+
+const int STICK_LAST_FRAME = -2;
+
+class AnimatedSprite : public Sprite {
+public:
+ AnimatedSprite(NeverhoodEngine *vm, int objectPriority);
+ AnimatedSprite(NeverhoodEngine *vm, uint32 fileHash, int surfacePriority, int16 x, int16 y);
+ void update();
+ void updateDeltaXY();
+ void setRepl(byte oldColor, byte newColor);
+ void clearRepl();
+ uint32 getCurrAnimFileHash() const { return _currAnimFileHash; }
+ int16 getFrameIndex() const { return _currFrameIndex; }
+ int16 getFrameIndex(uint32 frameHash) { return _animResource.getFrameIndex(frameHash); }
+ void setNewHashListIndex(int value) { _newStickFrameIndex = value; }
+ void startAnimation(uint32 fileHash, int16 plFirstFrameIndex, int16 plLastFrameIndex);
+protected:
+ typedef void (AnimatedSprite::*AnimationCb)();
+ AnimResource _animResource;
+ uint32 _currAnimFileHash, _newAnimFileHash, _nextAnimFileHash;
+ int16 _currFrameIndex, _lastFrameIndex;
+ int16 _plFirstFrameIndex, _plLastFrameIndex;
+ uint32 _plFirstFrameHash, _plLastFrameHash;
+ int16 _animStatus;
+ int16 _currFrameTicks;
+ int _currStickFrameIndex, _newStickFrameIndex;
+ uint32 _newStickFrameHash;
+ int16 _deltaX, _deltaY;
+ byte _replOldColor, _replNewColor;
+ bool _playBackwards, _frameChanged;
+ AnimationCb _finalizeStateCb;
+ AnimationCb _currStateCb;
+ AnimationCb _nextStateCb;
+ // For debugging purposes
+ Common::String _finalizeStateCbName;
+ Common::String _currStateCbName;
+ Common::String _nextStateCbName;
+ void init();
+ void updateAnim();
+ void updatePosition();
+ void updateFrameIndex();
+ void updateFrameInfo();
+ void createSurface1(uint32 fileHash, int surfacePriority);
+ void createShadowSurface1(BaseSurface *shadowSurface, uint32 fileHash, int surfacePriority);
+ void createShadowSurface(BaseSurface *shadowSurface, int16 width, int16 height, int surfacePriority);
+ void stopAnimation();
+ void startAnimationByHash(uint32 fileHash, uint32 plFirstFrameHash, uint32 plLastFrameHash);
+ void nextAnimationByHash(uint32 fileHash2, uint32 plFirstFrameHash, uint32 plLastFrameHash);
+ void setFinalizeState(AnimationCb finalizeStateCb);
+ void gotoState(AnimationCb currStateCb);
+ void gotoNextState();
+};
+
+} // End of namespace Neverhood
+
+#endif /* NEVERHOOD_SPRITE_H */
diff --git a/engines/neverhood/staticdata.cpp b/engines/neverhood/staticdata.cpp
new file mode 100644
index 0000000000..3f89c2236f
--- /dev/null
+++ b/engines/neverhood/staticdata.cpp
@@ -0,0 +1,200 @@
+/* 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/staticdata.h"
+
+namespace Neverhood {
+
+StaticData::StaticData() {
+}
+
+StaticData::~StaticData() {
+}
+
+void StaticData::load(const char *filename) {
+
+ Common::File fd;
+
+ if (!fd.open(filename))
+ error("StaticData::load() Could not open %s", filename);
+
+ fd.readUint32LE(); // magic
+ fd.readUint32LE(); // version
+
+ // Load message lists
+ uint32 messageListsCount = fd.readUint32LE();
+ debug(3, "messageListsCount: %d", messageListsCount);
+ for (uint32 i = 0; i < messageListsCount; i++) {
+ MessageList *messageList = new MessageList();
+ uint32 id = fd.readUint32LE();
+ uint32 itemCount = fd.readUint32LE();
+ for (uint32 itemIndex = 0; itemIndex < itemCount; itemIndex++) {
+ MessageItem messageItem;
+ messageItem.messageNum = fd.readUint16LE();
+ messageItem.messageValue = fd.readUint32LE();
+ messageList->push_back(messageItem);
+ }
+ _messageLists[id] = messageList;
+ }
+
+ // Load rect lists
+ uint32 rectListsCount = fd.readUint32LE();
+ debug(3, "rectListsCount: %d", rectListsCount);
+ for (uint32 i = 0; i < rectListsCount; i++) {
+ RectList *rectList = new RectList();
+ uint32 id = fd.readUint32LE();
+ uint32 itemCount = fd.readUint32LE();
+ for (uint32 itemIndex = 0; itemIndex < itemCount; itemIndex++) {
+ RectItem rectItem;
+ rectItem.rect.x1 = fd.readUint16LE();
+ rectItem.rect.y1 = fd.readUint16LE();
+ rectItem.rect.x2 = fd.readUint16LE();
+ rectItem.rect.y2 = fd.readUint16LE();
+ uint32 subItemCount = fd.readUint32LE();
+ rectItem.subRects.reserve(subItemCount);
+ for (uint32 subItemIndex = 0; subItemIndex < subItemCount; subItemIndex++) {
+ SubRectItem subRectItem;
+ subRectItem.rect.x1 = fd.readUint16LE();
+ subRectItem.rect.y1 = fd.readUint16LE();
+ subRectItem.rect.x2 = fd.readUint16LE();
+ subRectItem.rect.y2 = fd.readUint16LE();
+ subRectItem.messageListId = fd.readUint32LE();
+ rectItem.subRects.push_back(subRectItem);
+ }
+ rectList->push_back(rectItem);
+ }
+ _rectLists[id] = rectList;
+ }
+
+ // Load hit rects
+ uint32 hitRectListsCount = fd.readUint32LE();
+ debug(3, "hitRectListsCount: %d", hitRectListsCount);
+ for (uint32 i = 0; i < hitRectListsCount; i++) {
+ HitRectList *hitRectList = new HitRectList();
+ uint32 id = fd.readUint32LE();
+ uint32 itemCount = fd.readUint32LE();
+ for (uint32 itemIndex = 0; itemIndex < itemCount; itemIndex++) {
+ HitRect hitRect;
+ hitRect.rect.x1 = fd.readUint16LE();
+ hitRect.rect.y1 = fd.readUint16LE();
+ hitRect.rect.x2 = fd.readUint16LE();
+ hitRect.rect.y2 = fd.readUint16LE();
+ hitRect.type = fd.readUint16LE();
+ hitRectList->push_back(hitRect);
+ }
+ _hitRectLists[id] = hitRectList;
+ }
+
+ // Load navigation lists
+ uint32 navigationListsCount = fd.readUint32LE();
+ debug(3, "navigationListsCount: %d", navigationListsCount);
+ for (uint32 i = 0; i < navigationListsCount; i++) {
+ NavigationList *navigationList = new NavigationList();
+ uint32 id = fd.readUint32LE();
+ uint32 itemCount = fd.readUint32LE();
+ for (uint32 itemIndex = 0; itemIndex < itemCount; itemIndex++) {
+ NavigationItem navigationItem;
+ navigationItem.fileHash = fd.readUint32LE();
+ navigationItem.leftSmackerFileHash = fd.readUint32LE();
+ navigationItem.rightSmackerFileHash = fd.readUint32LE();
+ navigationItem.middleSmackerFileHash = fd.readUint32LE();
+ navigationItem.interactive = fd.readByte();
+ navigationItem.middleFlag = fd.readByte();
+ navigationItem.mouseCursorFileHash = fd.readUint32LE();
+ navigationList->push_back(navigationItem);
+ }
+ _navigationLists[id] = navigationList;
+ }
+
+ // Load HallOfRecordsInfo items
+ uint32 hallOfRecordsInfoItemsCount = fd.readUint32LE();
+ debug(3, "hallOfRecordsInfoItemsCount: %d", hallOfRecordsInfoItemsCount);
+ for (uint32 i = 0; i < hallOfRecordsInfoItemsCount; i++) {
+ HallOfRecordsInfo *hallOfRecordsInfo = new HallOfRecordsInfo();
+ uint32 id = fd.readUint32LE();
+ hallOfRecordsInfo->bgFilename1 = fd.readUint32LE();
+ hallOfRecordsInfo->bgFilename2 = fd.readUint32LE();
+ hallOfRecordsInfo->txFilename = fd.readUint32LE();
+ hallOfRecordsInfo->bgFilename3 = fd.readUint32LE();
+ hallOfRecordsInfo->xPosIndex = fd.readByte();
+ hallOfRecordsInfo->count = fd.readByte();
+ _hallOfRecordsInfoItems[id] = hallOfRecordsInfo;
+ }
+
+ // Load TrackInfo items
+ uint32 trackInfoItemsCount = fd.readUint32LE();
+ debug(3, "trackInfoItemsCount: %d", trackInfoItemsCount);
+ for (uint32 i = 0; i < trackInfoItemsCount; i++) {
+ TrackInfo *trackInfo = new TrackInfo();
+ uint32 id = fd.readUint32LE();
+ trackInfo->bgFilename = fd.readUint32LE();
+ trackInfo->bgShadowFilename = fd.readUint32LE();
+ trackInfo->dataResourceFilename = fd.readUint32LE();
+ trackInfo->trackPointsName = fd.readUint32LE();
+ trackInfo->rectListName = fd.readUint32LE();
+ trackInfo->exPaletteFilename2 = fd.readUint32LE();
+ trackInfo->exPaletteFilename1 = fd.readUint32LE();
+ trackInfo->mouseCursorFilename = fd.readUint32LE();
+ trackInfo->which1 = fd.readUint16LE();
+ trackInfo->which2 = fd.readUint16LE();
+ _trackInfoItems[id] = trackInfo;
+ }
+
+}
+
+HitRectList *StaticData::getHitRectList(uint32 id) {
+ if (!_hitRectLists[id])
+ error("StaticData::getHitRectList() HitRectList with id %08X not found", id);
+ return _hitRectLists[id];
+}
+
+RectList *StaticData::getRectList(uint32 id) {
+ if (!_rectLists[id])
+ error("StaticData::getRectList() RectList with id %08X not found", id);
+ return _rectLists[id];
+}
+
+MessageList *StaticData::getMessageList(uint32 id) {
+ if (!_messageLists[id])
+ error("StaticData::getMessageList() MessageList with id %08X not found", id);
+ return _messageLists[id];
+}
+
+NavigationList *StaticData::getNavigationList(uint32 id) {
+ if (!_navigationLists[id])
+ error("StaticData::getNavigationList() NavigationList with id %08X not found", id);
+ return _navigationLists[id];
+}
+
+HallOfRecordsInfo *StaticData::getHallOfRecordsInfoItem(uint32 id) {
+ if (!_hallOfRecordsInfoItems[id])
+ error("StaticData::getHallOfRecordsInfoItem() HallOfRecordsInfo with id %08X not found", id);
+ return _hallOfRecordsInfoItems[id];
+}
+
+TrackInfo *StaticData::getTrackInfo(uint32 id) {
+ if (!_trackInfoItems[id])
+ error("StaticData::getTrackInfo() TrackInfo with id %08X not found", id);
+ return _trackInfoItems[id];
+}
+
+} // End of namespace Neverhood
diff --git a/engines/neverhood/staticdata.h b/engines/neverhood/staticdata.h
new file mode 100644
index 0000000000..b1cab3bfcd
--- /dev/null
+++ b/engines/neverhood/staticdata.h
@@ -0,0 +1,116 @@
+/* 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_STATICDATA_H
+#define NEVERHOOD_STATICDATA_H
+
+#include "common/array.h"
+#include "common/hashmap.h"
+#include "neverhood/neverhood.h"
+#include "neverhood/graphics.h"
+
+namespace Neverhood {
+
+struct HitRect {
+ NRect rect;
+ uint16 type;
+};
+
+typedef Common::Array<HitRect> HitRectList;
+
+struct SubRectItem {
+ NRect rect;
+ uint32 messageListId;
+};
+
+struct RectItem {
+ NRect rect;
+ Common::Array<SubRectItem> subRects;
+};
+
+typedef Common::Array<RectItem> RectList;
+
+struct MessageItem {
+ uint32 messageNum;
+ uint32 messageValue;
+};
+
+typedef Common::Array<MessageItem> MessageList;
+
+struct NavigationItem {
+ uint32 fileHash;
+ uint32 leftSmackerFileHash;
+ uint32 rightSmackerFileHash;
+ uint32 middleSmackerFileHash;
+ byte interactive;
+ byte middleFlag;
+ uint32 mouseCursorFileHash;
+};
+
+typedef Common::Array<NavigationItem> NavigationList;
+
+struct HallOfRecordsInfo {
+ uint32 bgFilename1;
+ uint32 bgFilename2;
+ uint32 txFilename;
+ uint32 bgFilename3;
+ byte xPosIndex;
+ byte count;
+};
+
+struct TrackInfo {
+ uint32 id;
+ uint32 bgFilename;
+ uint32 bgShadowFilename;
+ uint32 dataResourceFilename;
+ uint32 trackPointsName;
+ uint32 rectListName;
+ uint32 exPaletteFilename2;
+ uint32 exPaletteFilename1;
+ uint32 mouseCursorFilename;
+ int16 which1;
+ int16 which2;
+};
+
+class StaticData {
+public:
+ StaticData();
+ ~StaticData();
+ void load(const char *filename);
+ HitRectList *getHitRectList(uint32 id);
+ RectList *getRectList(uint32 id);
+ MessageList *getMessageList(uint32 id);
+ NavigationList *getNavigationList(uint32 id);
+ HallOfRecordsInfo *getHallOfRecordsInfoItem(uint32 id);
+ TrackInfo *getTrackInfo(uint32 id);
+protected:
+ Common::HashMap<uint32, HitRectList*> _hitRectLists;
+ Common::HashMap<uint32, RectList*> _rectLists;
+ Common::HashMap<uint32, MessageList*> _messageLists;
+ Common::HashMap<uint32, NavigationList*> _navigationLists;
+ Common::HashMap<uint32, HallOfRecordsInfo*> _hallOfRecordsInfoItems;
+ Common::HashMap<uint32, TrackInfo*> _trackInfoItems;
+};
+
+} // End of namespace Neverhood
+
+#endif /* NEVERHOOD_STATICDATA_H */
diff --git a/engines/neverhood/todo.txt b/engines/neverhood/todo.txt
new file mode 100644
index 0000000000..9d781e06ec
--- /dev/null
+++ b/engines/neverhood/todo.txt
@@ -0,0 +1,45 @@
+NOTE:
+-------
+Some of the TODOs should be done AFTER the whole game logic is implemented
+else the game disasm and reimplemtation code become even more different
+(unless I decide it's ok to do it :)
+
+TODOs which can be done any time:
+-----------------------------------
+- Cleanup
+- Clean up staticdata structs to look more like the ones in create_neverhood
+ (e.g. by using template classes etc.)
+ - Or use a common base class and manage all stuff in one single table and cast stuff accordingly
+
+TODOs which should be done only after the game logic is finished:
+-------------------------------------------------------------------
+- Maybe rework organization of files (e.g. put ALL Sprites into one separate file, same with Modules and Scenes)
+ - This would solve the problem of how to organize stuff which is used several times, and less headers would have to be included
+ - The move special scenes (SmackerScene) into the scenes file
+
+DONE:
+-------
+- Implement game menus
+- Rework sound system (I don't like that SoundResources need to be explicitly initialized in Scene constructors)
+ - Should be just a handle object which initializes itself
+ - Play routine should fill the handle so it can be stopped/queried later
+ - Basically like ScummVM own sound handles
+- RE and implement yet unknown music/sound stuff
+- Implement clever sprite redrawing code (dirty rectangles, microtiles etc.), only redraw what's neccessary
+- Rework the resource system
+ - The current system can be simplified a lot
+ - Also resource purging needs to be implemented
+- Maybe merge CollisionMan with Scene (since it's so far never used independently)
+- Give placeholder stuff (e.g. sub?????, _flag??? etc.) better fitting names
+- Use CursorMan for the mouse cursor (instead of using it like a normal sprite)
+ - This whould make it neccessary to call _system->updateScreen more often else
+ the mouse movement would be choppy
+
+TODOs which are experimental:
+-------------------------------
+NOTE: Since they affect the whole game, they really should be only implemented once the full game logic is implemented.
+These are nothing more than wild ideas for now, any might never be implemented.
+- Use states instead of separate callback methods
+- Try to move more stuff to neverhood.dat
+- Try to use more template functions instead of manually creating functions
+ (Can be coupled with the above to move parameters to the dat and only use IDs)
diff --git a/engines/pegasus/neighborhood/norad/delta/globegame.cpp b/engines/pegasus/neighborhood/norad/delta/globegame.cpp
index 35ec9b813d..1416c51c8d 100644
--- a/engines/pegasus/neighborhood/norad/delta/globegame.cpp
+++ b/engines/pegasus/neighborhood/norad/delta/globegame.cpp
@@ -64,6 +64,10 @@ void GlobeTracker::setTrackParameters(const Hotspot *trackSpot, GlobeTrackDirect
_globeMovie->setSegment(start, start + kDurationPerRow);
+ // Clip new time so we don't go past the end of the segment
+ if (newTime >= start + kDurationPerRow)
+ newTime = start + kDurationPerRow - 1;
+
if (newTime != time) {
_globeMovie->setTime(newTime);
_globeMovie->redrawMovieWorld();
@@ -84,6 +88,10 @@ void GlobeTracker::setTrackParameters(const Hotspot *trackSpot, GlobeTrackDirect
_globeMovie->setSegment(start, start + kDurationPerRow);
+ // Clip new time so we don't go past the end of the segment
+ if (newTime >= start + kDurationPerRow)
+ newTime = start + kDurationPerRow - 1;
+
if (newTime != time) {
_globeMovie->setTime(newTime);
_globeMovie->redrawMovieWorld();
diff --git a/engines/plugins_table.h b/engines/plugins_table.h
index e5ac5efeb4..44979458ca 100644
--- a/engines/plugins_table.h
+++ b/engines/plugins_table.h
@@ -56,6 +56,9 @@ LINK_PLUGIN(MADE)
#if PLUGIN_ENABLED_STATIC(MOHAWK)
LINK_PLUGIN(MOHAWK)
#endif
+#if PLUGIN_ENABLED_STATIC(NEVERHOOD)
+LINK_PLUGIN(NEVERHOOD)
+#endif
#if PLUGIN_ENABLED_STATIC(PARALLACTION)
LINK_PLUGIN(PARALLACTION)
#endif
diff --git a/engines/saga/events.cpp b/engines/saga/events.cpp
index d98cef0740..c5842f4998 100644
--- a/engines/saga/events.cpp
+++ b/engines/saga/events.cpp
@@ -533,6 +533,7 @@ int Events::handleOneShot(Event *event) {
default:
break;
}
+ break;
#ifdef ENABLE_IHNM
case kCutawayEvent:
switch (event->op) {
@@ -545,6 +546,7 @@ int Events::handleOneShot(Event *event) {
default:
break;
}
+ break;
#endif
case kActorEvent:
switch (event->op) {
@@ -554,6 +556,7 @@ int Events::handleOneShot(Event *event) {
default:
break;
}
+ break;
default:
break;
}
diff --git a/engines/sci/detection_tables.h b/engines/sci/detection_tables.h
index fa7acaf4b8..66164a1937 100644
--- a/engines/sci/detection_tables.h
+++ b/engines/sci/detection_tables.h
@@ -663,6 +663,14 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::EN_ANY, Common::kPlatformDOS, ADGF_DEMO, GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ // Gabriel Knight - English DOS CD Demo (from DrMcCoy)
+ // SCI interpreter version 1.001.092
+ {"gk1", "CD Demo", {
+ {"resource.map", 0, "8cad2a256f41463030cbb7ea1bfb2857", 2490},
+ {"resource.000", 0, "eb3ed7477ca4110813fe1fcf35928561", 1718450},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformDOS, ADGF_DEMO, GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+
#ifdef ENABLE_SCI32
// Gabriel Knight - English DOS Floppy
// SCI interpreter version 2.000.000
@@ -1623,6 +1631,14 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::EN_ANY, Common::kPlatformDOS, ADGF_DEMO | ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ // King's Quest 7 - English Windows Demo (from DrMcCoy)
+ // SCI interpreter version 2.100.002
+ {"kq7", "Demo", {
+ {"resource.map", 0, "38e627a37a975aea40cc72b0518b0709", 18412},
+ {"resource.000", 0, "bad61d50aaa64298fa57a7c6ccd3bccf", 84020382},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_DEMO | ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+
#endif // ENABLE_SCI32
// Laura Bow - English Amiga
diff --git a/engines/sci/engine/workarounds.cpp b/engines/sci/engine/workarounds.cpp
index 6af6326042..6f0b34b457 100644
--- a/engines/sci/engine/workarounds.cpp
+++ b/engines/sci/engine/workarounds.cpp
@@ -249,6 +249,7 @@ const SciWorkaroundEntry kDoSoundFade_workarounds[] = {
{ GID_KQ6, 105, 989, 0, "globalSound", "fade", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // floppy: during intro, parameter 4 is an object
{ GID_KQ6, 460, 989, 0, "globalSound2", "fade", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // after pulling the black widow's web on the isle of wonder, parameter 4 is an object - bug #3034567
{ GID_QFG4, -1, 64989, 0, "longSong", "fade", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // CD version: many places, parameter 4 is an object (longSong)
+ { GID_SQ5, 800, 989, 0, "sq5Music1", "fade", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // when cutting the wrong part of Goliath with the laser - bug #3614145
SCI_WORKAROUNDENTRY_TERMINATOR
};
diff --git a/engines/sky/music/musicbase.cpp b/engines/sky/music/musicbase.cpp
index b388afdb13..944edb1fdc 100644
--- a/engines/sky/music/musicbase.cpp
+++ b/engines/sky/music/musicbase.cpp
@@ -44,9 +44,9 @@ MusicBase::~MusicBase() {
}
void MusicBase::loadSection(uint8 pSection) {
- Common::StackLock lock(_mutex);
if (_currentMusic)
stopMusicInternal();
+ Common::StackLock lock(_mutex);
free(_musicData);
_currentSection = pSection;
_musicData = _skyDisk->loadFile(_driverFileBase + FILES_PER_SECTION * pSection);
@@ -70,13 +70,14 @@ bool MusicBase::musicIsPlaying() {
}
void MusicBase::stopMusic() {
- Common::StackLock lock(_mutex);
stopMusicInternal();
}
void MusicBase::stopMusicInternal() {
_mixer->stopHandle(_musicHandle);
+ Common::StackLock lock(_mutex);
+
for (uint8 cnt = 0; cnt < _numberOfChannels; cnt++)
delete _channels[cnt];
_numberOfChannels = 0;
diff --git a/engines/sword1/animation.cpp b/engines/sword1/animation.cpp
index ff3c897dba..206bd68b07 100644
--- a/engines/sword1/animation.cpp
+++ b/engines/sword1/animation.cpp
@@ -98,7 +98,7 @@ static const char *const sequenceListPSX[20] = {
///////////////////////////////////////////////////////////////////////////////
MoviePlayer::MoviePlayer(SwordEngine *vm, Text *textMan, ResMan *resMan, OSystem *system, Video::VideoDecoder *decoder, DecoderType decoderType)
- : _vm(vm), _textMan(textMan), _resMan(resMan), _system(system) {
+ : _vm(vm), _textMan(textMan), _resMan(resMan), _system(system), _textX(0), _textY(0), _textWidth(0), _textHeight(0), _textColor(1) {
_decoderType = decoderType;
_decoder = decoder;
diff --git a/engines/sword1/sound.cpp b/engines/sword1/sound.cpp
index 61bf5257ab..268da74508 100644
--- a/engines/sword1/sound.cpp
+++ b/engines/sword1/sound.cpp
@@ -45,9 +45,8 @@ namespace Sword1 {
#define SOUND_SPEECH_ID 1
#define SPEECH_FLAGS (Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN)
-Sound::Sound(const char *searchPath, Audio::Mixer *mixer, ResMan *pResMan)
+Sound::Sound(Audio::Mixer *mixer, ResMan *pResMan)
: _rnd("sword1sound") {
- strcpy(_filePath, searchPath);
_mixer = mixer;
_resMan = pResMan;
_bigEndianSpeech = false;
diff --git a/engines/sword1/sound.h b/engines/sword1/sound.h
index 4e1ac7ba34..b52d89f390 100644
--- a/engines/sword1/sound.h
+++ b/engines/sword1/sound.h
@@ -80,7 +80,7 @@ enum CowMode {
class Sound {
public:
- Sound(const char *searchPath, Audio::Mixer *mixer, ResMan *pResMan);
+ Sound(Audio::Mixer *mixer, ResMan *pResMan);
~Sound();
void setSpeechVol(uint8 volL, uint8 volR) { _speechVolL = volL; _speechVolR = volR; }
void setSfxVol(uint8 volL, uint8 volR) { _sfxVolL = volL; _sfxVolR = volR; }
@@ -125,7 +125,6 @@ private:
Audio::Mixer *_mixer;
ResMan *_resMan;
bool _bigEndianSpeech;
- char _filePath[100];
static const char _musicList[270];
static const uint16 _roomsFixedFx[TOTAL_ROOMS][TOTAL_FX_PER_ROOM];
static const FxDef _fxList[312];
diff --git a/engines/sword1/sword1.cpp b/engines/sword1/sword1.cpp
index fa593b8df4..2d5452778d 100644
--- a/engines/sword1/sword1.cpp
+++ b/engines/sword1/sword1.cpp
@@ -104,7 +104,7 @@ Common::Error SwordEngine::init() {
_mouse = new Mouse(_system, _resMan, _objectMan);
_screen = new Screen(_system, _resMan, _objectMan);
_music = new Music(_mixer);
- _sound = new Sound("", _mixer, _resMan);
+ _sound = new Sound(_mixer, _resMan);
_menu = new Menu(_screen, _mouse);
_logic = new Logic(this, _objectMan, _resMan, _screen, _mouse, _sound, _music, _menu, _system, _mixer);
_mouse->useLogicAndMenu(_logic, _menu);
diff --git a/engines/toltecs/movie.cpp b/engines/toltecs/movie.cpp
index 20a4bdfb72..341da7e5f3 100644
--- a/engines/toltecs/movie.cpp
+++ b/engines/toltecs/movie.cpp
@@ -45,7 +45,7 @@ enum ChunkTypes {
kChunkStopSubtitles = 8
};
-MoviePlayer::MoviePlayer(ToltecsEngine *vm) : _vm(vm), _isPlaying(false) {
+MoviePlayer::MoviePlayer(ToltecsEngine *vm) : _vm(vm), _isPlaying(false), _lastPrefetchOfs(0), _framesPerSoundChunk(0), _endPos(0) {
}
MoviePlayer::~MoviePlayer() {
@@ -69,9 +69,10 @@ void MoviePlayer::playMovie(uint resIndex) {
_vm->_screen->finishTalkTextItems();
_vm->_arc->openResource(resIndex);
+ _endPos = _vm->_arc->pos() + _vm->_arc->getResourceSize(resIndex);
- _frameCount = _vm->_arc->readUint32LE();
- _chunkCount = _vm->_arc->readUint32LE();
+ /*_frameCount = */_vm->_arc->readUint32LE();
+ uint32 chunkCount = _vm->_arc->readUint32LE();
// TODO: Figure out rest of the header
_vm->_arc->readUint32LE();
@@ -90,7 +91,6 @@ void MoviePlayer::playMovie(uint resIndex) {
_vm->_mixer->playStream(Audio::Mixer::kPlainSoundType, &_audioStreamHandle, _audioStream);
- _soundChunkFramesLeft = 0;
_lastPrefetchOfs = 0;
fetchAudioChunks();
@@ -99,8 +99,9 @@ void MoviePlayer::playMovie(uint resIndex) {
uint32 chunkBufferSize = 0;
uint32 frame = 0;
bool abortMovie = false;
+ uint32 soundChunkFramesLeft = 0;
- while (_chunkCount-- && !abortMovie) {
+ while (chunkCount-- && !abortMovie) {
byte chunkType = _vm->_arc->readByte();
uint32 chunkSize = _vm->_arc->readUint32LE();
@@ -110,6 +111,7 @@ void MoviePlayer::playMovie(uint resIndex) {
// fetchAudioChunks()
if (chunkType == kChunkAudio) {
_vm->_arc->skip(chunkSize);
+ soundChunkFramesLeft += _framesPerSoundChunk;
} else {
// Only reallocate the chunk buffer if the new chunk is bigger
if (chunkSize > chunkBufferSize) {
@@ -127,8 +129,7 @@ void MoviePlayer::playMovie(uint resIndex) {
unpackRle(chunkBuffer, _vm->_screen->_backScreen);
_vm->_screen->_fullRefresh = true;
- _soundChunkFramesLeft--;
- if (_soundChunkFramesLeft <= _framesPerSoundChunk) {
+ if (--soundChunkFramesLeft <= _framesPerSoundChunk) {
fetchAudioChunks();
}
@@ -207,13 +208,12 @@ void MoviePlayer::playMovie(uint resIndex) {
void MoviePlayer::fetchAudioChunks() {
uint32 startOfs = _vm->_arc->pos();
- uint32 chunkCount = _chunkCount;
uint prefetchChunkCount = 0;
if (_lastPrefetchOfs != 0)
_vm->_arc->seek(_lastPrefetchOfs, SEEK_SET);
- while (chunkCount-- && prefetchChunkCount < _framesPerSoundChunk / 2) {
+ while (prefetchChunkCount < _framesPerSoundChunk / 2 && _vm->_arc->pos() < _endPos) {
byte chunkType = _vm->_arc->readByte();
uint32 chunkSize = _vm->_arc->readUint32LE();
if (chunkType == kChunkAudio) {
@@ -222,7 +222,6 @@ void MoviePlayer::fetchAudioChunks() {
_audioStream->queueBuffer(chunkBuffer, chunkSize, DisposeAfterUse::YES, Audio::FLAG_UNSIGNED);
chunkBuffer = NULL;
prefetchChunkCount++;
- _soundChunkFramesLeft += _framesPerSoundChunk;
} else {
_vm->_arc->seek(chunkSize, SEEK_CUR);
}
diff --git a/engines/toltecs/movie.h b/engines/toltecs/movie.h
index c1ed6d7ba0..aa28c83fef 100644
--- a/engines/toltecs/movie.h
+++ b/engines/toltecs/movie.h
@@ -46,8 +46,8 @@ protected:
bool _isPlaying;
- uint32 _chunkCount, _frameCount, _lastPrefetchOfs;
- uint32 _soundChunkFramesLeft, _framesPerSoundChunk;
+ uint32 _framesPerSoundChunk;
+ int32 _lastPrefetchOfs, _endPos;
void unpackPalette(byte *source, byte *dest, int elemCount, int elemSize);
void unpackRle(byte *source, byte *dest);
diff --git a/engines/wintermute/base/base_game.cpp b/engines/wintermute/base/base_game.cpp
index e97e342149..4d8e79b5c2 100644
--- a/engines/wintermute/base/base_game.cpp
+++ b/engines/wintermute/base/base_game.cpp
@@ -2558,7 +2558,7 @@ bool BaseGame::displayQuickMsg() {
// update
for (uint32 i = 0; i < _quickMessages.size(); i++) {
- if (_currentTime - _quickMessages[i]->_startTime >= QUICK_MSG_DURATION) {
+ if (_currentTime - _quickMessages[i]->getStartTime() >= QUICK_MSG_DURATION) {
delete _quickMessages[i];
_quickMessages.remove_at(i);
i--;
@@ -2583,7 +2583,7 @@ void BaseGame::quickMessage(const char *text) {
delete _quickMessages[0];
_quickMessages.remove_at(0);
}
- _quickMessages.add(new BaseQuickMsg(_gameRef, text));
+ _quickMessages.add(new BaseQuickMsg(_currentTime, text));
}
diff --git a/engines/wintermute/base/base_quick_msg.cpp b/engines/wintermute/base/base_quick_msg.cpp
index 50a9031eee..9f19dfd74a 100644
--- a/engines/wintermute/base/base_quick_msg.cpp
+++ b/engines/wintermute/base/base_quick_msg.cpp
@@ -27,14 +27,13 @@
*/
#include "engines/wintermute/base/base_quick_msg.h"
-#include "engines/wintermute/base/base_game.h"
namespace Wintermute {
//////////////////////////////////////////////////////////////////////////
-BaseQuickMsg::BaseQuickMsg(BaseGame *inGame, const char *text) {
+BaseQuickMsg::BaseQuickMsg(uint32 startTime, const char *text) {
_text = text;
- _startTime = _gameRef->_currentTime;
+ _startTime = startTime;
}
@@ -48,4 +47,9 @@ const char *BaseQuickMsg::getText() const {
return _text.c_str();
}
+//////////////////////////////////////////////////////////////////////////
+uint32 BaseQuickMsg::getStartTime() const {
+ return _startTime;
+}
+
} // end of namespace Wintermute
diff --git a/engines/wintermute/base/base_quick_msg.h b/engines/wintermute/base/base_quick_msg.h
index 0d342b3b12..377f7733fd 100644
--- a/engines/wintermute/base/base_quick_msg.h
+++ b/engines/wintermute/base/base_quick_msg.h
@@ -32,16 +32,16 @@
#include "common/str.h"
namespace Wintermute {
-class BaseGame;
+
class BaseQuickMsg {
public:
const char *getText() const;
- uint32 _startTime;
- BaseQuickMsg(BaseGame *inGame, const char *text);
+ uint32 getStartTime() const;
+ BaseQuickMsg(uint32 startTime, const char *text);
virtual ~BaseQuickMsg();
private:
- BaseGame *_gameRef;
Common::String _text;
+ uint32 _startTime;
};
} // end of namespace Wintermute
diff --git a/gui/browser.h b/gui/browser.h
index 7c098617bb..b82fe516f9 100644
--- a/gui/browser.h
+++ b/gui/browser.h
@@ -51,6 +51,7 @@ protected:
#ifdef MACOSX
const void *_titleRef;
const void *_chooseRef;
+ const void *_hiddenFilesRef;
#else
ListWidget *_fileList;
StaticTextWidget *_currentPath;
diff --git a/gui/browser_osx.mm b/gui/browser_osx.mm
index ecd60915f8..642718df94 100644
--- a/gui/browser_osx.mm
+++ b/gui/browser_osx.mm
@@ -30,10 +30,55 @@
#include "common/algorithm.h"
#include "common/translation.h"
+#include <AppKit/NSNibDeclarations.h>
#include <AppKit/NSOpenPanel.h>
+#include <AppKit/NSButton.h>
#include <Foundation/NSString.h>
#include <Foundation/NSURL.h>
+@interface ShowHiddenFilesController : NSObject {
+ NSOpenPanel* _panel;
+}
+
+- (id) init;
+- (void) dealloc;
+- (void) setOpenPanel : (NSOpenPanel*) panel;
+- (IBAction) showHiddenFiles : (id) sender;
+
+@end
+
+@implementation ShowHiddenFilesController
+
+- (id) init {
+ self = [super init];
+ _panel = 0;
+
+ return self;
+}
+
+- (void) dealloc {
+ [_panel release];
+ [super dealloc];
+}
+
+- (void) setOpenPanel : (NSOpenPanel*) panel {
+ _panel = panel;
+ [_panel retain];
+}
+
+
+- (IBAction) showHiddenFiles : (id) sender {
+ if ([sender state] == NSOnState) {
+ [_panel setShowsHiddenFiles: YES];
+ ConfMan.setBool("gui_browser_show_hidden", true, Common::ConfigManager::kApplicationDomain);
+ } else {
+ [_panel setShowsHiddenFiles: NO];
+ ConfMan.setBool("gui_browser_show_hidden", false, Common::ConfigManager::kApplicationDomain);
+ }
+}
+
+@end
+
namespace GUI {
BrowserDialog::BrowserDialog(const char *title, bool dirBrowser)
@@ -56,11 +101,13 @@ BrowserDialog::BrowserDialog(const char *title, bool dirBrowser)
// Convert button text to NSString
_chooseRef = CFStringCreateWithCString(0, _("Choose"), stringEncoding);
+ _hiddenFilesRef = CFStringCreateWithCString(0, _("Show hidden files"), stringEncoding);
}
BrowserDialog::~BrowserDialog() {
CFRelease(_titleRef);
CFRelease(_chooseRef);
+ CFRelease(_hiddenFilesRef);
}
int BrowserDialog::runModal() {
@@ -82,6 +129,29 @@ int BrowserDialog::runModal() {
[panel setCanChooseDirectories:_isDirBrowser];
[panel setTitle:(NSString *)_titleRef];
[panel setPrompt:(NSString *)_chooseRef];
+
+ NSButton *showHiddenFilesButton = 0;
+ ShowHiddenFilesController *showHiddenFilesController = 0;
+ if ([panel respondsToSelector:@selector(setShowsHiddenFiles:)]) {
+ showHiddenFilesButton = [[NSButton alloc] init];
+ [showHiddenFilesButton setButtonType:NSSwitchButton];
+ [showHiddenFilesButton setTitle:(NSString *)_hiddenFilesRef];
+ [showHiddenFilesButton sizeToFit];
+ if (ConfMan.getBool("gui_browser_show_hidden", Common::ConfigManager::kApplicationDomain)) {
+ [showHiddenFilesButton setState:NSOnState];
+ [panel setShowsHiddenFiles: YES];
+ } else {
+ [showHiddenFilesButton setState:NSOffState];
+ [panel setShowsHiddenFiles: NO];
+ }
+ [panel setAccessoryView:showHiddenFilesButton];
+
+ showHiddenFilesController = [[ShowHiddenFilesController alloc] init];
+ [showHiddenFilesController setOpenPanel:panel];
+ [showHiddenFilesButton setTarget:showHiddenFilesController];
+ [showHiddenFilesButton setAction:@selector(showHiddenFiles:)];
+ }
+
if ([panel runModal] == NSOKButton) {
NSURL *url = [panel URL];
if ([url isFileURL]) {
@@ -91,6 +161,9 @@ int BrowserDialog::runModal() {
}
}
+ [showHiddenFilesButton release];
+ [showHiddenFilesController release];
+
// If we were in fullscreen mode, switch back
if (wasFullscreen) {
g_system->beginGFXTransaction();
diff --git a/gui/themes/translations.dat b/gui/themes/translations.dat
index 6e9686d6c7..7bd1316208 100644
--- a/gui/themes/translations.dat
+++ b/gui/themes/translations.dat
Binary files differ
diff --git a/po/da_DA.po b/po/da_DA.po
index 4e2a06790d..bb7ed32c83 100644
--- a/po/da_DA.po
+++ b/po/da_DA.po
@@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: ScummVM 1.3.0svn\n"
"Report-Msgid-Bugs-To: scummvm-devel@lists.sf.net\n"
"POT-Creation-Date: 2013-04-24 13:35+0100\n"
-"PO-Revision-Date: 2012-07-09 20:27+0100\n"
+"PO-Revision-Date: 2013-05-17 14:24+0100\n"
"Last-Translator: Steffen Nyeland <steffen@nyeland.dk>\n"
"Language-Team: Steffen Nyeland <steffen@nyeland.dk>\n"
"Language: Dansk\n"
@@ -16,8 +16,7 @@ msgstr ""
"Content-Type: text/plain; charset=iso-8859-1\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-"X-Poedit-Language: Danish\n"
-"X-Poedit-Country: DENMARK\n"
+"X-Generator: Poedit 1.5.5\n"
#: gui/about.cpp:93
#, c-format
@@ -33,13 +32,12 @@ msgid "Available engines:"
msgstr "Tilgængelige \"motorer\":"
#: gui/browser.cpp:67
-#, fuzzy
msgid "Show hidden files"
-msgstr "Vis / Skjul konsol"
+msgstr "Vis skjulte filer"
#: gui/browser.cpp:67
msgid "Show files marked with the hidden attribute"
-msgstr ""
+msgstr "Vis filer markeret med skjult attribut"
#: gui/browser.cpp:71
msgid "Go up"
@@ -725,7 +723,7 @@ msgstr "MIDI lydstyrke:"
#: gui/options.cpp:873
msgid "FluidSynth Settings"
-msgstr ""
+msgstr "FluidSynth indstillinger"
#: gui/options.cpp:880
msgid "MT-32 Device:"
@@ -753,9 +751,8 @@ msgid "True Roland MT-32 (no GM emulation)"
msgstr "Ægte Roland MT-32 (ingen GM emulering)"
#: gui/options.cpp:890
-#, fuzzy
msgid "Roland GS Mode (disable GM mapping)"
-msgstr "Ægte Roland MT-32 (undlad GM emulering)"
+msgstr "Roland GS tilstand (undlad GM emulering)"
#: gui/options.cpp:890
msgid "Turns off General MIDI mapping for games with Roland MT-32 soundtrack"
@@ -941,11 +938,11 @@ msgstr ""
#: gui/saveload-dialog.cpp:166
msgid "List view"
-msgstr ""
+msgstr "Liste visning"
#: gui/saveload-dialog.cpp:167
msgid "Grid view"
-msgstr ""
+msgstr "Gitter visning"
#: gui/saveload-dialog.cpp:210 gui/saveload-dialog.cpp:358
msgid "No date saved"
@@ -985,31 +982,28 @@ msgstr "Unavngivet gemmetilstand"
#: gui/saveload-dialog.cpp:546
msgid "Next"
-msgstr ""
+msgstr "Næste"
#: gui/saveload-dialog.cpp:549
msgid "Prev"
-msgstr ""
+msgstr "Forrige"
#: gui/saveload-dialog.cpp:736
-#, fuzzy
msgid "New Save"
-msgstr "Gem"
+msgstr "Ny Gemmer"
#: gui/saveload-dialog.cpp:736
-#, fuzzy
msgid "Create a new save game"
-msgstr "Mislykkedes at gemme spil"
+msgstr "Opret en ny gemmer"
#: gui/saveload-dialog.cpp:865
-#, fuzzy
msgid "Name: "
msgstr "Navn:"
#: gui/saveload-dialog.cpp:937
#, c-format
msgid "Enter a description for slot %d:"
-msgstr ""
+msgstr "Indtast en beskrivelse af plads %d:"
#: gui/themebrowser.cpp:44
msgid "Select a Theme"
@@ -1045,93 +1039,91 @@ msgid "Clear value"
msgstr "Slet værdi"
#: gui/fluidsynth-dialog.cpp:67
-#, fuzzy
msgid "Reverb"
-msgstr "Aldrig"
+msgstr "Rumklang"
#: gui/fluidsynth-dialog.cpp:69 gui/fluidsynth-dialog.cpp:101
-#, fuzzy
msgid "Active"
-msgstr " (Aktiv)"
+msgstr "Aktiv"
#: gui/fluidsynth-dialog.cpp:71
msgid "Room:"
-msgstr ""
+msgstr "Rum:"
#: gui/fluidsynth-dialog.cpp:78
msgid "Damp:"
-msgstr ""
+msgstr "Dæmp:"
#: gui/fluidsynth-dialog.cpp:85
msgid "Width:"
-msgstr ""
+msgstr "Bredde:"
#: gui/fluidsynth-dialog.cpp:92 gui/fluidsynth-dialog.cpp:110
msgid "Level:"
-msgstr ""
+msgstr "Styrke:"
#: gui/fluidsynth-dialog.cpp:99
msgid "Chorus"
-msgstr ""
+msgstr "Kor"
#: gui/fluidsynth-dialog.cpp:103
msgid "N:"
-msgstr ""
+msgstr "N:"
#: gui/fluidsynth-dialog.cpp:117
-#, fuzzy
msgid "Speed:"
-msgstr "Tale"
+msgstr "Hastighed:"
#: gui/fluidsynth-dialog.cpp:124
msgid "Depth:"
-msgstr ""
+msgstr "Dybde:"
#: gui/fluidsynth-dialog.cpp:131
msgid "Type:"
-msgstr ""
+msgstr "Type:"
#: gui/fluidsynth-dialog.cpp:134
msgid "Sine"
-msgstr ""
+msgstr "Sinus"
#: gui/fluidsynth-dialog.cpp:135
msgid "Triangle"
-msgstr ""
+msgstr "Triangulær"
#: gui/fluidsynth-dialog.cpp:139
msgid "Interpolation:"
-msgstr ""
+msgstr "Interpolation:"
#: gui/fluidsynth-dialog.cpp:142
msgid "None (fastest)"
-msgstr ""
+msgstr "Ingen (hurtigst)"
#: gui/fluidsynth-dialog.cpp:143
msgid "Linear"
-msgstr ""
+msgstr "Lineær"
#: gui/fluidsynth-dialog.cpp:144
msgid "Fourth-order"
-msgstr ""
+msgstr "Fjerde-orden"
#: gui/fluidsynth-dialog.cpp:145
msgid "Seventh-order"
-msgstr ""
+msgstr "Syvende-orden"
#: gui/fluidsynth-dialog.cpp:149
msgid "Reset"
-msgstr ""
+msgstr "Nulstil"
#: gui/fluidsynth-dialog.cpp:149
msgid "Reset all FluidSynth settings to their default values."
-msgstr ""
+msgstr "Nulstil alle FluidSynth indstillinger til deres standard værdier."
#: gui/fluidsynth-dialog.cpp:216
-#, fuzzy
msgid ""
"Do you really want to reset all FluidSynth settings to their default values?"
-msgstr "Vil du virkelig gå tilbage til oversigten?"
+msgstr ""
+"Vil du virkelig nulstille alle FluidSynth indstillinger til deres standard "
+"værdier?"
#: base/main.cpp:210
#, c-format
@@ -1419,7 +1411,6 @@ msgid "Restore"
msgstr "Gendan"
#: engines/drascula/saveload.cpp:49
-#, fuzzy
msgid ""
"ScummVM found that you have old savefiles for Drascula that should be "
"converted.\n"
@@ -1429,7 +1420,7 @@ msgid ""
"Press OK to convert them now, otherwise you will be asked again the next "
"time you start the game.\n"
msgstr ""
-"ScummVM har konstateret, at du har gamle gemmer for Broken Sword 1, der skal "
+"ScummVM har konstateret, at du har gamle gemmer for Drascula, der skal "
"konverteres.\n"
"Det gamle gemte spil format understøttes ikke længere, så vil du ikke være i "
"stand til at indlæse dine spil, hvis du ikke konvertere dem.\n"
@@ -2244,13 +2235,12 @@ msgid "Failed to delete file."
msgstr "Mislykkedes at slette fil."
#: engines/groovie/detection.cpp:312
-#, fuzzy
msgid "Fast movie speed"
-msgstr "Hurtig tilstand"
+msgstr "Hurtig film hastighed"
#: engines/groovie/detection.cpp:313
msgid "Play movies at an increased speed"
-msgstr ""
+msgstr "Afspil film med forhøjet hastighed"
#: engines/groovie/script.cpp:420
msgid "Failed to save game"
@@ -2378,12 +2368,11 @@ msgstr ""
#: engines/queen/queen.cpp:59
msgid "Alternative intro"
-msgstr ""
+msgstr "Alternativ intro"
#: engines/queen/queen.cpp:60
-#, fuzzy
msgid "Use an alternative game intro (CD version only)"
-msgstr "Brug diskette versionens intro (kun CD version)"
+msgstr "Brug en alternativ spil intro (kun CD version)"
#: engines/sky/compact.cpp:130
msgid ""
@@ -2484,13 +2473,15 @@ msgstr "Vis labels for genstande musen er henover"
#: engines/teenagent/resources.cpp:94
msgid ""
"You're missing the 'teenagent.dat' file. Get it from the ScummVM website"
-msgstr ""
+msgstr "Du mangler filen 'teenagent.dat'. Hent den på ScummVM hjemmesiden"
#: engines/teenagent/resources.cpp:115
msgid ""
"The teenagent.dat file is compressed and zlib hasn't been included in this "
"executable. Please decompress it"
msgstr ""
+"Teenagent.dat filen er komprimeret og zlib er ikke blevet inkluderet i dette "
+"program. Udpak den venligst"
#: engines/parallaction/saveload.cpp:133
#, c-format
@@ -2543,44 +2534,43 @@ msgstr ""
#: engines/pegasus/pegasus.cpp:679
msgid "Invalid save file name"
-msgstr ""
+msgstr "Ugyldigt navn for gemmer"
#: engines/pegasus/pegasus.cpp:2445
msgid "Up/Zoom In/Move Forward/Open Doors"
-msgstr ""
+msgstr "Op/Forstør/Flyt frem/Åben døre"
#: engines/pegasus/pegasus.cpp:2446
-#, fuzzy
msgid "Down/Zoom Out"
-msgstr "Formindsk"
+msgstr "Ned/Forminsk"
#: engines/pegasus/pegasus.cpp:2449
msgid "Display/Hide Inventory Tray"
-msgstr ""
+msgstr "Vis/Skjul Oversigtsbakke"
#: engines/pegasus/pegasus.cpp:2450
msgid "Display/Hide Biochip Tray"
-msgstr ""
+msgstr "Vis/Skjul Biochip bakke"
#: engines/pegasus/pegasus.cpp:2451
msgid "Action/Select"
-msgstr ""
+msgstr "Handling/Vælg"
#: engines/pegasus/pegasus.cpp:2452
msgid "Toggle Center Data Display"
-msgstr ""
+msgstr "Skift Center Data Display"
#: engines/pegasus/pegasus.cpp:2453
msgid "Display/Hide Info Screen"
-msgstr ""
+msgstr "Vis/Skjul Info skærm"
#: engines/pegasus/pegasus.cpp:2454
msgid "Display/Hide Pause Menu"
-msgstr ""
+msgstr "Vis/Skjul Pause menu"
#: engines/pegasus/pegasus.cpp:2455
msgid "???"
-msgstr ""
+msgstr "???"
#: audio/fmopl.cpp:49
msgid "MAME OPL emulator"
@@ -3238,9 +3228,8 @@ msgid "Decreasing Volume"
msgstr "Sænker lydstyrke"
#: backends/events/openpandora/op-events.cpp:174
-#, fuzzy
msgid "Touchscreen 'Tap Mode' - Hover (DPad Clicks)"
-msgstr "Touchscreen 'Tap Mode' - Henover (Ingen Klik)"
+msgstr "Touchscreen 'Tap Mode' - Henover (DPad Klik)"
#: backends/updates/macosx/macosx-updates.mm:67
msgid "Check for Updates..."
diff --git a/po/pl_PL.po b/po/pl_PL.po
index 720a55daca..38cedb55d7 100644
--- a/po/pl_PL.po
+++ b/po/pl_PL.po
@@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: ScummVM 1.3.0\n"
"Report-Msgid-Bugs-To: scummvm-devel@lists.sf.net\n"
"POT-Creation-Date: 2013-04-24 13:35+0100\n"
-"PO-Revision-Date: 2012-07-29 15:49+0100\n"
+"PO-Revision-Date: 2013-05-22 14:26+0100\n"
"Last-Translator: Micha³ Zi±bkowski <mziab@o2.pl>\n"
"Language-Team: Grajpopolsku.pl <grajpopolsku@gmail.com>\n"
"Language: Polski\n"
@@ -34,13 +34,12 @@ msgid "Available engines:"
msgstr "Dostêpne silniki:"
#: gui/browser.cpp:67
-#, fuzzy
msgid "Show hidden files"
-msgstr "Schowaj / poka¿ konsolê"
+msgstr "Poka¿ ukryte pliki"
#: gui/browser.cpp:67
msgid "Show files marked with the hidden attribute"
-msgstr ""
+msgstr "Poka¿ pliki oznaczone atrybutem ukryty"
#: gui/browser.cpp:71
msgid "Go up"
@@ -724,7 +723,7 @@ msgstr "Wzm. MIDI:"
#: gui/options.cpp:873
msgid "FluidSynth Settings"
-msgstr ""
+msgstr "Ustawienia FluidSynth"
#: gui/options.cpp:880
msgid "MT-32 Device:"
@@ -753,9 +752,8 @@ msgid "True Roland MT-32 (no GM emulation)"
msgstr "Prawdziwy Roland MT-32 (brak emulacji GM)"
#: gui/options.cpp:890
-#, fuzzy
msgid "Roland GS Mode (disable GM mapping)"
-msgstr "Prawdziwy Roland MT-32 (wy³±cz emulacjê GM)"
+msgstr "Roland w trybie GS (wy³±cz mapowanie GM)"
#: gui/options.cpp:890
msgid "Turns off General MIDI mapping for games with Roland MT-32 soundtrack"
@@ -942,11 +940,11 @@ msgstr ""
#: gui/saveload-dialog.cpp:166
msgid "List view"
-msgstr ""
+msgstr "Widok listy"
#: gui/saveload-dialog.cpp:167
msgid "Grid view"
-msgstr ""
+msgstr "Widok siatki"
#: gui/saveload-dialog.cpp:210 gui/saveload-dialog.cpp:358
msgid "No date saved"
@@ -986,31 +984,28 @@ msgstr "Zapis bez nazwy"
#: gui/saveload-dialog.cpp:546
msgid "Next"
-msgstr ""
+msgstr "Nastêpny"
#: gui/saveload-dialog.cpp:549
msgid "Prev"
-msgstr ""
+msgstr "Poprzedni"
#: gui/saveload-dialog.cpp:736
-#, fuzzy
msgid "New Save"
-msgstr "Zapisz"
+msgstr "Nowy zapis"
#: gui/saveload-dialog.cpp:736
-#, fuzzy
msgid "Create a new save game"
-msgstr "Nie uda³o siê zapisaæ stanu gry"
+msgstr "Stwórz nowy zapis"
#: gui/saveload-dialog.cpp:865
-#, fuzzy
msgid "Name: "
-msgstr "Nazwa:"
+msgstr "Nazwa: "
#: gui/saveload-dialog.cpp:937
#, c-format
msgid "Enter a description for slot %d:"
-msgstr ""
+msgstr "Podaj opis dla slotu %d:"
#: gui/themebrowser.cpp:44
msgid "Select a Theme"
@@ -1046,93 +1041,90 @@ msgid "Clear value"
msgstr "Wyczy¶æ"
#: gui/fluidsynth-dialog.cpp:67
-#, fuzzy
msgid "Reverb"
-msgstr "Nigdy"
+msgstr "Pog³os"
#: gui/fluidsynth-dialog.cpp:69 gui/fluidsynth-dialog.cpp:101
-#, fuzzy
msgid "Active"
-msgstr " (Aktywny)"
+msgstr "Aktywny"
#: gui/fluidsynth-dialog.cpp:71
msgid "Room:"
-msgstr ""
+msgstr "Przestrzeñ:"
#: gui/fluidsynth-dialog.cpp:78
msgid "Damp:"
-msgstr ""
+msgstr "T³umienie:"
#: gui/fluidsynth-dialog.cpp:85
msgid "Width:"
-msgstr ""
+msgstr "Szeroko¶æ:"
#: gui/fluidsynth-dialog.cpp:92 gui/fluidsynth-dialog.cpp:110
msgid "Level:"
-msgstr ""
+msgstr "Poziom:"
#: gui/fluidsynth-dialog.cpp:99
msgid "Chorus"
-msgstr ""
+msgstr "Chorus"
#: gui/fluidsynth-dialog.cpp:103
msgid "N:"
-msgstr ""
+msgstr "N:"
#: gui/fluidsynth-dialog.cpp:117
-#, fuzzy
msgid "Speed:"
-msgstr "Mowa"
+msgstr "Szybko¶æ:"
#: gui/fluidsynth-dialog.cpp:124
msgid "Depth:"
-msgstr ""
+msgstr "G³êbia:"
#: gui/fluidsynth-dialog.cpp:131
msgid "Type:"
-msgstr ""
+msgstr "Typ:"
#: gui/fluidsynth-dialog.cpp:134
msgid "Sine"
-msgstr ""
+msgstr "Sinus"
#: gui/fluidsynth-dialog.cpp:135
msgid "Triangle"
-msgstr ""
+msgstr "Trójk±t"
#: gui/fluidsynth-dialog.cpp:139
msgid "Interpolation:"
-msgstr ""
+msgstr "Interpolacja:"
#: gui/fluidsynth-dialog.cpp:142
msgid "None (fastest)"
-msgstr ""
+msgstr "Brak (najszybsze)"
#: gui/fluidsynth-dialog.cpp:143
msgid "Linear"
-msgstr ""
+msgstr "Liniowa"
#: gui/fluidsynth-dialog.cpp:144
msgid "Fourth-order"
-msgstr ""
+msgstr "Czterostopniowa"
#: gui/fluidsynth-dialog.cpp:145
msgid "Seventh-order"
-msgstr ""
+msgstr "Siedmiostopniowa"
#: gui/fluidsynth-dialog.cpp:149
msgid "Reset"
-msgstr ""
+msgstr "Reset"
#: gui/fluidsynth-dialog.cpp:149
msgid "Reset all FluidSynth settings to their default values."
-msgstr ""
+msgstr "Przywróæ domy¶lne warto¶ci wszystkich ustawieñ FluidSynth."
#: gui/fluidsynth-dialog.cpp:216
-#, fuzzy
msgid ""
"Do you really want to reset all FluidSynth settings to their default values?"
-msgstr "Na pewno chcesz powróciæ do launchera?"
+msgstr ""
+"Na pewno chcesz przywróciæ domy¶lne warto¶ci wszystkich ustawieñ FluidSynth?"
#: base/main.cpp:210
#, c-format
@@ -1317,7 +1309,7 @@ msgstr ""
#: engines/dialogs.cpp:301 engines/mohawk/dialogs.cpp:109
#: engines/mohawk/dialogs.cpp:170
msgid "~O~K"
-msgstr ""
+msgstr "~O~K"
#: engines/dialogs.cpp:302 engines/mohawk/dialogs.cpp:110
#: engines/mohawk/dialogs.cpp:171
@@ -1414,7 +1406,6 @@ msgid "Restore"
msgstr "Wznów"
#: engines/drascula/saveload.cpp:49
-#, fuzzy
msgid ""
"ScummVM found that you have old savefiles for Drascula that should be "
"converted.\n"
@@ -1424,8 +1415,7 @@ msgid ""
"Press OK to convert them now, otherwise you will be asked again the next "
"time you start the game.\n"
msgstr ""
-"ScummVM znalaz³ stare zapisy z Broken Sword 1, które nale¿y "
-"przekonwertowaæ.\n"
+"ScummVM znalaz³ stare zapisy z Drascula, które nale¿y przekonwertowaæ.\n"
"Stary format zapisu nie jest ju¿ obs³ugiwany. Nie bêdziesz móg³ wczytaæ "
"zapisów, je¶li ich nie przekonwertujesz.\n"
"\n"
@@ -2097,7 +2087,7 @@ msgstr "Kiedy Indy jest po prawej,"
#: engines/scumm/help.cpp:308
msgid "7, 4, and 1 are switched with"
-msgstr "7, 4, i 1 zostaj± zamienione"
+msgstr "7, 4 i 1 zostaj± zamienione"
#: engines/scumm/help.cpp:309
msgid "9, 6, and 3, respectively."
@@ -2240,13 +2230,12 @@ msgid "Failed to delete file."
msgstr "Nie uda³o siê usun±æ pliku."
#: engines/groovie/detection.cpp:312
-#, fuzzy
msgid "Fast movie speed"
-msgstr "Tryb szybki"
+msgstr "Przy¶pieszone filmy"
#: engines/groovie/detection.cpp:313
msgid "Play movies at an increased speed"
-msgstr ""
+msgstr "Odtwarzaj filmy ze zwiêkszon± prêdko¶ci±"
#: engines/groovie/script.cpp:420
msgid "Failed to save game"
@@ -2373,12 +2362,11 @@ msgstr ""
#: engines/queen/queen.cpp:59
msgid "Alternative intro"
-msgstr ""
+msgstr "Alternatywne intro"
#: engines/queen/queen.cpp:60
-#, fuzzy
msgid "Use an alternative game intro (CD version only)"
-msgstr "U¿yj intra z wersji dyskietkowej (tylko dla wersji CD)"
+msgstr "U¿yj alternatywnego intra (tylko dla wersji CD)"
#: engines/sky/compact.cpp:130
msgid ""
@@ -2483,13 +2471,15 @@ msgstr "Poka¿ etykiety obiektów przy najechaniu myszk±"
#: engines/teenagent/resources.cpp:94
msgid ""
"You're missing the 'teenagent.dat' file. Get it from the ScummVM website"
-msgstr ""
+msgstr "Nie masz pliku 'teenagent.dat'. Pobierz go ze strony ScummVM"
#: engines/teenagent/resources.cpp:115
msgid ""
"The teenagent.dat file is compressed and zlib hasn't been included in this "
"executable. Please decompress it"
msgstr ""
+"Plik teenagent.dat jest skompresowany, a ScummVM nie zawiera obs³ugi zlib. "
+"Rozpakuj plik."
#: engines/parallaction/saveload.cpp:133
#, c-format
@@ -2542,44 +2532,43 @@ msgstr ""
#: engines/pegasus/pegasus.cpp:679
msgid "Invalid save file name"
-msgstr ""
+msgstr "Nieprawid³owa nazwa pliku zapisu"
#: engines/pegasus/pegasus.cpp:2445
msgid "Up/Zoom In/Move Forward/Open Doors"
-msgstr ""
+msgstr "Góra/Przybli¿/Ruch naprzód/Otwórz drzwi"
#: engines/pegasus/pegasus.cpp:2446
-#, fuzzy
msgid "Down/Zoom Out"
-msgstr "Przybli¿"
+msgstr "Dó³/Oddal"
#: engines/pegasus/pegasus.cpp:2449
msgid "Display/Hide Inventory Tray"
-msgstr ""
+msgstr "Poka¿/schowaj przedmioty"
#: engines/pegasus/pegasus.cpp:2450
msgid "Display/Hide Biochip Tray"
-msgstr ""
+msgstr "Poka¿/schowaj biochipy"
#: engines/pegasus/pegasus.cpp:2451
msgid "Action/Select"
-msgstr ""
+msgstr "Akcja/Wybór"
#: engines/pegasus/pegasus.cpp:2452
msgid "Toggle Center Data Display"
-msgstr ""
+msgstr "W³±cz/wy³±cz widok danych"
#: engines/pegasus/pegasus.cpp:2453
msgid "Display/Hide Info Screen"
-msgstr ""
+msgstr "Poka¿/schowaj ekran informacji"
#: engines/pegasus/pegasus.cpp:2454
msgid "Display/Hide Pause Menu"
-msgstr ""
+msgstr "Poka¿/schowaj menu pauzy"
#: engines/pegasus/pegasus.cpp:2455
msgid "???"
-msgstr ""
+msgstr "???"
#: audio/fmopl.cpp:49
msgid "MAME OPL emulator"
@@ -2856,15 +2845,15 @@ msgstr "Okno"
#: backends/graphics/opengl/opengl-graphics.cpp:135
msgid "OpenGL Normal"
-msgstr "OpenGL - normalny"
+msgstr "OpenGL (normalny)"
#: backends/graphics/opengl/opengl-graphics.cpp:136
msgid "OpenGL Conserve"
-msgstr "OpenGL - zachowanie proporcji"
+msgstr "OpenGL (zachowanie proporcji)"
#: backends/graphics/opengl/opengl-graphics.cpp:137
msgid "OpenGL Original"
-msgstr "OpenGL - oryginalny rozmiar"
+msgstr "OpenGL (oryginalny rozmiar)"
#: backends/graphics/openglsdl/openglsdl-graphics.cpp:415
msgid "Current display mode"
@@ -3194,7 +3183,7 @@ msgstr "Na pewno chcesz powróciæ do launchera?"
#: backends/events/default/default-events.cpp:193
msgid "Launcher"
-msgstr ""
+msgstr "Launcher"
#: backends/events/default/default-events.cpp:215
msgid "Do you really want to quit?"
@@ -3216,7 +3205,7 @@ msgstr "Dotkniêcie ekranu - klikniêcie PPM"
#: backends/events/gph/gph-events.cpp:433
#: backends/events/openpandora/op-events.cpp:172
msgid "Touchscreen 'Tap Mode' - Hover (No Click)"
-msgstr "Dotkniêcie ekranu - brak klikniêcia"
+msgstr "Dotkniêcie ekranu - przytrzymanie (brak klikniêcia)"
#: backends/events/gph/gph-events.cpp:410
msgid "Maximum Volume"
@@ -3235,9 +3224,8 @@ msgid "Decreasing Volume"
msgstr "Zmniejszenie g³o¶no¶ci"
#: backends/events/openpandora/op-events.cpp:174
-#, fuzzy
msgid "Touchscreen 'Tap Mode' - Hover (DPad Clicks)"
-msgstr "Dotkniêcie ekranu - brak klikniêcia"
+msgstr "Dotkniêcie ekranu - przytrzymanie (krzy¿ak klika)"
#: backends/updates/macosx/macosx-updates.mm:67
msgid "Check for Updates..."
diff --git a/po/se_SE.po b/po/se_SE.po
index 5e5a549f1b..0031e2dc06 100644
--- a/po/se_SE.po
+++ b/po/se_SE.po
@@ -8,17 +8,16 @@ msgstr ""
"Project-Id-Version: ScummVM 1.5.0svn\n"
"Report-Msgid-Bugs-To: scummvm-devel@lists.sf.net\n"
"POT-Creation-Date: 2013-04-24 13:35+0100\n"
-"PO-Revision-Date: 2012-07-08 18:03+0100\n"
+"PO-Revision-Date: 2013-05-13 21:30+0100\n"
"Last-Translator: Hampus Flink <hampus.flink@gmail.com>\n"
"Language-Team: \n"
"Language: Svenska\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=iso-8859-1\n"
"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=(n != 1)\n"
-"X-Poedit-Language: Swedish\n"
-"X-Poedit-Country: SWEDEN\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Poedit-SourceCharset: iso-8859-1\n"
+"X-Generator: Poedit 1.5.5\n"
#: gui/about.cpp:93
#, c-format
@@ -34,13 +33,12 @@ msgid "Available engines:"
msgstr "Tillgängliga motorer"
#: gui/browser.cpp:67
-#, fuzzy
msgid "Show hidden files"
-msgstr "Visa / göm konsol"
+msgstr "Visa gömda filer"
#: gui/browser.cpp:67
msgid "Show files marked with the hidden attribute"
-msgstr ""
+msgstr "Visa dolda filer"
#: gui/browser.cpp:71
msgid "Go up"
@@ -537,7 +535,7 @@ msgstr "Scanning färdig!"
#: gui/massadd.cpp:261
#, c-format
msgid "Discovered %d new games, ignored %d previously added games."
-msgstr "Upptäckte %n nya spel. Ignorerade %d tidigare tillagda spel."
+msgstr "Upptäckte %d nya spel. Ignorerade %d tidigare tillagda spel."
#: gui/massadd.cpp:265
#, c-format
@@ -726,7 +724,7 @@ msgstr "MIDI gain:"
#: gui/options.cpp:873
msgid "FluidSynth Settings"
-msgstr ""
+msgstr "FluidSynth inställningar"
#: gui/options.cpp:880
msgid "MT-32 Device:"
@@ -755,9 +753,8 @@ msgid "True Roland MT-32 (no GM emulation)"
msgstr "Äkta Roland MT-32 (ingen GM-emulation)"
#: gui/options.cpp:890
-#, fuzzy
msgid "Roland GS Mode (disable GM mapping)"
-msgstr "Äkta Roland MT-32 (inaktivera GM-emulation)"
+msgstr "Roland GS-läge (avaktivera GM-mapping)"
#: gui/options.cpp:890
msgid "Turns off General MIDI mapping for games with Roland MT-32 soundtrack"
@@ -946,11 +943,11 @@ msgstr ""
#: gui/saveload-dialog.cpp:166
msgid "List view"
-msgstr ""
+msgstr "Visa som lista"
#: gui/saveload-dialog.cpp:167
msgid "Grid view"
-msgstr ""
+msgstr "Visa som rutnät"
#: gui/saveload-dialog.cpp:210 gui/saveload-dialog.cpp:358
msgid "No date saved"
@@ -990,31 +987,28 @@ msgstr "Namnlös spardata"
#: gui/saveload-dialog.cpp:546
msgid "Next"
-msgstr ""
+msgstr "Nästa"
#: gui/saveload-dialog.cpp:549
msgid "Prev"
-msgstr ""
+msgstr "Bakåt"
#: gui/saveload-dialog.cpp:736
-#, fuzzy
msgid "New Save"
-msgstr "Spara"
+msgstr "Ny sparning"
#: gui/saveload-dialog.cpp:736
-#, fuzzy
msgid "Create a new save game"
-msgstr "Kunde inte spara spelet."
+msgstr "Skapa ett nytt sparat spel"
#: gui/saveload-dialog.cpp:865
-#, fuzzy
msgid "Name: "
msgstr "Namn:"
#: gui/saveload-dialog.cpp:937
#, c-format
msgid "Enter a description for slot %d:"
-msgstr ""
+msgstr "Ange en beskrivning för position %d:"
#: gui/themebrowser.cpp:44
msgid "Select a Theme"
@@ -1050,93 +1044,92 @@ msgid "Clear value"
msgstr "Töm sökfältet"
#: gui/fluidsynth-dialog.cpp:67
-#, fuzzy
msgid "Reverb"
-msgstr "Aldrig"
+msgstr "Reverb"
#: gui/fluidsynth-dialog.cpp:69 gui/fluidsynth-dialog.cpp:101
-#, fuzzy
msgid "Active"
-msgstr "(Aktiv)"
+msgstr "Aktiv"
#: gui/fluidsynth-dialog.cpp:71
msgid "Room:"
-msgstr ""
+msgstr "Rum:"
#: gui/fluidsynth-dialog.cpp:78
msgid "Damp:"
-msgstr ""
+msgstr "Dämpa:"
#: gui/fluidsynth-dialog.cpp:85
msgid "Width:"
-msgstr ""
+msgstr "Bredd:"
#: gui/fluidsynth-dialog.cpp:92 gui/fluidsynth-dialog.cpp:110
msgid "Level:"
-msgstr ""
+msgstr "Nivå:"
#: gui/fluidsynth-dialog.cpp:99
msgid "Chorus"
-msgstr ""
+msgstr "Chorus"
#: gui/fluidsynth-dialog.cpp:103
msgid "N:"
-msgstr ""
+msgstr "N:"
#: gui/fluidsynth-dialog.cpp:117
-#, fuzzy
msgid "Speed:"
-msgstr "Tal"
+msgstr "Hastighet:"
#: gui/fluidsynth-dialog.cpp:124
msgid "Depth:"
-msgstr ""
+msgstr "Djup:"
#: gui/fluidsynth-dialog.cpp:131
msgid "Type:"
-msgstr ""
+msgstr "Typ:"
#: gui/fluidsynth-dialog.cpp:134
msgid "Sine"
-msgstr ""
+msgstr "Sinus"
#: gui/fluidsynth-dialog.cpp:135
msgid "Triangle"
-msgstr ""
+msgstr "Triangel"
#: gui/fluidsynth-dialog.cpp:139
msgid "Interpolation:"
-msgstr ""
+msgstr "Interpolering:"
#: gui/fluidsynth-dialog.cpp:142
msgid "None (fastest)"
-msgstr ""
+msgstr "Ingen (snabbast)"
#: gui/fluidsynth-dialog.cpp:143
msgid "Linear"
-msgstr ""
+msgstr "Linjär"
#: gui/fluidsynth-dialog.cpp:144
msgid "Fourth-order"
-msgstr ""
+msgstr "Fjärde ordningen"
#: gui/fluidsynth-dialog.cpp:145
msgid "Seventh-order"
-msgstr ""
+msgstr "Sjunde ordningen"
#: gui/fluidsynth-dialog.cpp:149
msgid "Reset"
-msgstr ""
+msgstr "Återställ"
#: gui/fluidsynth-dialog.cpp:149
msgid "Reset all FluidSynth settings to their default values."
msgstr ""
+"Återställ alla FluidSynth-inställningar till deras ursprungliga värden."
#: gui/fluidsynth-dialog.cpp:216
-#, fuzzy
msgid ""
"Do you really want to reset all FluidSynth settings to their default values?"
-msgstr "Vill du verkligen återgå till launchern?"
+msgstr ""
+"Vill du verkligen återställa alla FluidSynth-inställningar till deras "
+"ursprungliga värden?"
#: base/main.cpp:210
#, c-format
@@ -1315,7 +1308,9 @@ msgstr ""
msgid ""
"Gamestate save failed (%s)! Please consult the README for basic information, "
"and for instructions on how to obtain further assistance."
-msgstr "Spar"
+msgstr ""
+"Kunde inte spara data (%s)! Var god läs README-filen för grundläggande "
+"information och instruktioner för hur du kan få mer hjälp."
#: engines/dialogs.cpp:301 engines/mohawk/dialogs.cpp:109
#: engines/mohawk/dialogs.cpp:170
@@ -1421,7 +1416,6 @@ msgid "Restore"
msgstr "Återställ"
#: engines/drascula/saveload.cpp:49
-#, fuzzy
msgid ""
"ScummVM found that you have old savefiles for Drascula that should be "
"converted.\n"
@@ -1431,12 +1425,12 @@ msgid ""
"Press OK to convert them now, otherwise you will be asked again the next "
"time you start the game.\n"
msgstr ""
-"ScummVM upptäckte att du har gamla spardata för Broken Sword 1 som borde "
+"ScummVM upptäckte att du har gamla spardata för Drascula som borde "
"konverteras.\n"
"Det gamla spardataformatet stöds inte längre, så du kommer inte kunna ladda "
"dina data om du inte konverterar dem.\n"
"\n"
-"Tryck \"OK\" för att konvertera dem nu, annars kommer du tillfrågas igen "
+"Tryck på OK för att konvertera dem nu, annars kommer du tillfrågas igen "
"nästa gång du startar spelet.\n"
#: engines/dreamweb/detection.cpp:57
@@ -2247,13 +2241,12 @@ msgid "Failed to delete file."
msgstr "Kunde inte radera filen."
#: engines/groovie/detection.cpp:312
-#, fuzzy
msgid "Fast movie speed"
-msgstr "Snabbläge"
+msgstr "Snabb filmhastighet"
#: engines/groovie/detection.cpp:313
msgid "Play movies at an increased speed"
-msgstr ""
+msgstr "Spela filmer i högre hastighet"
#: engines/groovie/script.cpp:420
msgid "Failed to save game"
@@ -2381,12 +2374,11 @@ msgstr ""
#: engines/queen/queen.cpp:59
msgid "Alternative intro"
-msgstr ""
+msgstr "Alternativt intro"
#: engines/queen/queen.cpp:60
-#, fuzzy
msgid "Use an alternative game intro (CD version only)"
-msgstr "Använd diskettversionens intro (endast CD-version)"
+msgstr "Använd alternativt spelintro (endast CD-version)"
#: engines/sky/compact.cpp:130
msgid ""
@@ -2485,13 +2477,15 @@ msgstr "Visar etiketter för objekten som musen pekar på"
#: engines/teenagent/resources.cpp:94
msgid ""
"You're missing the 'teenagent.dat' file. Get it from the ScummVM website"
-msgstr ""
+msgstr "Du har inte 'teenagent.dat'-filen. Hämta den från ScummVM:s webbsida"
#: engines/teenagent/resources.cpp:115
msgid ""
"The teenagent.dat file is compressed and zlib hasn't been included in this "
"executable. Please decompress it"
msgstr ""
+"Teenagent.dat-filen är komprimerad och zlib har inte inkluderats i det här "
+"programmet. Var god dekomprimera den"
#: engines/parallaction/saveload.cpp:133
#, c-format
@@ -2545,44 +2539,43 @@ msgstr ""
#: engines/pegasus/pegasus.cpp:679
msgid "Invalid save file name"
-msgstr ""
+msgstr "Ogiltigt namn på sparfil"
#: engines/pegasus/pegasus.cpp:2445
msgid "Up/Zoom In/Move Forward/Open Doors"
-msgstr ""
+msgstr "Upp/Zooma in/Gå framåt/Öppna dörrar"
#: engines/pegasus/pegasus.cpp:2446
-#, fuzzy
msgid "Down/Zoom Out"
-msgstr "Zooma upp"
+msgstr "Ned/Zooma ut"
#: engines/pegasus/pegasus.cpp:2449
msgid "Display/Hide Inventory Tray"
-msgstr ""
+msgstr "Visa/dölj inventarium"
#: engines/pegasus/pegasus.cpp:2450
msgid "Display/Hide Biochip Tray"
-msgstr ""
+msgstr "Visa/dölj biochip"
#: engines/pegasus/pegasus.cpp:2451
msgid "Action/Select"
-msgstr ""
+msgstr "Handling/Välj"
#: engines/pegasus/pegasus.cpp:2452
msgid "Toggle Center Data Display"
-msgstr ""
+msgstr "Aktivera centrerad dataskärm"
#: engines/pegasus/pegasus.cpp:2453
msgid "Display/Hide Info Screen"
-msgstr ""
+msgstr "Visa/dölj informationsskärm"
#: engines/pegasus/pegasus.cpp:2454
msgid "Display/Hide Pause Menu"
-msgstr ""
+msgstr "Visa/dölj pausmeny"
#: engines/pegasus/pegasus.cpp:2455
msgid "???"
-msgstr ""
+msgstr "???"
#: audio/fmopl.cpp:49
msgid "MAME OPL emulator"
@@ -3241,9 +3234,8 @@ msgid "Decreasing Volume"
msgstr "Sänka volymen"
#: backends/events/openpandora/op-events.cpp:174
-#, fuzzy
msgid "Touchscreen 'Tap Mode' - Hover (DPad Clicks)"
-msgstr "Touchscreen \"Tap-läge\" - Hover (utan klick)"
+msgstr "Touchscreen 'Tap-läge' - Hover (DPad klick)"
#: backends/updates/macosx/macosx-updates.mm:67
msgid "Check for Updates..."
diff --git a/video/smk_decoder.h b/video/smk_decoder.h
index 7227238373..e4bc9bab42 100644
--- a/video/smk_decoder.h
+++ b/video/smk_decoder.h
@@ -122,7 +122,6 @@ protected:
Common::SeekableReadStream *_fileStream;
-private:
enum AudioCompression {
kCompressionNone,
kCompressionDPCM,
@@ -151,6 +150,10 @@ private:
uint32 dummy;
} _header;
+ uint32 *_frameSizes;
+
+private:
+
class SmackerAudioTrack : public AudioTrack {
public:
SmackerAudioTrack(const AudioInfo &audioInfo, Audio::Mixer::SoundType soundType);
@@ -173,7 +176,6 @@ private:
AudioInfo _audioInfo;
};
- uint32 *_frameSizes;
// The FrameTypes section of a Smacker file contains an array of bytes, where
// the 8 bits of each byte describe the contents of the corresponding frame.
// The highest 7 bits correspond to audio frames (bit 7 is track 6, bit 6 track 5