Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
engines/neverhood/dialogs.cpp
engines/neverhood/menumodule.cpp
engines/neverhood/metaengine.cpp

View File

@@ -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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "neverhood/background.h"
namespace Neverhood {
// Background
Background::Background(NeverhoodEngine *vm, int objectPriority)
: Entity(vm, objectPriority), _surface(nullptr), _spriteResource(vm) {
// Empty
}
Background::Background(NeverhoodEngine *vm, uint32 fileHash, int objectPriority, int surfacePriority)
: Entity(vm, objectPriority), _surface(nullptr), _spriteResource(vm) {
_spriteResource.load(fileHash);
createSurface(surfacePriority, _spriteResource.getDimensions().width, _spriteResource.getDimensions().height);
_surface->drawSpriteResource(_spriteResource);
}
void Background::createSurface(int surfacePriority, int16 width, int16 height) {
_surface .reset(new BaseSurface(_vm, surfacePriority, width, height, "background"));
_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

View File

@@ -0,0 +1,47 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#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);
Common::SharedPtr<BaseSurface> getSurface() { return _surface; }
void createSurface(int surfacePriority, int16 width, int16 height);
void load(uint32 fileHash);
SpriteResource& getSpriteResource() { return _spriteResource; }
protected:
Common::SharedPtr<BaseSurface> _surface;
SpriteResource _spriteResource;
};
} // End of namespace Neverhood
#endif /* NEVERHOOD_BACKGROUND_H */

View File

@@ -0,0 +1,144 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/compression/dcl.h"
#include "neverhood/blbarchive.h"
namespace Neverhood {
BlbArchive::BlbArchive() : _extData(nullptr) {
}
BlbArchive::~BlbArchive() {
delete[] _extData;
}
bool BlbArchive::open(const Common::Path &filename, bool isOptional) {
BlbHeader header;
uint16 *extDataOffsets;
_entries.clear();
if (!_fd.open(filename)) {
if (!isOptional)
error("BlbArchive::open() Could not open %s", filename.toString(Common::Path::kNativeSeparator).c_str());
return false;
}
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.toString(Common::Path::kNativeSeparator).c_str());
return false;
}
debug(4, "%s: fileCount = %d", filename.toString(Common::Path::kNativeSeparator).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 = nullptr;
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 : nullptr;
}
delete[] extDataOffsets;
return true;
}
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
memset(buffer, 0, size);
if (!Common::decompressDCL(&_fd, buffer, entry->diskSize, entry->size))
warning("BlbArchive::load() Error during decompression of %08X (offset: %d, disk size: %d, size: %d)",
entry->fileHash, entry->offset, entry->diskSize, entry->size);
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 Common::SafeMutexedSeekableSubReadStream(&_fd, entry->offset, entry->offset + entry->diskSize,
DisposeAfterUse::NO, _mutex);
}
} // End of namespace Neverhood

View File

@@ -0,0 +1,76 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#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();
bool open(const Common::Path &filename, bool isOptional);
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 */

View File

@@ -0,0 +1,3 @@
# This file is included from the main "configure" script
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] [components]
add_engine neverhood "Neverhood" yes "" "" "highres"

View File

@@ -0,0 +1,268 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "neverhood/console.h"
#include "gui/debugger.h"
#include "neverhood/neverhood.h"
#include "neverhood/gamemodule.h"
#include "neverhood/navigationscene.h"
#include "neverhood/scene.h"
#include "neverhood/smackerscene.h"
#include "neverhood/sound.h"
#include "neverhood/modules/module1600.h"
#include "neverhood/modules/module3000_sprites.h"
namespace Neverhood {
Console::Console(NeverhoodEngine *vm) : GUI::Debugger(), _vm(vm) {
registerCmd("cheat", WRAP_METHOD(Console, Cmd_Cheat));
registerCmd("checkresource", WRAP_METHOD(Console, Cmd_CheckResource));
registerCmd("dumpresource", WRAP_METHOD(Console, Cmd_DumpResource));
registerCmd("dumpvars", WRAP_METHOD(Console, Cmd_Dumpvars));
registerCmd("playsound", WRAP_METHOD(Console, Cmd_PlaySound));
registerCmd("scene", WRAP_METHOD(Console, Cmd_Scene));
registerCmd("surfaces", WRAP_METHOD(Console, Cmd_Surfaces));
}
Console::~Console() {
}
bool Console::Cmd_Scene(int argc, const char **argv) {
if (argc != 3) {
int currentModule = _vm->_gameModule->getCurrentModuleNum();
int previousModule = _vm->_gameModule->getPreviousModuleNum();
int scenenNum = _vm->gameState().sceneNum;
SceneType sceneType = ((GameModule *)_vm->_gameModule->_childObject)->getSceneType();
const char *sceneTypes[] = { "normal", "smacker", "navigation" };
debugPrintf("Current module: %d, previous module: %d, scene %d (%s scene)\n", currentModule, previousModule, scenenNum, sceneTypes[sceneType]);
if (sceneType == kSceneTypeNormal) {
Scene *scene = (Scene *)((GameModule *)_vm->_gameModule->_childObject)->_childObject;
// Normal scenes have a background and a cursor file hash
debugPrintf("Background hash: 0x%x, cursor hash: 0x%x\n", scene->getBackgroundFileHash(), scene->getCursorFileHash());
} else if (sceneType == kSceneTypeSmacker) {
SmackerScene *scene = (SmackerScene *)((GameModule *)_vm->_gameModule->_childObject)->_childObject;
// Smacker scenes have a file hash, or a list of hashes
// TODO: Only the first file hash is shown - any additional hashes, found in
// scenes with a list of hashes (two scenes in module 1100 and the making of
// video) aren't shown yet
debugPrintf("File hash: 0x%x\n", scene->getSmackerFileHash());
} else if (sceneType == kSceneTypeNavigation) {
NavigationScene *scene = (NavigationScene *)((GameModule *)_vm->_gameModule->_childObject)->_childObject;
// Navigation scenes have a navigation list and its index
NavigationList *navigationList = _vm->_staticData->getNavigationList(scene->getNavigationListId());
int navigationIndex = scene->getGlobalVar(V_NAVIGATION_INDEX);
NavigationItem curNavigation = (*navigationList)[navigationIndex];
debugPrintf("Navigation list ID: 0x%x, index: %d\n", scene->getNavigationListId(), navigationIndex);
debugPrintf("File hash: 0x%x, cursor hash: 0x%x, Smacker hashes: [left: 0x%x, middle: 0x%x, right: 0x%x\n",
curNavigation.fileHash, curNavigation.mouseCursorFileHash,
curNavigation.leftSmackerFileHash, curNavigation.middleSmackerFileHash, curNavigation.rightSmackerFileHash);
}
debugPrintf("Use %s <module> <scene> to change scenes\n", argv[0]);
debugPrintf("Modules are incremental by 100, from 1000 to 3000\n");
} else {
int newModule = atoi(argv[1]);
int newScene = atoi(argv[2]);
_vm->gameState().sceneNum = newScene;
_vm->_gameModule->createModule(newModule, -1);
}
return true;
}
bool Console::Cmd_Surfaces(int argc, const char **argv) {
if (_vm->_gameModule->_childObject) {
((Scene *)((GameModule *)_vm->_gameModule->_childObject)->_childObject)->printSurfaces(this);
}
return true;
}
bool Console::Cmd_Cheat(int argc, const char **argv) {
if (argc < 2) {
debugPrintf("Cheats for various puzzles in the game\n");
debugPrintf("Use %s <cheatname> to use a cheat.\n", argv[0]);
debugPrintf("Cheats:\n-------\n");
debugPrintf(" buttons - enables all 3 buttons on the door in the purple building, module 3000, scene 9\n");
debugPrintf(" cannon - sets the correct cannon combination in module 3000, scene 8\n");
debugPrintf(" dice - shows the correct dice combination in the teddy bear puzzle, module 1100, scene 6\n");
debugPrintf(" memory - solves the memory puzzle, module 1400, scene 4\n");
debugPrintf(" music - shows the correct index in the radio music puzzle, module 2800, scene 1\n");
debugPrintf(" radio - enables the radio, module 3000, scene 9 - same as pulling the rightmost cord in the flytrap room\n");
debugPrintf(" symbols - solves the symbols puzzle, module 1600, scene 8. Only available in that room\n");
debugPrintf(" tubes - shows the correct test tube combination in module 2800, scenes 7 and 10\n");
return true;
}
Common::String cheatName = argv[1];
int moduleNum = _vm->_gameModule->getCurrentModuleNum();
int sceneNum = _vm->gameState().sceneNum;
if (cheatName == "buttons") {
Scene *scene = (Scene *)((GameModule *)_vm->_gameModule->_childObject)->_childObject;
scene->setSubVar(VA_LOCKS_DISABLED, 0x304008D2, 1); // kScene3010ButtonNameHashes[0]
scene->setSubVar(VA_LOCKS_DISABLED, 0x40119852, 1); // kScene3010ButtonNameHashes[1]
scene->setSubVar(VA_LOCKS_DISABLED, 0x01180951, 1); // kScene3010ButtonNameHashes[2]
debugPrintf("All 3 door buttons have been enabled\n");
} else if (cheatName == "cannon") {
Scene *scene = (Scene *)((GameModule *)_vm->_gameModule->_childObject)->_childObject;
for (int i = 0; i < 3; i++)
scene->setSubVar(VA_CURR_CANNON_SYMBOLS, i, scene->getSubVar(VA_GOOD_CANNON_SYMBOLS_1, i));
for (int i = 3; i < 6; i++)
scene->setSubVar(VA_CURR_CANNON_SYMBOLS, i, scene->getSubVar(VA_GOOD_CANNON_SYMBOLS_2, i - 3));
debugPrintf("Puzzle solved\n");
} else if (cheatName == "dice") {
Scene *scene = (Scene *)((GameModule *)_vm->_gameModule->_childObject)->_childObject;
debugPrintf("Good: (%d %d %d), current: (%d %d %d)\n",
scene->getSubVar(VA_GOOD_DICE_NUMBERS, 0), scene->getSubVar(VA_GOOD_DICE_NUMBERS, 1), scene->getSubVar(VA_GOOD_DICE_NUMBERS, 2),
scene->getSubVar(VA_CURR_DICE_NUMBERS, 0), scene->getSubVar(VA_CURR_DICE_NUMBERS, 1), scene->getSubVar(VA_CURR_DICE_NUMBERS, 2)
);
} else if (cheatName == "memory") {
Scene *scene = (Scene *)((GameModule *)_vm->_gameModule->_childObject)->_childObject;
// Autosolve all tiles and leave only two matching tiles closed
for (int i = 0; i < 48; i++)
scene->setSubVar(VA_IS_TILE_MATCH, i, 1);
// Close the top left tile
scene->setSubVar(VA_IS_TILE_MATCH, 0, 0);
// Find and close the pair of the top left tile
for (int i = 0; i < 48; i++) {
if (i != 0 && scene->getSubVar(VA_TILE_SYMBOLS, i) == scene->getSubVar(VA_TILE_SYMBOLS, 0)) {
scene->setSubVar(VA_IS_TILE_MATCH, i, 0);
break;
}
}
debugPrintf("Puzzle solved\n");
} else if (cheatName == "music") {
Scene *scene = (Scene *)((GameModule *)_vm->_gameModule->_childObject)->_childObject;
debugPrintf("Good music index: %d, current radio music index: %d\n", scene->getGlobalVar(V_CURR_RADIO_MUSIC_INDEX), scene->getGlobalVar(V_GOOD_RADIO_MUSIC_INDEX));
} else if (cheatName == "radio") {
Scene *scene = (Scene *)((GameModule *)_vm->_gameModule->_childObject)->_childObject;
scene->setGlobalVar(V_RADIO_ENABLED, 1);
debugPrintf("The radio has been enabled\n");
} else if (cheatName == "symbols") {
if (moduleNum == 1600 && sceneNum == 8) {
Scene1609 *scene = ((Scene1609 *)((Module1600 *)_vm->_gameModule->_childObject)->_childObject);
for (int index = 0; index < 12; index++) {
scene->_asSymbols[index]->change((int)scene->getSubVar(VA_CODE_SYMBOLS, index) + 12, index == (int)scene->getSubVar(VA_CODE_SYMBOLS, scene->_noisySymbolIndex));
}
scene->_changeCurrentSymbol = false;
scene->_symbolPosition = 11;
scene->_countdown1 = 36;
debugPrintf("Puzzle solved\n");
} else {
debugPrintf("Only available in module 1600, scene 8\n");
}
} else if (cheatName == "tubes") {
Scene *scene = (Scene *)((GameModule *)_vm->_gameModule->_childObject)->_childObject;
debugPrintf("Tube set 1: %d %d %d\n", scene->getSubVar(VA_GOOD_TEST_TUBES_LEVEL_1, 0), scene->getSubVar(VA_GOOD_TEST_TUBES_LEVEL_1, 1), scene->getSubVar(VA_GOOD_TEST_TUBES_LEVEL_1, 2));
debugPrintf("Tube set 2: %d %d %d\n", scene->getSubVar(VA_GOOD_TEST_TUBES_LEVEL_2, 0), scene->getSubVar(VA_GOOD_TEST_TUBES_LEVEL_2, 1), scene->getSubVar(VA_GOOD_TEST_TUBES_LEVEL_2, 2));
}
return true;
}
bool Console::Cmd_Dumpvars(int argc, const char **argv) {
_vm->_gameVars->dumpVars(this);
return true;
}
bool Console::Cmd_PlaySound(int argc, const char **argv) {
if (argc < 2) {
debugPrintf("Usage: %s <sound hash>\n", argv[0]);
} else {
uint32 soundHash = strtol(argv[1], nullptr, 0);
AudioResourceManSoundItem *soundItem = new AudioResourceManSoundItem(_vm, soundHash);
soundItem->setVolume(100);
soundItem->playSound(false);
while (soundItem->isPlaying()) {
_vm->_system->delayMillis(10);
}
delete soundItem;
}
return true;
}
bool Console::Cmd_CheckResource(int argc, const char **argv) {
const char *resourceNames[] = { "unknown", "unknown", "bitmap", "palette", "animation", "data", "text", "sound", "music", "unknown", "video" };
if (argc < 2) {
debugPrintf("Gets information about a resource\n");
debugPrintf("Usage: %s <resource hash>\n", argv[0]);
} else {
uint32 resourceHash = strtol(argv[1], nullptr, 0);
ResourceHandle handle;
_vm->_res->queryResource(resourceHash, handle);
if (!handle.isValid()) {
debugPrintf("Invalid resource hash\n");
} else {
debugPrintf("Resource type: %d (%s). Size: %d bytes\n", handle.type(), resourceNames[handle.type()], handle.size());
}
}
return true;
}
bool Console::Cmd_DumpResource(int argc, const char **argv) {
if (argc < 3) {
debugPrintf("Dumps a resource to disk\n");
debugPrintf("Usage: %s <resource hash> <output file>\n", argv[0]);
} else {
uint32 resourceHash = strtol(argv[1], nullptr, 0);
const char *outFileName = argv[2];
ResourceHandle handle;
_vm->_res->queryResource(resourceHash, handle);
if (!handle.isValid()) {
debugPrintf("Invalid resource hash\n");
} else {
_vm->_res->loadResource(handle, _vm->applyResourceFixes());
Common::DumpFile outFile;
outFile.open(outFileName);
outFile.write(handle.data(), handle.size());
outFile.finalize();
outFile.close();
_vm->_res->unloadResource(handle);
}
}
return true;
}
} // End of namespace Neverhood

View File

@@ -0,0 +1,50 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef NEVERHOOD_CONSOLE_H
#define NEVERHOOD_CONSOLE_H
#include "gui/debugger.h"
namespace Neverhood {
class NeverhoodEngine;
class Console : public GUI::Debugger {
public:
Console(NeverhoodEngine *vm);
~Console(void) override;
private:
NeverhoodEngine *_vm;
bool Cmd_Scene(int argc, const char **argv);
bool Cmd_Surfaces(int argc, const char **argv);
bool Cmd_Cheat(int argc, const char **argv);
bool Cmd_Dumpvars(int argc, const char **argv);
bool Cmd_PlaySound(int argc, const char **argv);
bool Cmd_CheckResource(int argc, const char **argv);
bool Cmd_DumpResource(int argc, const char **argv);
};
} // End of namespace Neverhood
#endif

View File

@@ -0,0 +1,4 @@
begin_section("Neverhood");
add_person("Benjamin Haisch", "john_doe", "");
add_person("Filippos Karapetis", "bluegr", "");
end_section();

View File

@@ -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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "base/plugins.h"
#include "engines/advancedDetector.h"
#include "common/file.h"
#include "neverhood/detection.h"
static const PlainGameDescriptor neverhoodGames[] = {
{"neverhood", "The Neverhood"},
{nullptr, nullptr}
};
namespace Neverhood {
static const ADGameDescription gameDescriptions[] = {
// Neverhood English version
{
"neverhood",
nullptr,
AD_ENTRY1s("hd.blb", "22958d968458c9ff221aee38577bb2b2", 4279716),
Common::EN_ANY,
Common::kPlatformWindows,
ADGF_DROPPLATFORM,
GUIO1(GUIO_NONE)
},
// Neverhood English big demo version
{
"neverhood",
"Big Demo",
AD_ENTRY1s("nevdemo.blb", "e637221d296f9a25ff22eaed96b07519", 117274189),
Common::EN_ANY,
Common::kPlatformWindows,
GF_BIG_DEMO | ADGF_DEMO | ADGF_DROPPLATFORM,
GUIO1(GUIO_NONE)
},
// Neverhood English demo version
{
"neverhood",
"Demo",
AD_ENTRY1s("nevdemo.blb", "05b735cfb1086892bec79b54dca5545b", 22564568),
Common::EN_ANY,
Common::kPlatformWindows,
ADGF_DEMO | ADGF_DROPPLATFORM,
GUIO1(GUIO_NONE)
},
// Neverhood earlier English demo version
{
"neverhood",
"Demo",
AD_ENTRY1s("nevdemo.blb", "9cbc33bc8ebacacfc8071f3e26a9c85f", 22357020),
Common::EN_ANY,
Common::kPlatformWindows,
ADGF_DEMO | ADGF_DROPPLATFORM,
GUIO1(GUIO_NONE)
},
// Neverhood lite English demo version
{
"neverhood",
"Demo",
AD_ENTRY1s("nevdemo.blb", "816741610771f6434a673b6821e88899", 11986100),
Common::EN_ANY,
Common::kPlatformWindows,
ADGF_DEMO | ADGF_DROPPLATFORM,
GUIO1(GUIO_NONE)
},
// Neverhood Russian version. Dyadyushka Risech
{
"neverhood",
"DR",
AD_ENTRY1s("hd.blb", "787951bf094aad9962291e69a707bdde", 4248635),
Common::RU_RUS,
Common::kPlatformWindows,
ADGF_DROPPLATFORM,
GUIO1(GUIO_NONE)
},
// Neverhood Russian version. Fargus
{
"neverhood",
"Fargus",
AD_ENTRY1s("hd.blb", "c87c69db423f560d3708e9de78751a7f", 4425816),
Common::RU_RUS,
Common::kPlatformWindows,
ADGF_DROPPLATFORM,
GUIO1(GUIO_NONE)
},
// Alternative Neverhood Russian translation.
{
"neverhood",
"Stream",
AD_ENTRY1s("hd.blb", "1b6bfa33c5e9e7a7cc02964e9ea7a6f8", 4540208),
Common::RU_RUS,
Common::kPlatformWindows,
ADGF_DROPPLATFORM,
GUIO1(GUIO_NONE)
},
// Neverhood Japanese version
// Bugreport #11074
{
"neverhood",
"",
AD_ENTRY1s("hd.blb", "c791725bbbc23c0f8bf78eece4555565", 4308928),
Common::JA_JPN,
Common::kPlatformWindows,
ADGF_DROPPLATFORM,
GUIO1(GUIO_NONE)
},
AD_TABLE_END_MARKER
};
} // End of namespace Neverhood
class NeverhoodMetaEngineDetection : public AdvancedMetaEngineDetection<ADGameDescription> {
public:
NeverhoodMetaEngineDetection() : AdvancedMetaEngineDetection(Neverhood::gameDescriptions, neverhoodGames) {
_guiOptions = GUIO5(GUIO_NOMIDI, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_SKIP_HALL_OF_RECORDS,
GAMEOPTION_SCALE_MAKING_OF_VIDEOS, GAMEOPTION_REPEAT_WILLIE_HINT);
}
const char *getName() const override {
return "neverhood";
}
const char *getEngineName() const override {
return "The Neverhood";
}
const char *getOriginalCopyright() const override {
return "The Neverhood (C) The Neverhood, Inc.";
}
};
REGISTER_PLUGIN_STATIC(NEVERHOOD_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, NeverhoodMetaEngineDetection);

View File

@@ -0,0 +1,38 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef NEVERHOOD_DETECTION_H
#define NEVERHOOD_DETECTION_H
namespace Neverhood {
enum NeverhoodGameFeatures {
GF_BIG_DEMO = (1 << 0)
};
#define GAMEOPTION_ORIGINAL_SAVELOAD GUIO_GAMEOPTIONS1
#define GAMEOPTION_SKIP_HALL_OF_RECORDS GUIO_GAMEOPTIONS2
#define GAMEOPTION_SCALE_MAKING_OF_VIDEOS GUIO_GAMEOPTIONS3
#define GAMEOPTION_REPEAT_WILLIE_HINT GUIO_GAMEOPTIONS4
} // End of namespace Neverhood
#endif // NEVERHOOD_DETECTION_H

View File

@@ -0,0 +1,170 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "neverhood/neverhood.h"
#include "neverhood/dialogs.h"
#include "gui/gui-manager.h"
#include "gui/message.h"
#include "gui/saveload.h"
#include "gui/ThemeEval.h"
#include "gui/widget.h"
#include "gui/widgets/popup.h"
#include "common/system.h"
#include "common/translation.h"
namespace Neverhood {
NeverhoodOptionsWidget::NeverhoodOptionsWidget(GuiObject *boss, const Common::String &name, const Common::String &domain) :
OptionsContainerWidget(boss, name, "NeverhoodGameOptionsDialog", domain),
_originalSaveLoadCheckbox(nullptr),
_skipHallOfRecordsCheckbox(nullptr),
_scaleMakingOfVideosCheckbox(nullptr),
_nhcPopUp(nullptr) {
_originalSaveLoadCheckbox = new GUI::CheckboxWidget(
widgetsBoss(),
"NeverhoodGameOptionsDialog.OriginalSaveLoad",
_("Use original save/load screens"),
_("Use the original save/load screens instead of the ScummVM ones"));
_skipHallOfRecordsCheckbox = new GUI::CheckboxWidget(
widgetsBoss(),
"NeverhoodGameOptionsDialog.SkipHallOfRecords", _("Skip the Hall of Records storyboard scenes"),
_("Allows the player to skip past the Hall of Records storyboard scenes"));
_scaleMakingOfVideosCheckbox = new GUI::CheckboxWidget(
widgetsBoss(),
"NeverhoodGameOptionsDialog.ScaleMakingOfVideos", _("Scale the making of videos to full screen"),
_("Scale the making of videos, so that they use the whole screen"));
_repeatWillieHint = new GUI::CheckboxWidget(
widgetsBoss(),
"NeverhoodGameOptionsDialog.RepeatWillieHint", _("Repeat useful Willie's hint"),
_("Repeat actual useful hint by Willie"));
Common::FSDirectory dir(ConfMan.getPath("path", _domain));
Common::Path extraPath(ConfMan.getPath("extrapath", _domain));
Common::FSDirectory extraDir(extraPath);
Common::Array<Common::FSDirectory *> langdirs = { &dir, dir.getSubDirectory("language") };
if (!extraPath.empty()) {
langdirs.push_back(&extraDir);
langdirs.push_back(extraDir.getSubDirectory("language"));
}
_nhcFiles.push_back("");
for (Common::Array<Common::FSDirectory *>::const_iterator langdir = langdirs.begin(); langdir != langdirs.end(); langdir++) {
Common::ArchiveMemberList nhcFileList;
if (!(*langdir))
continue;
(*langdir)->listMatchingMembers(nhcFileList, "*.nhc");
for (Common::ArchiveMemberList::iterator iter = nhcFileList.begin(); iter != nhcFileList.end(); ++iter) {
Common::String nhcFileName = (*iter)->getName();
nhcFileName.erase(nhcFileName.size() - 4); // remove .nhc extension
_nhcFiles.push_back(nhcFileName);
}
}
if (_nhcFiles.size() > 1) {
// I18N: NHC is a file extension
GUI::StaticTextWidget *nhcCaption = new GUI::StaticTextWidget(widgetsBoss(), "NeverhoodGameOptionsDialog.NhcDesc", _("NHC replacement:"));
nhcCaption->setAlign(Graphics::kTextAlignRight);
_nhcPopUp = new GUI::PopUpWidget(widgetsBoss(), "NeverhoodGameOptionsDialog.Nhc");
for (uint i = 0; i < _nhcFiles.size(); i++)
_nhcPopUp->appendEntry(_nhcFiles[i].empty() ? _("<original>") : Common::U32String(_nhcFiles[i]), i);
}
}
NeverhoodOptionsWidget::~NeverhoodOptionsWidget() {
}
void NeverhoodOptionsWidget::defineLayout(GUI::ThemeEval &layouts, const Common::String &layoutName, const Common::String &overlayedLayout) const {
layouts.addDialog(layoutName, overlayedLayout)
.addLayout(GUI::ThemeLayout::kLayoutVertical)
.addPadding(0, 0, 0, 0)
.addWidget("OriginalSaveLoad", "Checkbox")
.addWidget("SkipHallOfRecords", "Checkbox")
.addWidget("ScaleMakingOfVideos", "Checkbox")
.addWidget("RepeatWillieHint", "Checkbox")
.addLayout(GUI::ThemeLayout::kLayoutHorizontal)
.addPadding(0, 0, 0, 0)
.addWidget("NhcDesc", "OptionsLabel")
.addWidget("Nhc", "PopUp")
.closeLayout()
.closeLayout()
.closeDialog();
}
void NeverhoodOptionsWidget::load() {
if (_originalSaveLoadCheckbox) {
_originalSaveLoadCheckbox->setState(ConfMan.getBool("originalsaveload", _domain));
}
if (_skipHallOfRecordsCheckbox) {
_skipHallOfRecordsCheckbox->setState(ConfMan.getBool("skiphallofrecordsscenes", _domain));
}
if (_scaleMakingOfVideosCheckbox) {
_scaleMakingOfVideosCheckbox->setState(ConfMan.getBool("scalemakingofvideos", _domain));
}
if (_repeatWillieHint) {
_repeatWillieHint->setState(ConfMan.getBool("repeatwilliehint", _domain));
}
if (_nhcPopUp) {
Common::String nhcFile(ConfMan.get("nhc_file", _domain));
for (uint i = 0; i < _nhcFiles.size(); i++)
if (_nhcFiles[i].equalsIgnoreCase(nhcFile))
_nhcPopUp->setSelectedTag(i);
}
}
bool NeverhoodOptionsWidget::save() {
if (_originalSaveLoadCheckbox) {
ConfMan.setBool("originalsaveload", _originalSaveLoadCheckbox->getState(), _domain);
}
if (_skipHallOfRecordsCheckbox) {
ConfMan.setBool("skiphallofrecordsscenes", _skipHallOfRecordsCheckbox->getState(), _domain);
}
if (_scaleMakingOfVideosCheckbox) {
ConfMan.setBool("scalemakingofvideos", _scaleMakingOfVideosCheckbox->getState(), _domain);
}
if (_repeatWillieHint) {
ConfMan.setBool("repeatwilliehint", _repeatWillieHint->getState(), _domain);
}
if (_nhcPopUp) {
uint32 selectedNhcFile = _nhcPopUp->getSelectedTag();
if (selectedNhcFile < _nhcFiles.size())
ConfMan.set("nhc_file", _nhcFiles[selectedNhcFile], _domain);
}
return true;
}
} // End of namespace Neverhood

View File

@@ -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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef NEVERHOOD_DIALOGS_H
#define NEVERHOOD_DIALOGS_H
#include "neverhood/neverhood.h"
#include "common/events.h"
#include "common/str.h"
#include "common/ustr.h"
#include "engines/dialogs.h"
#include "gui/dialog.h"
#include "gui/widget.h"
namespace GUI {
class PopUpWidget;
}
namespace Neverhood {
class NeverhoodEngine;
class NeverhoodOptionsWidget : public GUI::OptionsContainerWidget {
public:
NeverhoodOptionsWidget(GuiObject *boss, const Common::String &name, const Common::String &domain);
~NeverhoodOptionsWidget() override;
// OptionsContainerWidget API
void load() override;
bool save() override;
private:
// OptionsContainerWidget API
void defineLayout(GUI::ThemeEval &layouts, const Common::String &layoutName, const Common::String &overlayedLayout) const override;
GUI::CheckboxWidget *_originalSaveLoadCheckbox;
GUI::CheckboxWidget *_skipHallOfRecordsCheckbox;
GUI::CheckboxWidget *_scaleMakingOfVideosCheckbox;
GUI::CheckboxWidget *_repeatWillieHint;
GUI::PopUpWidget *_nhcPopUp;
Common::StringArray _nhcFiles;
};
} // End of namespace Neverhood
#endif

View File

@@ -0,0 +1,520 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "neverhood/diskplayerscene.h"
#include "neverhood/mouse.h"
#include "neverhood/smackerplayer.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 NM_ANIMATION_STOP:
gotoNextState();
break;
default:
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;
default:
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(nullptr), _appearSlot(nullptr), _activeSlot(nullptr) {
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 = addSmackerPlayer(new SmackerPlayer(_vm, this, 0x08288103, false, true));
_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 NM_MOUSE_CLICK:
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 NM_CHEAT:
if (param.asInteger() == 0x2C034A29 && !_dropKey && !_hasAllDisks) { // "itsshowtime"
for (uint i = 0; i < 20; i++)
setSubVar(VA_HAS_TAPE, i, 1);
sendMessage(_parentModule, 0x1009, 0);
}
break;
case NM_ANIMATION_UPDATE:
tuneIn();
break;
case 0x2001:
stop();
break;
default:
break;
}
}
return 0;
}
void DiskplayerScene::openSmacker(uint32 fileHash, bool keepLastFrame) {
_diskSmackerPlayer->open(fileHash, keepLastFrame);
_vm->_screen->setSmackerDecoder(_diskSmackerPlayer->getSmackerDecoder());
_palette->usePalette();
}
void DiskplayerScene::stop() {
openSmacker(0x08288103, true);
_ssPlayButton->release();
_updateStatus = kUSStopped;
_diskSlots[_diskIndex]->activate();
}
void DiskplayerScene::tuneIn() {
openSmacker(0x900001C1, false);
_ssPlayButton->release();
_updateStatus = kUSTuningIn;
_diskSlots[_diskIndex]->activate();
}
void DiskplayerScene::playDisk() {
openSmacker(kDiskplayerSmackerFileHashes[_diskIndex], false);
_updateStatus = kUSPlaying;
_diskSlots[_diskIndex]->play();
}
void DiskplayerScene::playStatic() {
openSmacker(0x90000101, false);
_ssPlayButton->release();
_updateStatus = kUSPlaying;
_diskSlots[_diskIndex]->activate();
}
} // End of namespace Neverhood

View File

@@ -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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef NEVERHOOD_DISKPLAYERSCENE_H
#define NEVERHOOD_DISKPLAYERSCENE_H
#include "neverhood/neverhood.h"
#include "neverhood/resourceman.h"
#include "neverhood/scene.h"
namespace Neverhood {
class DiskplayerScene;
class SmackerPlayer;
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 openSmacker(uint32 fileHash, bool keepLastFrame);
void stop();
void tuneIn();
void playDisk();
void playStatic();
};
} // End of namespace Neverhood
#endif /* NEVERHOOD_DISKPLAYERSCENE_H */

View File

@@ -0,0 +1,158 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#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(nullptr), _messageHandlerCb(nullptr), _priority(priority), _soundResources(nullptr) {
}
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] = nullptr;
}
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

119
engines/neverhood/entity.h Normal file
View File

@@ -0,0 +1,119 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#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 != nullptr; }
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 */

View File

@@ -0,0 +1,871 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#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, nullptr), _moduleNum(-1), _prevChildObject(nullptr), _prevModuleNum(-1),
_restoreGameRequested(false), _restartGameRequested(false), _canRequestMainMenu(true),
_mainMenuRequested(false) {
// Other initializations moved to actual engine class
_vm->_soundMan->playSoundThree(0x002D0031, 0x08861079);
SetMessageHandler(&GameModule::handleMessage);
}
GameModule::~GameModule() {
_vm->_soundMan->deleteSoundGroup(0x002D0031);
delete _childObject;
_childObject = nullptr;
}
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, NM_MOUSE_MOVE, 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, NM_MOUSE_CLICK, 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, NM_MOUSE_RELEASE, mousePos);
}
}
void GameModule::handleWheelUp() {
if (_childObject) {
sendMessage(_childObject, NM_MOUSE_WHEELUP, 0);
}
}
void GameModule::handleWheelDown() {
if (_childObject) {
sendMessage(_childObject, NM_MOUSE_WHEELDOWN, 0);
}
}
void GameModule::handleSpaceKey() {
if (_childObject) {
debug(2, "GameModule::handleSpaceKey()");
sendMessage(_childObject, NM_KEYPRESS_SPACE, 0);
}
}
void GameModule::handleAsciiKey(char key) {
if (_childObject) {
debug(2, "GameModule::handleAsciiKey()");
sendMessage(_childObject, 0x000A, (uint32)key);
}
if (key == '\n' || key == '\r') {
if (!_currentCheat.empty() && _childObject) {
uint32 cheatHash = calcHash(_currentCheat.c_str());
debug(2, "GameModule: cheat=\"%s\" (0x%08x)", _currentCheat.c_str(), cheatHash);
sendMessage(_childObject, NM_CHEAT, cheatHash);
} else if (!_currentCheat.empty())
debug(2, "GameModule: cheat=\"%s\" but no child", _currentCheat.c_str());
_currentCheat.clear();
} else if (key)
_currentCheat += key;
}
void GameModule::handleKeyDown(Common::CustomEventType action) {
if (_childObject) {
if (action == kActionPause || action == kActionQuit || action == kActionSkipFull)
handleEscapeKey();
else if (action == kActionSkipPartial)
handleSpaceKey();
debug(2, "GameModule::handleKeyDown()");
sendMessage(_childObject, 0x000B, action);
}
}
void GameModule::handleEscapeKey() {
if (_vm->isDemo())
_vm->quitGame();
else if (!_prevChildObject && _canRequestMainMenu)
_mainMenuRequested = true;
else if (_childObject)
sendMessage(_childObject, NM_KEYPRESS_ESC, 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 tileSymbolOccurrence = _vm->_rnd->getRandomNumber(4 - 1) * 2 + 2;
setSubVar(VA_GOOD_DICE_NUMBERS, i, tileSymbolOccurrence);
while (tileSymbolOccurrence--)
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);
}
}
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);
}
}
byte GameModule::parseCrystalColor(char colorLetter) {
switch (colorLetter) {
case 'B':
return 4;
case 'G':
return 3;
case 'O':
return 1;
case 'R':
return 0;
case 'V':
return 5;
case 'Y':
return 2;
default:
return 0;
}
}
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 = parseCrystalColor(colorLetter), misalignedColorNum;
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;
default:
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;
_vm->_audioResourceMan->stopAllMusic();
_vm->_audioResourceMan->stopAllSounds();
_vm->_soundMan->stopAllMusic();
_vm->_soundMan->stopAllSounds();
// Reinsert turning sound because SoundMan::stopAllSounds() removes it
_vm->_soundMan->playSoundThree(0x002D0031, 0x08861079);
delete _childObject;
delete _prevChildObject;
_childObject = nullptr;
_prevChildObject = nullptr;
_prevModuleNum = 0;
createModuleByHash(getGlobalVar(V_MODULE_NAME));
}
if (_mainMenuRequested)
openMainMenu();
}
void GameModule::createModule(int moduleNum, int which) {
debug(1, "GameModule::createModule(%d, %d)", moduleNum, which);
_moduleNum = moduleNum;
delete _childObject;
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(1, "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(1, "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) {
if (_vm->isDemo())
createModule(9999, -1);
else
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;
default:
break;
}
}
}
void GameModule::openMainMenu() {
if (_childObject) {
sendMessage(_childObject, NM_MOUSE_HIDE, 0);
_childObject->draw();
} else {
// If there's no module, create one so there's something to return to
createModule(1000, 0);
}
_vm->_screen->saveParams();
_vm->_screen->update();
_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, NM_MOUSE_SHOW, 0);
_prevChildObject = nullptr;
_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

View File

@@ -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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#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);
~GameModule() override;
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 handleWheelUp();
void handleWheelDown();
void handleSpaceKey();
void handleAsciiKey(char key);
void handleKeyDown(Common::CustomEventType action);
void handleEscapeKey();
void initKeySlotsPuzzle();
void initMemoryPuzzle();
void initWaterPipesPuzzle();
void initRadioPuzzle();
void initTestTubes1Puzzle();
void initTestTubes2Puzzle();
void initCannonSymbolsPuzzle();
void initCodeSymbolsPuzzle();
void initCubeSymbolsPuzzle();
void initCrystalColorsPuzzle();
uint32 getCurrRadioMusicFileHash();
int getCurrentModuleNum() { return _moduleNum; }
int getPreviousModuleNum() { return _moduleNum; }
void createModule(int moduleNum, int which);
static byte parseCrystalColor(char colorLetter);
protected:
int _moduleNum;
Entity *_prevChildObject;
int _prevModuleNum;
bool _restoreGameRequested;
bool _restartGameRequested;
bool _canRequestMainMenu;
bool _mainMenuRequested;
Common::String _currentCheat;
uint32 handleMessage(int messageNum, const MessageParam &param, Entity *sender);
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 */

View File

@@ -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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "neverhood/console.h"
#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(Console *con) {
for (Common::Array<GameVar>::iterator it = _vars.begin(); it != _vars.end(); ++it) {
GameVar gameVar = *it;
con->debugPrintf("hash: %08X, var: %08X, first index: %3d, next index: %3d\n", gameVar.nameHash, gameVar.value, gameVar.firstIndex, gameVar.nextIndex);
}
}
} // End of namespace Neverhood

View File

@@ -0,0 +1,194 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef NEVERHOOD_GAMEVARS_H
#define NEVERHOOD_GAMEVARS_H
#include "common/array.h"
#include "common/savefile.h"
#include "neverhood/neverhood.h"
namespace Neverhood {
enum : uint {
// 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 Console;
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(Console *con);
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 */

View File

@@ -0,0 +1,358 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "neverhood/graphics.h"
#include "neverhood/resource.h"
#include "neverhood/screen.h"
namespace Neverhood {
BaseSurface::BaseSurface(NeverhoodEngine *vm, int priority, int16 width, int16 height, Common::String name)
: _vm(vm), _priority(priority), _visible(true), _transparent(true),
_clipRects(nullptr), _clipRectsCount(0), _version(0), _name(name) {
_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() {
_surface->free();
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, 0 is the transparent color
// Clipping is performed against the right/bottom border since x, y will always be >= 0
if (x + sourceRect.width > _surface->w)
sourceRect.width = _surface->w - x - 1;
if (y + sourceRect.height > _surface->h)
sourceRect.height = _surface->h - y - 1;
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, const Common::SharedPtr<BaseSurface> &shadowSurface)
: BaseSurface(vm, priority, width, height, "shadow"), _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 + 4, "font"), _charsPerRow(charsPerRow), _numRows(numRows),
_firstChar(firstChar), _charWidth(charWidth), _charHeight(charHeight), _tracking(nullptr) {
_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 + 4, "font"), _charsPerRow(charsPerRow), _numRows(numRows),
_firstChar(firstChar), _charWidth(charWidth), _charHeight(charHeight), _tracking(nullptr) {
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(const Common::SharedPtr<BaseSurface> &destSurface, int16 x, int16 y, const byte *string, int stringLen) {
BaseSurface *destSurfaceRaw = destSurface.get();
if (stringLen < 0)
stringLen = strlen((const char*)string);
for (; stringLen > 0; --stringLen, ++string) {
drawChar(destSurfaceRaw, 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 = nullptr;
if (flags & BF_HAS_IMAGE) {
if (pixels)
*pixels = sprite;
} else if (pixels)
*pixels = nullptr;
}
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;
}
if (replaceColors)
for (int xc = 0; xc < width; xc++)
if (dest[xc] == oldColor)
dest[xc] = newColor;
dest += destPitch;
}
}
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 (int)sqrt((double)(deltaX * deltaX + deltaY * deltaY));
}
} // End of namespace Neverhood

View File

@@ -0,0 +1,162 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#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;
static NRect make(int16 x01, int16 y01, int16 x02, int16 y02) {
NRect r;
r.set(x01, y01, x02, y02);
return r;
}
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, Common::String name);
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; }
const Common::String getName() const { return _name; }
protected:
NeverhoodEngine *_vm;
int _priority;
bool _visible;
Common::String _name;
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, const Common::SharedPtr<BaseSurface> &shadowSurface);
void draw() override;
protected:
Common::SharedPtr<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);
~FontSurface() override;
void drawChar(BaseSurface *destSurface, int16 x, int16 y, byte chr);
void drawString(const Common::SharedPtr<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 */

File diff suppressed because it is too large Load Diff

373
engines/neverhood/klaymen.h Normal file
View File

@@ -0,0 +1,373 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#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 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();
// Idle animations - start
void stIdlePickEar();
void evIdlePickEarDone();
void stIdleSpinHead();
void stIdleArms();
void evIdleArmsDone();
void stIdleChest();
void stIdleHeadOff();
void stIdleWonderAbout();
void stIdleTeleporterHands();
void stIdleTeleporterHands2();
uint32 hmIdlePickEar(int messageNum, const MessageParam &param, Entity *sender);
uint32 hmIdleSpinHead(int messageNum, const MessageParam &param, Entity *sender);
uint32 hmIdleArms(int messageNum, const MessageParam &param, Entity *sender);
uint32 hmIdleChest(int messageNum, const MessageParam &param, Entity *sender);
uint32 hmIdleHeadOff(int messageNum, const MessageParam &param, Entity *sender);
// Idle animations - end
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 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 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 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 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 stFallSkipJump();
void suFallSkipJump();
void stMoveObject();
void stContinueMoveObject();
uint32 hmMoveObject(int messageNum, const MessageParam &param, Entity *sender);
void upMoveObject();
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 stStandIdleSpecial();
uint32 hmStandIdleSpecial(int messageNum, const MessageParam &param, Entity *sender);
void stSpitOutFall0();
void stSpitOutFall2();
void suFallDown();
void upSpitOutFall();
void stFalling();
void stFallTouchdown();
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 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;
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();
};
} // End of namespace Neverhood
#endif /* NEVERHOOD_KLAYMEN_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,267 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#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);
~MenuModule() override;
void setLoadgameInfo(uint index);
void setLoadgameSlot(int slot);
void setSavegameInfo(const Common::String &description, uint index, bool newSavegame);
void setDeletegameInfo(uint index);
void refreshSaveGameList();
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);
bool hasMakingOf() const;
protected:
uint32 handleMessage(int messageNum, const MessageParam &param, Entity *sender);
};
class CreditsScene : public Scene {
public:
CreditsScene(NeverhoodEngine *vm, Module *parentModule, bool canAbort);
~CreditsScene() override;
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, const Common::SharedPtr<BaseSurface> &drawSurface, int16 tx, int16 ty, const Common::SharedPtr<FontSurface> &fontSurface);
void initialize() override;
int16 getWidth() override;
int16 getHeight() override;
void drawString(int maxStringLength);
void clear();
void setString(const byte *string, int stringLen);
Common::SharedPtr<FontSurface> getFontSurface() const { return _fontSurface; }
protected:
Common::SharedPtr<BaseSurface> _drawSurface;
int16 _tx, _ty;
Common::SharedPtr<FontSurface> _fontSurface;
const byte *_string;
int _stringLen;
};
class TextEditWidget : public Widget {
public:
TextEditWidget(NeverhoodEngine *vm, int16 x, int16 y, GameStateMenu *parentScene,
int maxStringLength, const Common::SharedPtr<FontSurface> &fontSurface, uint32 fileHash, const NRect &rect);
~TextEditWidget() override;
void onClick() override;
void initialize() override;
void enterWidget() override;
void exitWidget() override;
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;
Common::SharedPtr<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, const Common::SharedPtr<FontSurface> &fontSurface, uint32 bgFileHash, const NRect &rect);
void onClick() override;
void initialize() override;
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;
Common::SharedPtr<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);
NPoint getMousePos();
virtual void setCurrWidget(Widget *newWidget);
virtual Widget *getCurrWidget() { return _currWidget; }
virtual void refreshDescriptionEdit();
protected:
Widget *_currWidget;
SavegameList *_savegameList;
Common::SharedPtr<FontSurface> _fontSurface;
SavegameListBox *_listBox;
TextEditWidget *_textEditWidget;
Common::String _savegameDescription;
uint32 handleMessage(int messageNum, const MessageParam &param, Entity *sender);
virtual void performAction();
int scummVMSaveLoadDialog(bool isSave, Common::String &saveDesc);
};
class SaveGameMenu : public GameStateMenu {
public:
SaveGameMenu(NeverhoodEngine *vm, Module *parentModule, SavegameList *savegameList);
protected:
void performAction() override;
};
class LoadGameMenu : public GameStateMenu {
public:
LoadGameMenu(NeverhoodEngine *vm, Module *parentModule, SavegameList *savegameList);
protected:
void performAction() override;
};
class DeleteGameMenu : public GameStateMenu {
public:
DeleteGameMenu(NeverhoodEngine *vm, Module *parentModule, SavegameList *savegameList);
protected:
void performAction() override;
};
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);
void displayOverwriteStrings(const Common::String &description);
};
} // End of namespace Neverhood
#endif /* NEVERHOOD_MENUMODULE_H */

View File

@@ -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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef NEVERHOOD_MESSAGES_H
#define NEVERHOOD_MESSAGES_H
namespace Neverhood {
enum NeverhoodMessage {
NM_MOUSE_MOVE = 0x0000,
NM_MOUSE_CLICK = 0x0001,
NM_MOUSE_RELEASE = 0x0002,
NM_MOUSE_HIDE = 0x101D,
NM_MOUSE_SHOW = 0x101E,
NM_KEYPRESS_SPACE = 0x0009,
NM_KEYPRESS_ESC = 0x000C,
NM_CHEAT = 0x000D,
NM_ANIMATION_START = 0x100D,
NM_SCENE_LEAVE = 0x1019,
NM_PRIORITY_CHANGE = 0x1022,
NM_ANIMATION_UPDATE = 0x2000,
NM_POSITION_CHANGE = 0x2002,
NM_KLAYMEN_CLIMB_LADDER = 0x2005,
NM_KLAYMEN_STOP_CLIMBING = 0x2006,
NM_CAR_MOVE_TO_PREV_POINT = 0x2007,
NM_CAR_MOVE_TO_NEXT_POINT = 0x2008,
NM_CAR_ENTER = 0x2009,
NM_CAR_LEAVE = 0x200A,
NM_CAR_TURN = 0x200E,
NM_CAR_AT_HOME = 0x200F,
NM_ANIMATION_STOP = 0x3002,
NM_KLAYMEN_STAND_IDLE = 0x4004,
NM_KLAYMEN_USE_OBJECT = 0x4806,
NM_KLAYMEN_RAISE_LEVER = 0x4807,
NM_KLAYMEN_OPEN_DOOR = 0x4808,
NM_KLAYMEN_CLOSE_DOOR = 0x4809,
NM_KLAYMEN_MOVE_OBJECT = 0x480A,
NM_KLAYMEN_LOWER_LEVER = 0x480F,
NM_KLAYMEN_PICKUP = 0x4812,
NM_KLAYMEN_PRESS_BUTTON = 0x4816,
NM_KLAYMEN_INSERT_DISK = 0x481A,
NM_KLAYMEN_TURN_TO_USE = 0x481D,
NM_KLAYMEN_RETURN_FROM_USE = 0x481E,
NM_KLAYMEN_RELEASE_LEVER = 0x4827,
NM_MOVE_TO_BACK = 0x482A,
NM_MOVE_TO_FRONT = 0x482B,
// New to ScummVM
NM_MOUSE_WHEELUP = 0xF000,
NM_MOUSE_WHEELDOWN = 0xF001
};
} // End of namespace Neverhood
#endif /* NEVERHOOD_MESSAGES_H */

View File

@@ -0,0 +1,249 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "base/plugins.h"
#include "engines/advancedDetector.h"
#include "common/file.h"
#include "neverhood/dialogs.h"
#include "neverhood/neverhood.h"
#include "neverhood/detection.h"
#include "common/translation.h"
#include "backends/keymapper/action.h"
#include "backends/keymapper/keymapper.h"
#include "backends/keymapper/standard-actions.h"
namespace Neverhood {
const char *NeverhoodEngine::getGameId() const {
return _gameDescription->gameId;
}
Common::Platform NeverhoodEngine::getPlatform() const {
return _gameDescription->platform;
}
Common::Language NeverhoodEngine::getLanguage() const {
return _gameDescription->language;
}
bool NeverhoodEngine::isDemo() const {
return _gameDescription->flags & ADGF_DEMO;
}
bool NeverhoodEngine::isBigDemo() const {
return _gameDescription->flags & GF_BIG_DEMO;
}
bool NeverhoodEngine::applyResourceFixes() const {
return getLanguage() == Common::RU_RUS;
}
} // End of namespace Neverhood
class NeverhoodMetaEngine : public AdvancedMetaEngine<ADGameDescription> {
public:
const char *getName() const override {
return "neverhood";
}
GUI::OptionsContainerWidget *buildEngineOptionsWidget(GUI::GuiObject *boss, const Common::String &name, const Common::String &target) const override {
return new Neverhood::NeverhoodOptionsWidget(boss, name, target);
}
bool hasFeature(MetaEngineFeature f) const override;
Common::Error createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const override;
SaveStateList listSaves(const char *target) const override;
int getMaximumSaveSlot() const override;
bool removeSaveState(const char *target, int slot) const override;
SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const override;
Common::KeymapArray initKeymaps(const char *target) const override;
};
bool NeverhoodMetaEngine::hasFeature(MetaEngineFeature f) const {
return
(f == kSupportsListSaves) ||
(f == kSupportsLoadingDuringStartup) ||
(f == kSupportsDeleteSave) ||
(f == kSavesSupportMetaInfo) ||
(f == kSavesSupportThumbnail) ||
(f == kSavesSupportCreationDate) ||
(f == kSavesSupportPlayTime) ||
(f == kSimpleSavesNames);
}
bool Neverhood::NeverhoodEngine::hasFeature(EngineFeature f) const {
return
(f == kSupportsReturnToLauncher) ||
(f == kSupportsLoadingDuringRuntime) ||
(f == kSupportsSavingDuringRuntime);
}
Common::Error NeverhoodMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
*engine = new Neverhood::NeverhoodEngine(syst, desc);
return Common::kNoError;
}
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());
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, header) == Neverhood::NeverhoodEngine::kRSHENoError) {
saveList.push_back(SaveStateDescriptor(this, slotNum, header.description));
}
delete in;
}
}
}
// Sort saves based on slot number.
Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
int NeverhoodMetaEngine::getMaximumSaveSlot() const {
return 999;
}
bool NeverhoodMetaEngine::removeSaveState(const char *target, int slot) const {
Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
Common::String filename = Neverhood::NeverhoodEngine::getSavegameFilename(target, slot);
return 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, header, false);
delete in;
if (error == Neverhood::NeverhoodEngine::kRSHENoError) {
SaveStateDescriptor desc(this, slot, header.description);
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();
}
Common::KeymapArray NeverhoodMetaEngine::initKeymaps(const char *target) const {
using namespace Common;
using namespace Neverhood;
Common::String extra = ConfMan.get("extra", target);
const bool isDemo = extra.contains("Demo");
Keymap *engineKeyMap = new Keymap(Keymap::kKeymapTypeGame, "neverhood-default", _("Default keymappings"));
Keymap *gameKeymap = new Keymap(Keymap::kKeymapTypeGame, "game", _("Game keymappings"));
Keymap *saveMenuKeymap = new Keymap(Keymap::kKeymapTypeGame, "save-management", _("Save file management menus keymappings"));
Keymap *pauseKeymap = new Keymap(Keymap::kKeymapTypeGame, "pause", _("Pause menu keymappings"));
Action *act;
act = new Action(kStandardActionInteract, _("Move / Interact"));
act->setLeftClickEvent();
act->addDefaultInputMapping("MOUSE_LEFT");
act->addDefaultInputMapping("MOUSE_RIGHT");
act->addDefaultInputMapping("JOY_A");
engineKeyMap->addAction(act);
if (isDemo) {
act = new Action("QUIT", _("Quit"));
act->setCustomEngineActionEvent(kActionQuit);
act->addDefaultInputMapping("ESCAPE");
act->addDefaultInputMapping("JOY_Y");
pauseKeymap->addAction(act);
} else {
act = new Action("PAUSE", _("Pause / Exit menu"));
act->setCustomEngineActionEvent(kActionPause);
act->addDefaultInputMapping("ESCAPE");
act->addDefaultInputMapping("JOY_Y");
pauseKeymap->addAction(act);
}
// I18N: (Game name: The Neverhood) The game has multiple cutscenes, and this action skips part of the scene.
act = new Action("SKIP", _("Skip section of cutscene"));
act->setCustomEngineActionEvent(kActionSkipPartial);
act->addDefaultInputMapping("SPACE");
act->addDefaultInputMapping("JOY_B");
gameKeymap->addAction(act);
// I18N: (Game name: The Neverhood) The game has multiple cutscenes, and this action skips the entire scene.
act = new Action("SKIPCREDITS", _("Skip entire scene (works only in some scenes)"));
act->setCustomEngineActionEvent(kActionSkipFull);
act->addDefaultInputMapping("ESCAPE");
act->addDefaultInputMapping("JOY_X");
gameKeymap->addAction(act);
// I18N: (Game name: The Neverhood) This action confirms the selected save or entered new save name in the save file management menus.
act = new Action("Confirm", _("Confirm the selected save / new save name"));
act->setCustomEngineActionEvent(kActionConfirm);
act->addDefaultInputMapping("RETURN");
act->addDefaultInputMapping("JOY_X");
saveMenuKeymap->addAction(act);
KeymapArray keymaps(4);
keymaps[0] = engineKeyMap;
keymaps[1] = gameKeymap;
keymaps[2] = pauseKeymap;
keymaps[3] = saveMenuKeymap;
saveMenuKeymap->setEnabled(false);
return keymaps;
}
#if PLUGIN_ENABLED_DYNAMIC(NEVERHOOD)
REGISTER_PLUGIN_DYNAMIC(NEVERHOOD, PLUGIN_TYPE_ENGINE, NeverhoodMetaEngine);
#else
REGISTER_PLUGIN_STATIC(NEVERHOOD, PLUGIN_TYPE_ENGINE, NeverhoodMetaEngine);
#endif

View File

@@ -0,0 +1,158 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#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

View File

@@ -0,0 +1,62 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#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

View File

@@ -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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
// 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);
~Module() override;
void draw() override;
SceneType getSceneType() { return _sceneType; }
Entity *_childObject;
protected:
Module *_parentModule;
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 */

View File

@@ -0,0 +1,83 @@
MODULE := engines/neverhood
MODULE_OBJS = \
background.o \
blbarchive.o \
console.o \
dialogs.o \
diskplayerscene.o \
entity.o \
gamemodule.o \
gamevars.o \
graphics.o \
klaymen.o \
menumodule.o \
metaengine.o \
microtiles.o \
module_scene.o \
modules/module1000.o \
modules/module1000_sprites.o \
modules/module1100.o \
modules/module1100_sprites.o \
modules/module1200.o \
modules/module1200_sprites.o \
modules/module1300.o \
modules/module1300_sprites.o \
modules/module1400.o \
modules/module1400_sprites.o \
modules/module1500.o \
modules/module1600.o \
modules/module1600_sprites.o \
modules/module1700.o \
modules/module1700_sprites.o \
modules/module1800.o \
modules/module1900.o \
modules/module1900_sprites.o \
modules/module2000.o \
modules/module2000_sprites.o \
modules/module2100.o \
modules/module2100_sprites.o \
modules/module2200.o \
modules/module2200_sprites.o \
modules/module2300.o \
modules/module2400.o \
modules/module2400_sprites.o \
modules/module2500.o \
modules/module2500_sprites.o \
modules/module2600.o \
modules/module2600_sprites.o \
modules/module2700.o \
modules/module2700_sprites.o \
modules/module2800.o \
modules/module2800_sprites.o \
modules/module2900.o \
modules/module2900_sprites.o \
modules/module3000.o \
modules/module3000_sprites.o \
mouse.o \
navigationscene.o \
neverhood.o \
nhcarchive.o \
palette.o \
resource.o \
resourceman.o \
saveload.o \
scene.o \
screen.o \
smackerscene.o \
smackerplayer.o \
sound.o \
sprite.o \
staticdata.o \
subtitles.o
# This module can be built as a plugin
ifeq ($(ENABLE_NEVERHOOD), DYNAMIC_PLUGIN)
PLUGIN := 1
endif
# Include common rules
include $(srcdir)/rules.mk
# Detection objects
DETECT_OBJS += $(MODULE)/detection.o

View File

@@ -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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#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(nullptr),
_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, 0x0008, 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 = nullptr;
_sceneType = kSceneTypeNormal;
return false;
}
}
return true;
}
void Module::leaveModule(uint32 result) {
sendMessage(_parentModule, 0x1009, result);
}
} // End of namespace Neverhood

View File

@@ -0,0 +1,794 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "neverhood/modules/module1000.h"
#include "neverhood/modules/module1000_sprites.h"
#include "common/config-manager.h"
namespace Neverhood {
Module1000::Module1000(NeverhoodEngine *vm, Module *parentModule, int which)
: Module(vm, parentModule) {
_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(1, "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;
default:
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() && !_vm->isBigDemo())
// 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;
default:
break;
}
}
}
Scene1001::Scene1001(NeverhoodEngine *vm, Module *parentModule, int which)
: Scene(vm, parentModule), _asDoor(nullptr), _asWindow(nullptr) {
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 NM_ANIMATION_START:
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 NM_POSITION_CHANGE:
setRectList(0x004B49F0);
break;
case 0x480B:
sendMessage(_asWindow, 0x2001, 0);
break;
case NM_KLAYMEN_LOWER_LEVER:
sendMessage(_asHammer, 0x2000, 0);
break;
default:
break;
}
return messageResult;
}
Scene1002::Scene1002(NeverhoodEngine *vm, Module *parentModule, int which)
: Scene(vm, parentModule), _isKlaymenFloor(false), _isClimbingLadder(false), _asKlaymenPeekHand(nullptr) {
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, NM_CAR_MOVE_TO_PREV_POINT, _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 NM_ANIMATION_START:
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 NM_ANIMATION_UPDATE:
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 NM_POSITION_CHANGE:
_messageList = nullptr;
break;
case NM_KLAYMEN_CLIMB_LADDER:
_isClimbingLadder = true;
setRectList(0x004B4418);
break;
case NM_KLAYMEN_STOP_CLIMBING:
_isClimbingLadder = false;
setRectList(0x004B43A0);
break;
case NM_KLAYMEN_USE_OBJECT:
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, NM_KLAYMEN_OPEN_DOOR, 0);
sendMessage(_asOutsideDoorBackground, NM_KLAYMEN_OPEN_DOOR, 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 NM_KLAYMEN_RAISE_LEVER:
if (sender == _asRing3) {
playSound(2);
sendMessage(_asDoor, NM_KLAYMEN_CLOSE_DOOR, 0);
sendMessage(_asOutsideDoorBackground, NM_KLAYMEN_CLOSE_DOOR, 0);
} else if (sender == _asVenusFlyTrap) {
if (getGlobalVar(V_FLYTRAP_RING_DOOR)) {
sendMessage(_asRing3, NM_KLAYMEN_RAISE_LEVER, 0);
}
}
break;
case 0x480B:
sendEntityMessage(_klaymen, 0x1014, _asDoorSpy);
break;
case NM_KLAYMEN_LOWER_LEVER:
setGlobalVar(V_RADIO_ENABLED, 0);
playSound(1);
sendMessage(_asDoor, NM_KLAYMEN_OPEN_DOOR, 0);
sendMessage(_asOutsideDoorBackground, NM_KLAYMEN_OPEN_DOOR, 0);
break;
case 0x8000:
setSpriteSurfacePriority(_ssCeiling, 995);
setSpriteSurfacePriority(_ssLadderArch, 995);
break;
case 0x8001:
setSpriteSurfacePriority(_ssCeiling, 1015);
setSpriteSurfacePriority(_ssLadderArch, 1015);
break;
default:
break;
}
return messageResult;
}
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 NM_ANIMATION_START:
if (param.asInteger() == 0x926500A1) {
setMessageList(0x004B7C20);
messageResult = 1;
}
break;
case NM_ANIMATION_UPDATE:
loadDataResource(0x01900A04);
break;
case 0x2001:
setRectList(0x004B7C70);
break;
case NM_POSITION_CHANGE:
sendMessage(_asTrashCan, NM_POSITION_CHANGE, 0);
break;
default:
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(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 NM_MOUSE_CLICK:
if (param.asPoint().x <= 20 || param.asPoint().x >= 620)
leaveScene(0);
break;
default:
break;
}
return 0;
}
void Scene1005::drawTextToBackground() {
uint32 textIndex = getTextIndex();
if (_vm->getLanguage() == Common::Language::JA_JPN) {
static const struct {
uint32 image_id;
int x, y;
} jp_images[] = {
/* 1 */ { 0xB1048044, 205, 39 },
/* 2 */ { 0x31048045, 201, 54 },
/* 3 */ { 0x31048046, 193, 37 },
/* 4 */ { 0x31048040, 192, 57 },
/* 5 */ { 0x3104804C, 195, 43 },
/* 6 */ { 0x31048054, 188, 32 },
/* 7 */ { 0x31048064, 201, 52 },
/* 8 */ { 0x31048004, 191, 51 },
/* 9 */ { 0x310480C4, 197, 58 },
/* 10 */ { 0xB2048044, 196, 65 },
/* 11 */ { 0x32048045, 188, 53 },
/* 12 */ { 0x32048046, 190, 71 },
/* 13 */ { 0x32048040, 210, 45 },
/* 14 */ { 0x3204804C, 199, 67 },
/* 15 */ { 0x32048054, 214, 58 },
/* 16 */ { 0x32048064, 201, 52 },
/* 17 */ { 0x32048004, 205, 57 },
/* 18 */ { 0x320480C4, 215, 65 },
/* 19 */ { 0x32048144, 223, 85 },
/* 20 */ { 0x34048045, 208, 82 },
/* 21 */ { 0x34048046, 200, 66 },
/* 22 */ { 0x34048040, 209, 76 },
/* 23 */ { 0x3404804C, 211, 80 },
/* 24 */ { 0x34048054, 211, 50 },
/* 25 */ { 0x34048064, 224, 76 },
/* 26 */ { 0x34048004, 209, 85 },
/* 27 */ { 0x340480C4, 202, 61 },
/* 28 */ { 0x34048144, 206, 79 },
/* 29 */ { 0x34048244, 219, 71 },
/* 30 */ { 0x38048046, 200, 87 },
/* 31 */ { 0x38048040, 215, 85 },
/* 32 */ { 0x3804804C, 209, 87 },
/* 33 */ { 0x38048054, 201, 62 },
/* 34 */ { 0x38048064, 213, 95 },
/* 35 */ { 0x38048004, 204, 90 },
/* 36 */ { 0x380480C4, 214, 77 },
/* 37 */ { 0x38048144, 211, 96 },
/* 38 */ { 0x38048244, 207, 91 },
/* 39 */ { 0x38048444, 207, 71 },
/* 40 */ { 0x20048040, 206, 68 },
/* 41 */ { 0x2004804C, 201, 140 },
/* 42 */ { 0x20048054, 218, 122 },
/* 43 */ { 0x20048064, 219, 107 },
/* 44 */ { 0x20048004, 214, 115 },
/* 45 */ { 0x200480C4, 193, 142 },
/* 46 */ { 0x20048144, 210, 130 },
/* 47 */ { 0x20048244, 208, 110 },
/* 48 */ { 0x20048444, 213, 112 },
/* 49 */ { 0x20048844, 221, 112 },
/* 50 */ { 0x1004804C, 207, 133 },
/* 51 */ { 0x10048054, 209, 111 },
};
if (textIndex >= ARRAYSIZE(jp_images))
textIndex = 0;
addSprite(new StaticSprite(_vm, jp_images[textIndex].image_id, kSLFSetPosition, jp_images[textIndex].x, jp_images[textIndex].y));
} else {
TextResource textResource(_vm);
const char *textStart, *textEnd;
int16 x = 188, y = 36;
FontSurface *fontSurface = FontSurface::createFontSurface(_vm, getGlobalVar(V_ENTRANCE_OPEN) ? 0x283CE401 : 0xC6604282);
textResource.load(0x80283101);
textStart = textResource.getString(textIndex, textEnd);
if (_vm->shouldOffsetFontNhc()) {
x += 15;
y += 18;
}
while (textStart < textEnd) {
fontSurface->drawString(_background->getSurface(), x, 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 = getKloggsTextIndex();
}
if (getGlobalVar(V_TEXT_FLAG1) && getGlobalVar(V_TEXT_INDEX) == textIndex) {
textIndex = getTextIndex3(textIndex);
} 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::getKloggsTextIndex() {
uint32 textIndex = getGlobalVar(V_TEXT_COUNTING_INDEX1);
if (textIndex + 1 > 10) {
textIndex = 0;
}
setGlobalVar(V_TEXT_COUNTING_INDEX1, textIndex + 1);
return textIndex + 40;
}
uint32 Scene1005::getTextIndex3(uint32 usefulHint) {
uint32 textIndex = getGlobalVar(V_TEXT_COUNTING_INDEX2);
if (textIndex + 1 > 10 && ConfMan.getBool("repeatwilliehint")) {
setGlobalVar(V_TEXT_COUNTING_INDEX2, 0);
return usefulHint;
}
if (textIndex + 1 > 10) {
textIndex = 0;
}
setGlobalVar(V_TEXT_COUNTING_INDEX2, textIndex + 1);
return textIndex + 30;
}
} // End of namespace Neverhood

View File

@@ -0,0 +1,109 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef NEVERHOOD_MODULES_MODULE1000_H
#define NEVERHOOD_MODULES_MODULE1000_H
#include "neverhood/neverhood.h"
#include "neverhood/module.h"
#include "neverhood/scene.h"
namespace Neverhood {
class Module1000 : public Module {
public:
Module1000(NeverhoodEngine *vm, Module *parentModule, int which);
~Module1000() override;
protected:
int _sceneNum;
uint32 _musicFileHash;
void createScene(int sceneNum, int which);
void updateScene();
};
class Scene1001 : public Scene {
public:
Scene1001(NeverhoodEngine *vm, Module *parentModule, int which);
~Scene1001() override;
protected:
Sprite *_asHammer;
Sprite *_asDoor;
Sprite *_asWindow;
Sprite *_asLever;
Sprite *_ssButton;
uint32 handleMessage(int messageNum, const MessageParam &param, Entity *sender);
};
class Scene1002 : public Scene {
public:
Scene1002(NeverhoodEngine *vm, Module *parentModule, int which);
~Scene1002() override;
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);
};
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();
};
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 getKloggsTextIndex();
uint32 getTextIndex3(uint32 usefulHint);
};
} // End of namespace Neverhood
#endif /* NEVERHOOD_MODULES_MODULE1000_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,261 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef NEVERHOOD_MODULES_MODULE1000_SPRITES_H
#define NEVERHOOD_MODULES_MODULE1000_SPRITES_H
#include "neverhood/neverhood.h"
#include "neverhood/module.h"
#include "neverhood/scene.h"
namespace Neverhood {
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 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 AsScene1004TrashCan : public AnimatedSprite {
public:
AsScene1004TrashCan(NeverhoodEngine *vm);
protected:
uint32 handleMessage(int messageNum, const MessageParam &param, Entity *sender);
};
class KmScene1001 : public Klaymen {
public:
KmScene1001(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
protected:
void stWakeUp();
void stSleeping();
void stPullHammerLever();
uint32 hmSleeping(int messageNum, const MessageParam &param, Entity *sender);
uint32 hmPullHammerLever(int messageNum, const MessageParam &param, Entity *sender);
uint32 xHandleMessage(int messageNum, const MessageParam &param) override;
};
class KmScene1002 : public Klaymen {
public:
KmScene1002(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
protected:
void stJumpToRing1();
void stJumpToRing2();
void stJumpToRing3();
void stJumpToRing4();
void setupJumpToRing();
void stHangOnRing();
void stHoldRing3();
void stDropFromRing();
void stJumpToRingVenusFlyTrap();
void stJumpAndFall();
void stMoveVenusFlyTrap();
void stContinueMovingVenusFlyTrap();
void evMoveVenusFlyTrapDone();
void stPressDoorButton();
void stHitByBoxingGlove();
void evHitByBoxingGloveDone();
uint32 hmJumpToRing(int messageNum, const MessageParam &param, Entity *sender);
uint32 hmJumpToRing3(int messageNum, const MessageParam &param, Entity *sender);
uint32 hmHoldRing3(int messageNum, const MessageParam &param, Entity *sender);
uint32 hmJumpToRingVenusFlyTrap(int messageNum, const MessageParam &param, Entity *sender);
uint32 hmJumpAndFall(int messageNum, const MessageParam &param, Entity *sender);
uint32 hmMoveVenusFlyTrap(int messageNum, const MessageParam &param, Entity *sender);
uint32 hmFirstMoveVenusFlyTrap(int messageNum, const MessageParam &param, Entity *sender);
uint32 hmPressDoorButton(int messageNum, const MessageParam &param, Entity *sender);
uint32 hmHitByBoxingGlove(int messageNum, const MessageParam &param, Entity *sender);
void xUpdate() override;
uint32 xHandleMessage(int messageNum, const MessageParam &param) override;
};
class KmScene1004 : public Klaymen {
public:
KmScene1004(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
protected:
void stReadNote();
uint32 hmReadNote(int messageNum, const MessageParam &param, Entity *sender);
uint32 xHandleMessage(int messageNum, const MessageParam &param) override;
};
} // End of namespace Neverhood
#endif /* NEVERHOOD_MODULES_MODULE1000_SPRITES_H */

View File

@@ -0,0 +1,543 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "neverhood/gamemodule.h"
#include "neverhood/navigationscene.h"
#include "neverhood/modules/module1100.h"
#include "neverhood/modules/module1100_sprites.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};
static const byte kNavigationTypes02[] = {1, 0, 4, 1};
debug(1, "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, kNavigationTypes02);
} else {
createNavigationScene(0x004B8490, which, kNavigationTypes02);
}
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;
default:
break;
}
SetUpdateHandler(&Module1100::updateScene);
_childObject->handleUpdate();
}
void Module1100::updateScene() {
if (!updateChild()) {
switch (_sceneNum) {
case 0:
_countdown = 0;
_vm->_soundMan->setSoundVolume(0x48498E46, 65);
_vm->_soundMan->setSoundVolume(0x50399F64, 65);
if (_moduleResult == 0) {
_vm->_soundMan->playTwoSounds(0x0002C818, 0x48498E46, 0x50399F64, 0);
createScene(1, 0);
} else if (_moduleResult == 1) {
/* NOTE This fixes a bug in the original where the "tunnel" footstep
sounds are played instead of the correct footsteps. */
_vm->_soundMan->playTwoSounds(0x0002C818, 0x41861371, 0x43A2507F, 0);
createScene(8, 0);
}
break;
case 1:
_countdown = 0;
_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;
default:
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;
default:
break;
}
}
}
static const uint32 kScene1105BackgroundFileHashes[] = {
0x20018662,
0x20014202,
0x20012202,
0x20010002 // CHECKME: This used ??
};
static const uint32 kScene1105FileHashes[] = {
0x00028006,
0x0100A425,
0x63090415,
0x082100C4,
0x0068C607,
0x00018344,
0x442090E4,
0x0400E004,
0x5020A054,
0xB14A891E
};
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 NM_MOUSE_CLICK:
if (param.asPoint().x <= 20 || param.asPoint().x >= 620) {
if (!_isActionButtonClicked && _backgroundIndex == 0) {
if (_isPanelOpen) {
_isPanelOpen = false;
_backgroundIndex = 15;
SetUpdateHandler(&Scene1105::upClosePanel);
} else
_isClosePanelDone = 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 NM_KLAYMEN_RAISE_LEVER:
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, NM_POSITION_CHANGE, 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;
default:
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::make(146, 362, 192, 403));
addCollisionSprite(_ssSymbol1UpButton);
_ssSymbol1DownButton = insertSprite<SsScene1105Button>(this, 0x42012460, NRect::make(147, 404, 191, 442));
addCollisionSprite(_ssSymbol1DownButton);
_ssSymbol2UpButton = insertSprite<SsScene1105Button>(this, 0x100030A0, NRect::make(308, 361, 355, 402));
addCollisionSprite(_ssSymbol2UpButton);
_ssSymbol2DownButton = insertSprite<SsScene1105Button>(this, 0x840228A0, NRect::make(306, 406, 352, 445));
addCollisionSprite(_ssSymbol2DownButton);
_ssSymbol3UpButton = insertSprite<SsScene1105Button>(this, 0x20000120, NRect::make(476, 358, 509, 394));
addCollisionSprite(_ssSymbol3UpButton);
_ssSymbol3DownButton = insertSprite<SsScene1105Button>(this, 0x08043121, NRect::make(463, 401, 508, 438));
addCollisionSprite(_ssSymbol3DownButton);
_ssActionButton = insertSprite<SsScene1105Button>(this, 0x8248AD35, NRect::make(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() {
Scene::update();
if (_countdown != 0 && (--_countdown == 0))
createObjects();
if (_isClosePanelDone && !isSoundPlaying(1))
leaveScene(_leaveResult);
if (_doMoveTeddy && !isSoundPlaying(2)) {
sendMessage(_asTeddyBear, NM_POSITION_CHANGE, 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 NM_ANIMATION_UPDATE:
if (param.asInteger()) {
setRectList(0x004B63A8);
_klaymen->setKlaymenIdleTable3();
} else {
setRectList(0x004B6398);
_klaymen->setKlaymenIdleTable1();
}
break;
default:
break;
}
return 0;
}
} // End of namespace Neverhood

View File

@@ -0,0 +1,84 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef NEVERHOOD_MODULES_MODULE1100_H
#define NEVERHOOD_MODULES_MODULE1100_H
#include "neverhood/neverhood.h"
#include "neverhood/module.h"
#include "neverhood/scene.h"
namespace Neverhood {
class Module1100 : public Module {
public:
Module1100(NeverhoodEngine *vm, Module *parentModule, int which);
~Module1100() override;
protected:
int _sceneNum;
int _countdown;
void createScene(int sceneNum, int which);
void updateScene();
};
class AsScene1105TeddyBear;
class SsScene1105Symbol;
class SsScene1105SymbolDie;
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 */

View File

@@ -0,0 +1,274 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "neverhood/modules/module1100_sprites.h"
namespace Neverhood {
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, NM_KLAYMEN_RAISE_LEVER, 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;
default:
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 NM_ANIMATION_UPDATE:
loadSymbolSprite();
break;
default:
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 NM_POSITION_CHANGE:
if (getGlobalVar(V_ROBOT_TARGET)) {
startAnimation(0x6B0C0432, 0, -1);
playSound(0);
} else {
startAnimation(0x65084002, 0, -1);
playSound(1);
}
break;
case NM_ANIMATION_STOP:
sendMessage(_parentScene, 0x2003, 0);
stopAnimation();
break;
default:
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;
default:
break;
}
return messageResult;
}
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 NM_ANIMATION_UPDATE:
_isSittingInTeleporter = param.asInteger() != 0;
messageResult = 1;
break;
case 0x4001:
case 0x4800:
startWalkToX(param.asPoint().x, false);
break;
case NM_KLAYMEN_STAND_IDLE:
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 NM_KLAYMEN_TURN_TO_USE:
if (_isSittingInTeleporter)
GotoState(&Klaymen::stTurnToUseInTeleporter);
break;
case NM_KLAYMEN_RETURN_FROM_USE:
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;
default:
break;
}
return messageResult;
}
} // End of namespace Neverhood

View File

@@ -0,0 +1,87 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef NEVERHOOD_MODULES_MODULE1100_SPRITES_H
#define NEVERHOOD_MODULES_MODULE1100_SPRITES_H
#include "neverhood/neverhood.h"
#include "neverhood/module.h"
#include "neverhood/scene.h"
namespace Neverhood {
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 KmScene1109 : public Klaymen {
public:
KmScene1109(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
protected:
uint32 xHandleMessage(int messageNum, const MessageParam &param) override;
};
} // End of namespace Neverhood
#endif /* NEVERHOOD_MODULES_MODULE1100_SPRITES_H */

View File

@@ -0,0 +1,471 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "neverhood/modules/module1200.h"
#include "neverhood/modules/module1200_sprites.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(1, "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;
default:
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;
default:
break;
}
}
}
static const uint32 kScene1201InitArray[] = {
1, 0, 2, 4, 5, 3, 6, 7, 8, 10, 9, 11, 13, 14, 12, 16, 17, 15
};
Scene1201::Scene1201(NeverhoodEngine *vm, Module *parentModule, int which)
: Scene(vm, parentModule), _creatureExploded(false), _asMatch(nullptr), _asTntMan(nullptr),
_asCreature(nullptr), _asTntManRope(nullptr), _asLeftDoor(nullptr), _asRightDoor(nullptr), _asTape(nullptr) {
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 = nullptr;
} 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 = nullptr;
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 NM_ANIMATION_START:
if (param.asInteger() == 0x07053000) {
_creatureExploded = true;
sendMessage(_asCreature, 0x2004, 0);
} else if (param.asInteger() == 0x140E5744)
sendMessage(_asCreature, NM_KLAYMEN_CLIMB_LADDER, 0);
else if (param.asInteger() == 0x40253C40) {
_canAcceptInput = false;
sendMessage(_asCreature, NM_KLAYMEN_STOP_CLIMBING, 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 NM_POSITION_CHANGE:
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, NM_KLAYMEN_STOP_CLIMBING, 0);
break;
default:
break;
}
return messageResult;
}
static const uint32 kScene1202Table[] = {
1, 2, 0, 4, 5, 3, 7, 8, 6, 10, 11, 9, 13, 14, 12, 16, 17, 15
};
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 NM_MOUSE_CLICK:
if ((param.asPoint().x <= 20 || param.asPoint().x >= 620) && !_isPuzzleSolved)
leaveScene(0);
break;
case NM_ANIMATION_UPDATE:
_clickedIndex = (int)param.asInteger();
break;
case NM_POSITION_CHANGE:
_counter--;
break;
default:
break;
}
return messageResult;
}
uint32 Scene1202::hmSolved(int messageNum, const MessageParam &param, Entity *sender) {
Scene::handleMessage(messageNum, param, sender);
switch (messageNum) {
case NM_MOUSE_CLICK:
if (param.asPoint().x <= 20 || param.asPoint().x >= 620)
leaveScene(0);
break;
default:
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

View File

@@ -0,0 +1,81 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef NEVERHOOD_MODULES_MODULE1200_H
#define NEVERHOOD_MODULES_MODULE1200_H
#include "neverhood/neverhood.h"
#include "neverhood/module.h"
#include "neverhood/scene.h"
namespace Neverhood {
class Module1200 : public Module {
public:
Module1200(NeverhoodEngine *vm, Module *parentModule, int which);
~Module1200() override;
protected:
int _sceneNum;
void createScene(int sceneNum, int which);
void updateScene();
};
class AsScene1201TntMan;
class Scene1201 : public Scene {
public:
Scene1201(NeverhoodEngine *vm, Module *parentModule, int which);
~Scene1201() override;
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);
};
class Scene1202 : public Scene {
public:
Scene1202(NeverhoodEngine *vm, Module *parentModule);
~Scene1202() override;
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 */

View File

@@ -0,0 +1,847 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "neverhood/modules/module1200_sprites.h"
namespace Neverhood {
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 - 20);
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(nullptr);
}
}
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 NM_KLAYMEN_USE_OBJECT:
setSubVar(VA_HAS_TAPE, _nameHash, 1);
setVisible(false);
SetMessageHandler(nullptr);
break;
default:
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 NM_ANIMATION_START:
if (param.asInteger() == 0x02060018)
playSound(0, 0x47900E06);
break;
case NM_KLAYMEN_STOP_CLIMBING:
startAnimation(0x928F0C10, 1, -1);
_newStickFrameIndex = STICK_LAST_FRAME;
break;
default:
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 NM_ANIMATION_STOP:
gotoNextState();
break;
case 0x4829:
stOpenDoor();
break;
default:
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 NM_KLAYMEN_STOP_CLIMBING:
_x = 436;
_y = 339;
startAnimation(0xA060C599, 0, -1);
setVisible(true);
break;
case NM_ANIMATION_STOP:
stopAnimation();
setVisible(false);
gotoNextState();
break;
default:
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 NM_ANIMATION_START:
if (param.asInteger() == 0x092870C0)
sendMessage(_asTntManRope, NM_KLAYMEN_STOP_CLIMBING, 0);
else if (param.asInteger() == 0x11CA0144)
playSound(0, 0x51800A04);
break;
case 0x1011:
sendMessage(_parentScene, NM_POSITION_CHANGE, 0);
messageResult = 1;
break;
case 0x480B:
if (!_isMoving) {
_sprite = (Sprite*)sender;
stMoving();
}
break;
default:
break;
}
return messageResult;
}
uint32 AsScene1201TntMan::hmComingDown(int messageNum, const MessageParam &param, Entity *sender) {
uint32 messageResult = AsScene1201TntMan::handleMessage(messageNum, param, sender);
switch (messageNum) {
case NM_ANIMATION_STOP:
gotoNextState();
break;
default:
break;
}
return messageResult;
}
void AsScene1201TntMan::suMoving() {
_x = _sprite->getX() + 100;
}
void AsScene1201TntMan::stStanding() {
startAnimation(0x654913D0, 0, -1);
SetMessageHandler(&AsScene1201TntMan::handleMessage);
SetSpriteUpdate(nullptr);
}
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;
default:
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 NM_ANIMATION_START:
if (param.asInteger() == 0x86668011)
playSound(0);
break;
default:
break;
}
return messageResult;
}
uint32 AsScene1201Match::hmOnDoorFrameMoving(int messageNum, const MessageParam &param, Entity *sender) {
uint32 messageResult = hmOnDoorFrameAboutToMove(messageNum, param, sender);
switch (messageNum) {
case NM_ANIMATION_STOP:
gotoNextState();
break;
default:
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 NM_KLAYMEN_USE_OBJECT:
setVisible(false);
setGlobalVar(V_MATCH_STATUS, 3);
break;
default:
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 NM_ANIMATION_START:
if (param.asInteger() == 0x02060018)
playSound(0, 0xCD298116);
break;
case 0x2004:
GotoState(&AsScene1201Creature::stStartReachForTntDummy);
break;
case NM_KLAYMEN_STOP_CLIMBING:
GotoState(&AsScene1201Creature::stPincerSnapKlaymen);
break;
default:
break;
}
return messageResult;
}
uint32 AsScene1201Creature::hmPincerSnap(int messageNum, const MessageParam &param, Entity *sender) {
uint32 messageResult = hmWaiting(messageNum, param, sender);
switch (messageNum) {
case NM_ANIMATION_STOP:
gotoNextState();
break;
default:
break;
}
return messageResult;
}
uint32 AsScene1201Creature::hmPincerSnapKlaymen(int messageNum, const MessageParam &param, Entity *sender) {
uint32 messageResult = Sprite::handleMessage(messageNum, param, sender);
switch (messageNum) {
case NM_ANIMATION_START:
if (param.asInteger() == 0x02060018) {
playSound(0, 0xCD298116);
sendMessage(_parentScene, 0x4814, 0);
sendMessage(_klaymen, 0x4814, 0);
}
break;
case NM_ANIMATION_STOP:
gotoNextState();
break;
default:
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 NM_KLAYMEN_CLOSE_DOOR:
stCloseDoor();
break;
default:
break;
}
return messageResult;
}
void AsScene1201LeftDoor::stCloseDoor() {
startAnimation(0x508A111B, -1, -1);
_playBackwards = true;
_newStickFrameIndex = 0;
}
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;
default:
break;
}
return messageResult;
}
uint32 AsScene1202TntItem::hmChangePosition(int messageNum, const MessageParam &param, Entity *sender) {
uint32 messageResult = Sprite::handleMessage(messageNum, param, sender);
switch (messageNum) {
case NM_ANIMATION_STOP:
gotoNextState();
break;
default:
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, NM_POSITION_CHANGE, _itemIndex);
stShowIdle();
}
static const KlaymenIdleTableItem klaymenIdleTable1201[] = {
{1, kIdleSpinHead},
{1, kIdleChest},
{1, kIdleHeadOff},
};
KmScene1201::KmScene1201(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y)
: Klaymen(vm, parentScene, x, y) {
setKlaymenIdleTable(klaymenIdleTable1201, ARRAYSIZE(klaymenIdleTable1201));
_doYHitIncr = true;
}
uint32 KmScene1201::xHandleMessage(int messageNum, const MessageParam &param) {
switch (messageNum) {
case 0x4001:
case 0x4800:
startWalkToX(param.asPoint().x, false);
break;
case NM_KLAYMEN_STAND_IDLE:
GotoState(&Klaymen::stTryStandIdle);
break;
case NM_KLAYMEN_MOVE_OBJECT:
GotoState(&Klaymen::stMoveObject);
break;
case NM_KLAYMEN_PICKUP:
GotoState(&Klaymen::stPickUpGeneric);
break;
case 0x4813:
GotoState(&KmScene1201::stFetchMatch);
break;
case 0x4814:
GotoState(&KmScene1201::stTumbleHeadless);
break;
case 0x4815:
GotoState(&KmScene1201::stCloseEyes);
break;
case NM_KLAYMEN_PRESS_BUTTON:
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 NM_KLAYMEN_TURN_TO_USE:
GotoState(&Klaymen::stTurnToUse);
break;
case NM_KLAYMEN_RETURN_FROM_USE:
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;
default:
break;
}
return 0;
}
void KmScene1201::stTumbleHeadless() {
if (!stStartActionFromIdle(AnimationCallback(&KmScene1201::stTumbleHeadless))) {
_busyStatus = 1;
_acceptInput = false;
setDoDeltaX(0);
startAnimation(0x2821C590, 0, -1);
SetUpdateHandler(&Klaymen::update);
SetMessageHandler(&KmScene1201::hmTumbleHeadless);
SetSpriteUpdate(&AnimatedSprite::updateDeltaXY);
NextState(&Klaymen::stTryStandIdle);
sendMessage(_parentScene, 0x8000, 0);
playSound(0, 0x62E0A356);
}
}
void KmScene1201::stCloseEyes() {
if (!stStartActionFromIdle(AnimationCallback(&KmScene1201::stCloseEyes))) {
_busyStatus = 1;
_acceptInput = false;
startAnimation(0x5420E254, 0, -1);
SetUpdateHandler(&Klaymen::update);
SetMessageHandler(&Klaymen::hmLowLevel);
SetSpriteUpdate(nullptr);
}
}
uint32 KmScene1201::hmMatch(int messageNum, const MessageParam &param, Entity *sender) {
uint32 messageResult = Klaymen::hmLowLevelAnimation(messageNum, param, sender);
switch (messageNum) {
case NM_ANIMATION_START:
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;
default:
break;
}
return messageResult;
}
void KmScene1201::stFetchMatch() {
if (!stStartAction(AnimationCallback(&KmScene1201::stFetchMatch))) {
_busyStatus = 0;
_acceptInput = false;
setDoDeltaX(_attachedSprite->getX() < _x ? 1 : 0);
startAnimation(0x9CAA0218, 0, -1);
SetUpdateHandler(&Klaymen::update);
SetMessageHandler(&KmScene1201::hmMatch);
SetSpriteUpdate(nullptr);
NextState(&KmScene1201::stLightMatch);
}
}
void KmScene1201::stLightMatch() {
_busyStatus = 1;
_acceptInput = false;
setDoDeltaX(_attachedSprite->getX() < _x ? 1 : 0);
startAnimation(0x1222A513, 0, -1);
SetUpdateHandler(&Klaymen::update);
SetMessageHandler(&KmScene1201::hmMatch);
SetSpriteUpdate(nullptr);
}
uint32 KmScene1201::hmTumbleHeadless(int messageNum, const MessageParam &param, Entity *sender) {
uint32 messageResult = Klaymen::hmLowLevelAnimation(messageNum, param, sender);
switch (messageNum) {
case NM_ANIMATION_START:
if (param.asInteger() == 0x000F0082) {
playSound(0, 0x74E2810F);
}
break;
default:
break;
}
return messageResult;
}
} // End of namespace Neverhood

View File

@@ -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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef NEVERHOOD_MODULES_MODULE1200_SPRITES_H
#define NEVERHOOD_MODULES_MODULE1200_SPRITES_H
#include "neverhood/neverhood.h"
#include "neverhood/module.h"
#include "neverhood/scene.h"
namespace Neverhood {
// Used for both the scene sprites and the scene itself (for clipping)
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}
};
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);
~AsScene1201TntMan() override;
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() override;
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);
};
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 KmScene1201 : public Klaymen {
public:
KmScene1201(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
protected:
void stCloseEyes();
void stTumbleHeadless();
void stFetchMatch();
void stLightMatch();
uint32 hmTumbleHeadless(int messageNum, const MessageParam &param, Entity *sender);
uint32 hmMatch(int messageNum, const MessageParam &param, Entity *sender);
uint32 xHandleMessage(int messageNum, const MessageParam &param) override;
};
} // End of namespace Neverhood
#endif /* NEVERHOOD_MODULES_MODULE1200_SPRITES_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,166 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef NEVERHOOD_MODULES_MODULE1300_H
#define NEVERHOOD_MODULES_MODULE1300_H
#include "neverhood/neverhood.h"
#include "neverhood/module.h"
#include "neverhood/scene.h"
namespace Neverhood {
class SmackerPlayer;
class Module1300 : public Module {
public:
Module1300(NeverhoodEngine *vm, Module *parentModule, int which);
~Module1300() override;
protected:
int _sceneNum;
uint32 _musicFileHash;
void createScene(int sceneNum, int which);
void updateScene();
};
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 Scene1303 : public Scene {
public:
Scene1303(NeverhoodEngine *vm, Module *parentModule);
protected:
Sprite *_sprite1;
Sprite *_asBalloon;
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 Scene1306 : public Scene {
public:
Scene1306(NeverhoodEngine *vm, Module *parentModule, int which);
~Scene1306() override;
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 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 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 */

View File

@@ -0,0 +1,966 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "neverhood/modules/module1300_sprites.h"
namespace Neverhood {
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 NM_ANIMATION_STOP:
gotoNextState();
break;
case NM_KLAYMEN_OPEN_DOOR:
stLowerBridge();
break;
case NM_KLAYMEN_CLOSE_DOOR:
stRaiseBridge();
break;
default:
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(nullptr);
}
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 NM_KLAYMEN_OPEN_DOOR:
playSound(0);
SetMessageHandler(nullptr);
SetSpriteUpdate(&SsScene1302Fence::suMoveDown);
break;
case NM_KLAYMEN_CLOSE_DOOR:
playSound(1);
SetMessageHandler(nullptr);
SetSpriteUpdate(&SsScene1302Fence::suMoveUp);
break;
default:
break;
}
return messageResult;
}
void SsScene1302Fence::suMoveDown() {
if (_y < _firstY + 152)
_y += 8;
else {
SetMessageHandler(&SsScene1302Fence::handleMessage);
SetSpriteUpdate(nullptr);
}
}
void SsScene1302Fence::suMoveUp() {
if (_y > _firstY)
_y -= 8;
else {
SetMessageHandler(&SsScene1302Fence::handleMessage);
SetSpriteUpdate(nullptr);
}
}
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 NM_ANIMATION_UPDATE:
stPopBalloon();
break;
default:
break;
}
return messageResult;
}
uint32 AsScene1303Balloon::hmBalloonPopped(int messageNum, const MessageParam &param, Entity *sender) {
uint32 messageResult = Sprite::handleMessage(messageNum, param, sender);
switch (messageNum) {
case NM_ANIMATION_START:
if (param.asInteger() == 0x020B0003)
playSound(0, 0x742B0055);
break;
case NM_ANIMATION_STOP:
playSound(0, 0x470007EE);
stopAnimation();
setVisible(false);
SetMessageHandler(nullptr);
break;
default:
break;
}
return messageResult;
}
void AsScene1303Balloon::stPopBalloon() {
startAnimation(0xAC004CD0, 0, -1);
SetMessageHandler(&AsScene1303Balloon::hmBalloonPopped);
}
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 NM_KLAYMEN_USE_OBJECT:
setGlobalVar(V_HAS_NEEDLE, 1);
setVisible(false);
SetMessageHandler(nullptr);
break;
default:
break;
}
return messageResult;
}
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 && _asElevatorDoor->getVisible()) {
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 NM_ANIMATION_STOP:
gotoNextState();
break;
case NM_KLAYMEN_OPEN_DOOR:
if (_isDown)
stGoingUp();
break;
default:
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, NM_KLAYMEN_OPEN_DOOR, 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, NM_KLAYMEN_CLOSE_DOOR, 0);
stopAnimation();
SetUpdateHandler(&AsScene1306Elevator::update);
}
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 NM_ANIMATION_UPDATE:
_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;
default:
break;
}
return messageResult;
}
void AsScene1307Key::suRemoveKey() {
if (_pointIndex < kAsScene1307KeyPointsCount) {
_x += kAsScene1307KeyPoints[_pointIndex].x;
_y += kAsScene1307KeyPoints[_pointIndex].y;
updateBounds();
_pointIndex++;
} else {
SetSpriteUpdate(nullptr);
}
}
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(nullptr);
sendMessage(_parentScene, NM_POSITION_CHANGE, 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, NM_PRIORITY_CHANGE, 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, NM_PRIORITY_CHANGE, 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;
}
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 NM_ANIMATION_STOP:
gotoNextState();
break;
case NM_KLAYMEN_OPEN_DOOR:
stOpenDoor();
break;
case NM_KLAYMEN_CLOSE_DOOR:
stCloseDoor();
break;
default:
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 NM_ANIMATION_STOP:
gotoNextState();
break;
default:
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 NM_POSITION_CHANGE:
stFadeIn();
break;
case 0x2003:
stFadeOut();
break;
case NM_ANIMATION_STOP:
gotoNextState();
break;
default:
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 NM_ANIMATION_START:
if (param.asInteger() == 0x66382026)
playSound(0, 0x0CD84468);
else if (param.asInteger() == 0x6E28061C)
playSound(0, 0x78C8402C);
else if (param.asInteger() == 0x462F0410)
playSound(0, 0x60984E28);
break;
default:
break;
}
return messageResult;
}
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;
default:
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 NM_KLAYMEN_STAND_IDLE:
GotoState(&Klaymen::stTryStandIdle);
break;
case NM_KLAYMEN_PICKUP:
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;
default:
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 NM_KLAYMEN_STAND_IDLE:
GotoState(&Klaymen::stTryStandIdle);
break;
case 0x4804:
GotoState(&KmScene1305::stCrashDown);
break;
case 0x4817:
setDoDeltaX(param.asInteger());
gotoNextStateExt();
break;
default:
break;
}
return 0;
}
void KmScene1305::stCrashDown() {
playSound(0, 0x41648271);
_busyStatus = 1;
_acceptInput = false;
startAnimationByHash(0x000BAB02, 0x88003000, 0);
SetUpdateHandler(&Klaymen::update);
SetSpriteUpdate(nullptr);
SetMessageHandler(&Klaymen::hmLowLevelAnimation);
NextState(&KmScene1305::stCrashDownFinished);
}
void KmScene1305::stCrashDownFinished() {
setDoDeltaX(2);
stTryStandIdle();
}
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 NM_ANIMATION_UPDATE:
_isSittingInTeleporter = param.asInteger() != 0;
messageResult = 1;
break;
case 0x4001:
case 0x4800:
startWalkToX(param.asPoint().x, false);
break;
case NM_KLAYMEN_STAND_IDLE:
if (_isSittingInTeleporter)
GotoState(&Klaymen::stSitIdleTeleporter);
else
GotoState(&Klaymen::stTryStandIdle);
break;
case NM_KLAYMEN_PICKUP:
if (param.asInteger() == 2)
GotoState(&Klaymen::stPickUpNeedle);
else if (param.asInteger() == 1)
GotoState(&Klaymen::stPickUpTube);
else
GotoState(&Klaymen::stPickUpGeneric);
break;
case NM_KLAYMEN_PRESS_BUTTON:
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 NM_KLAYMEN_INSERT_DISK:
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 NM_KLAYMEN_TURN_TO_USE:
if (_isSittingInTeleporter)
GotoState(&Klaymen::stTurnToUseInTeleporter);
else
GotoState(&Klaymen::stTurnToUse);
break;
case NM_KLAYMEN_RETURN_FROM_USE:
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;
default:
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 NM_KLAYMEN_STAND_IDLE:
GotoState(&Klaymen::stTryStandIdle);
break;
case NM_KLAYMEN_MOVE_OBJECT:
if (param.asInteger() == 1)
GotoState(&Klaymen::stMoveObjectSkipTurnFaceObject);
else
GotoState(&Klaymen::stMoveObjectFaceObject);
break;
case 0x480D:
GotoState(&Klaymen::stUseLever);
break;
case NM_KLAYMEN_PICKUP:
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 NM_KLAYMEN_INSERT_DISK:
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 NM_KLAYMEN_TURN_TO_USE:
GotoState(&Klaymen::stTurnToUse);
break;
case NM_KLAYMEN_RETURN_FROM_USE:
GotoState(&Klaymen::stReturnFromUse);
break;
case NM_KLAYMEN_RELEASE_LEVER:
GotoState(&Klaymen::stReleaseLever);
break;
case 0x4834:
GotoState(&Klaymen::stStepOver);
break;
case 0x483F:
startSpecialWalkRight(param.asInteger());
break;
case 0x4840:
startSpecialWalkLeft(param.asInteger());
break;
default:
break;
}
return 0;
}
} // End of namespace Neverhood

View File

@@ -0,0 +1,198 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef NEVERHOOD_MODULES_MODULE1300_SPRITES_H
#define NEVERHOOD_MODULES_MODULE1300_SPRITES_H
#include "neverhood/neverhood.h"
#include "neverhood/module.h"
#include "neverhood/scene.h"
namespace Neverhood {
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 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 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 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 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 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 KmScene1303 : public Klaymen {
public:
KmScene1303(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
protected:
uint32 xHandleMessage(int messageNum, const MessageParam &param) override;
};
class KmScene1304 : public Klaymen {
public:
KmScene1304(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
protected:
uint32 xHandleMessage(int messageNum, const MessageParam &param) override;
};
class KmScene1305 : public Klaymen {
public:
KmScene1305(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
protected:
void stCrashDown();
void stCrashDownFinished();
uint32 xHandleMessage(int messageNum, const MessageParam &param) override;
};
class KmScene1306 : public Klaymen {
public:
KmScene1306(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
protected:
uint32 xHandleMessage(int messageNum, const MessageParam &param) override;
};
class KmScene1308 : public Klaymen {
public:
KmScene1308(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
protected:
uint32 xHandleMessage(int messageNum, const MessageParam &param) override;
};
} // End of namespace Neverhood
#endif /* NEVERHOOD_MODULES_MODULE1300_SPRITES_H */

View File

@@ -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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "neverhood/diskplayerscene.h"
#include "neverhood/gamemodule.h"
#include "neverhood/modules/module1000_sprites.h"
#include "neverhood/modules/module1200_sprites.h"
#include "neverhood/modules/module1400.h"
#include "neverhood/modules/module1400_sprites.h"
#include "neverhood/modules/module2100_sprites.h"
#include "neverhood/modules/module2200_sprites.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(1, "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;
default:
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;
default:
break;
}
}
}
Scene1401::Scene1401(NeverhoodEngine *vm, Module *parentModule, int which)
: Scene(vm, parentModule), _projectorBorderFlag(false), _ssFloorButton(nullptr), _asProjector(nullptr),
_asPipe(nullptr), _asMouse(nullptr), _asCheese(nullptr), _asBackDoor(nullptr),
_sprite1(nullptr), _sprite2(nullptr), _sprite3(nullptr), _ssButton(nullptr) {
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, NM_MOVE_TO_FRONT, 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 NM_ANIMATION_START:
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 NM_SCENE_LEAVE:
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, NM_KLAYMEN_OPEN_DOOR, 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 NM_MOVE_TO_BACK:
_sprite1->setVisible(true);
if (_asProjector)
sendMessage(_asProjector, NM_MOVE_TO_FRONT, 0);
break;
case NM_MOVE_TO_FRONT:
_sprite1->setVisible(false);
if (_asProjector)
sendMessage(_asProjector, NM_MOVE_TO_BACK, 0);
break;
default:
break;
}
return 0;
}
Scene1402::Scene1402(NeverhoodEngine *vm, Module *parentModule, int which)
: Scene(vm, parentModule), _isShaking(false), _asPuzzleBox(nullptr), _asProjector(nullptr) {
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*)nullptr);
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 NM_ANIMATION_START:
if (param.asInteger() == 0x00F43389) {
if (getGlobalVar(V_MOUSE_PUZZLE_SOLVED))
leaveScene(0);
else {
clearRectList();
_klaymen->setVisible(false);
showMouse(false);
sendMessage(_asPuzzleBox, NM_POSITION_CHANGE, 0);
startShaking();
}
}
break;
case NM_SCENE_LEAVE:
if (param.asInteger())
leaveScene(0);
else
leaveScene(1);
break;
case NM_ANIMATION_UPDATE:
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;
default:
break;
}
return 0;
}
void Scene1402::startShaking() {
_isShaking = true;
SetUpdateHandler(&Scene1402::upShaking);
}
void Scene1402::stopShaking() {
_isShaking = false;
}
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 NM_MOUSE_CLICK:
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 NM_ANIMATION_UPDATE:
// The mouse got the cheese (nomnom)
setGlobalVar(V_MOUSE_PUZZLE_SOLVED, 1);
playSound(0, 0x68E25540);
showMouse(false);
_puzzleSolvedCountdown = 72;
break;
default:
break;
}
return 0;
}
Scene1403::Scene1403(NeverhoodEngine *vm, Module *parentModule, int which)
: Scene(vm, parentModule), _asProjector(nullptr), _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*)nullptr);
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 NM_ANIMATION_START:
if (param.asInteger() == 0x88C11390) {
setRectList(0x004B2008);
_isProjecting = true;
} else if (param.asInteger() == 0x08821382) {
sendEntityMessage(_klaymen, 0x1014, _asProjector);
setRectList(0x004B1FF8);
_isProjecting = false;
}
break;
case NM_SCENE_LEAVE:
leaveScene(0);
break;
case NM_PRIORITY_CHANGE:
if (sender == _asProjector) {
if (param.asInteger() >= 1000)
setSurfacePriority(_sprite3->getSurface(), 1100);
else
setSurfacePriority(_sprite3->getSurface(), 995);
}
break;
case NM_KLAYMEN_RAISE_LEVER:
_sprite1->setVisible(false);
break;
case NM_KLAYMEN_LOWER_LEVER:
_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;
default:
break;
}
return 0;
}
Scene1404::Scene1404(NeverhoodEngine *vm, Module *parentModule, int which)
: Scene(vm, parentModule), _asProjector(nullptr), _asKey(nullptr) {
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*)nullptr);
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 NM_ANIMATION_START:
if (param.asInteger() == 0x410650C2) {
if (_asProjector && _asProjector->getX() == 220)
setMessageList(0x004B8C40);
else
setMessageList(0x004B8CE8);
}
break;
case NM_SCENE_LEAVE:
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;
default:
break;
}
return 0;
}
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();
// Check if the player chose a wrong tile, in which case the whole grid gets reset
if (_countdown != 0 && (--_countdown == 0)) {
_tilesLeft = 48;
_tiles[_firstTileIndex]->hide(true);
_tiles[_secondTileIndex]->hide(false);
for (uint32 i = 0; i < 48; i++) {
if (getSubVar(VA_IS_TILE_MATCH, i)) {
_tiles[i]->hide(false);
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 NM_MOUSE_CLICK:
if (param.asPoint().x <= 20 || param.asPoint().x >= 620)
leaveScene(0);
break;
case NM_ANIMATION_UPDATE:
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;
default:
break;
}
return 0;
}
} // End of namespace Neverhood

View File

@@ -0,0 +1,135 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef NEVERHOOD_MODULES_MODULE1400_H
#define NEVERHOOD_MODULES_MODULE1400_H
#include "neverhood/neverhood.h"
#include "neverhood/module.h"
#include "neverhood/scene.h"
namespace Neverhood {
class Module1400 : public Module {
public:
Module1400(NeverhoodEngine *vm, Module *parentModule, int which);
~Module1400() override;
protected:
int _sceneNum;
void createScene(int sceneNum, int which);
void updateScene();
};
class AsCommonProjector;
class AsScene1201Tape;
class AsScene1405Tile;
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);
};
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();
};
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);
};
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);
};
class Scene1404 : public Scene {
public:
Scene1404(NeverhoodEngine *vm, Module *parentModule, int which);
~Scene1404() override;
protected:
Sprite *_sprite1;
Sprite *_asTape;
AsCommonProjector *_asProjector;
Sprite *_asKey;
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 */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,197 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef NEVERHOOD_MODULES_MODULE1400_SPRITES_H
#define NEVERHOOD_MODULES_MODULE1400_SPRITES_H
#include "neverhood/neverhood.h"
#include "neverhood/module.h"
#include "neverhood/scene.h"
namespace Neverhood {
class AsScene1401Pipe : public AnimatedSprite {
public:
AsScene1401Pipe(NeverhoodEngine *vm);
~AsScene1401Pipe() override;
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);
~AsCommonProjector() override;
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 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 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 Scene1405;
class AsScene1405Tile : public AnimatedSprite {
public:
AsScene1405Tile(NeverhoodEngine *vm, Scene1405 *parentScene, uint32 tileIndex);
void show();
void hide(bool playClickSound);
protected:
Scene1405 *_parentScene;
bool _isShowing;
uint32 _tileIndex;
int _countdown;
void update();
uint32 handleMessage(int messageNum, const MessageParam &param, Entity *sender);
};
class KmScene1401 : public Klaymen {
public:
KmScene1401(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
protected:
uint32 xHandleMessage(int messageNum, const MessageParam &param) override;
};
class KmScene1402 : public Klaymen {
public:
KmScene1402(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
protected:
uint32 xHandleMessage(int messageNum, const MessageParam &param) override;
};
class KmScene1403 : public Klaymen {
public:
KmScene1403(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
protected:
uint32 xHandleMessage(int messageNum, const MessageParam &param) override;
};
class KmScene1404 : public Klaymen {
public:
KmScene1404(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
protected:
uint32 xHandleMessage(int messageNum, const MessageParam &param) override;
};
} // End of namespace Neverhood
#endif /* NEVERHOOD_MODULES_MODULE1400_SPRITES_H */

View File

@@ -0,0 +1,137 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#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(1, "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;
default:
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 || _skip) {
_vm->_screen->clear();
leaveScene(0);
}
} else if ((_countdown2 != 0 && (--_countdown2 == 0)) || (_countdown2 == 0 && !isSoundPlaying(0)) || _skip) {
_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 NM_KEYPRESS_SPACE:
_skip = true;
break;
default:
break;
}
return messageResult;
}
} // End of namespace Neverhood

View File

@@ -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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
// 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 */

View File

@@ -0,0 +1,609 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "neverhood/gamemodule.h"
#include "neverhood/modules/module1200_sprites.h"
#include "neverhood/modules/module1600.h"
#include "neverhood/modules/module1600_sprites.h"
#include "neverhood/modules/module2200_sprites.h"
#include "neverhood/modules/module3000_sprites.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(1, "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;
default:
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;
default:
break;
}
}
}
Scene1608::Scene1608(NeverhoodEngine *vm, Module *parentModule, int which)
: Scene(vm, parentModule), _asCar(nullptr), _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
playSound(0, calcHash("fxDoorOpen23"));
_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, NM_POSITION_CHANGE, _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, NM_POSITION_CHANGE, 0);
sendMessage(_asCar, NM_CAR_MOVE_TO_NEXT_POINT, 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(_clipRect3);
_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, NM_CAR_ENTER, 0);
_asCar->handleUpdate();
_klaymen = nullptr;
_carStatus = 0;
}
updateKlaymenCliprect();
}
void Scene1608::upCarAtHome() {
Scene::update();
if (_mouseClicked) {
if (_mouseClickPos.x <= 329 && _asCar->getX() == 375 && _asCar->getY() == 227) {
sendMessage(_asCar, NM_CAR_LEAVE, 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->getY() < 330) {
if (_carClipFlag) {
_carClipFlag = false;
_asCar->setClipRect(_clipRect1);
if (!_asCar->isDoDeltaX())
sendMessage(_asCar, NM_CAR_TURN, 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 NM_ANIMATION_START:
if (param.asInteger() == 0x20250B1A) {
clearRectList();
_klaymen->setVisible(false);
showMouse(false);
_sprite1->setVisible(false);
playSound(0, calcHash("fxDoorClose23"));
_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;
default:
break;
}
return 0;
}
uint32 Scene1608::hmUpperFloor(int messageNum, const MessageParam &param, Entity *sender) {
Scene::handleMessage(messageNum, param, sender);
switch (messageNum) {
case NM_ANIMATION_START:
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;
default:
break;
}
return 0;
}
uint32 Scene1608::hmRidingCar(int messageNum, const MessageParam &param, Entity *sender) {
Scene::handleMessage(messageNum, param, sender);
switch (messageNum) {
case NM_KLAYMEN_CLIMB_LADDER:
leaveScene(1);
break;
case NM_KLAYMEN_STOP_CLIMBING:
SetMessageHandler(&Scene1608::hmCarAtHome);
SetUpdateHandler(&Scene1608::upCarAtHome);
sendMessage(_asCar, NM_CAR_AT_HOME, 1);
break;
case 0x200D:
sendMessage(_parentModule, 0x200D, 0);
break;
default:
break;
}
return 0;
}
uint32 Scene1608::hmCarAtHome(int messageNum, const MessageParam &param, Entity *sender) {
Scene::handleMessage(messageNum, param, sender);
switch (messageNum) {
case NM_CAR_LEAVE:
_carStatus = 2;
break;
case 0x200D:
sendMessage(_parentModule, 0x200D, 0);
break;
default:
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 NM_MOUSE_CLICK:
if (param.asPoint().x <= 20 || param.asPoint().x >= 620)
leaveScene(0);
break;
case NM_ANIMATION_UPDATE:
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;
default:
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

View File

@@ -0,0 +1,99 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef NEVERHOOD_MODULES_MODULE1600_H
#define NEVERHOOD_MODULES_MODULE1600_H
#include "neverhood/neverhood.h"
#include "neverhood/module.h"
#include "neverhood/scene.h"
namespace Neverhood {
class Module1600 : public Module {
public:
Module1600(NeverhoodEngine *vm, Module *parentModule, int which);
~Module1600() override;
protected:
int _sceneNum;
void createScene(int sceneNum, int which);
void updateScene();
};
class AsCommonCar;
class Scene1608 : public Scene {
public:
Scene1608(NeverhoodEngine *vm, Module *parentModule, int which);
~Scene1608() override;
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 AsScene3011Symbol;
class Scene1609 : public Scene {
friend class Console;
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 */

View File

@@ -0,0 +1,944 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "neverhood/modules/module1600_sprites.h"
namespace Neverhood {
AsCommonCar::AsCommonCar(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y)
: AnimatedSprite(vm, 1000), _parentScene(parentScene) {
createSurface(200, 556, 328);
_x = x;
_y = y;
_destX = x;
_destY = 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 = nullptr;
_currMoveDirection = 0;
_newMoveDirection = 0;
startAnimation(0xD4220027, 0, -1);
setDoDeltaX(getGlobalVar(V_CAR_DELTA_X));
SetUpdateHandler(&AsCommonCar::update);
SetMessageHandler(&AsCommonCar::handleMessage);
SetSpriteUpdate(nullptr);
}
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 NM_SCENE_LEAVE:
SetSpriteUpdate(nullptr);
break;
case NM_POSITION_CHANGE:
// 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, NM_KLAYMEN_CLIMB_LADDER, 0);
} else if (_currPointIndex == (int)_pathPoints->size()) {
_yMoveTotalSteps = 0;
sendMessage(_parentScene, NM_KLAYMEN_STOP_CLIMBING, 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(nullptr);
} else {
if (minMatchIndex > _currPointIndex)
moveToNextPoint();
else
moveToPrevPoint();
}
} else {
_hasAgainDestPoint = true;
_againDestPoint = pt;
}
}
break;
case NM_CAR_MOVE_TO_PREV_POINT:
_yMoveTotalSteps = param.asInteger();
_steps = 0;
_isBraking = false;
_lastDistance = 640;
SetSpriteUpdate(&AsCommonCar::suMoveToPrevPoint);
break;
case NM_CAR_MOVE_TO_NEXT_POINT:
_yMoveTotalSteps = param.asInteger();
_steps = 0;
_isBraking = false;
_lastDistance = 640;
SetSpriteUpdate(&AsCommonCar::suMoveToNextPoint);
break;
case NM_CAR_ENTER:
stEnterCar();
break;
case NM_CAR_LEAVE:
stLeaveCar();
break;
case NM_CAR_TURN:
stTurnCar();
break;
case NM_CAR_AT_HOME:
stCarAtHome();
_newDeltaXType = param.asInteger();
break;
default:
break;
}
return messageResult;
}
uint32 AsCommonCar::hmAnimation(int messageNum, const MessageParam &param, Entity *sender) {
uint32 messageResult = AsCommonCar::handleMessage(messageNum, param, sender);
switch (messageNum) {
case NM_ANIMATION_START:
if (_isBusy && param.asInteger() == 0x025424A2)
gotoNextState();
break;
case NM_ANIMATION_STOP:
gotoNextState();
break;
default:
break;
}
return messageResult;
}
uint32 AsCommonCar::hmLeaveCar(int messageNum, const MessageParam &param, Entity *sender) {
switch (messageNum) {
case NM_CAR_ENTER:
stEnterCar();
break;
case NM_ANIMATION_STOP:
sendMessage(_parentScene, NM_CAR_LEAVE, 0);
SetMessageHandler(&AsCommonCar::handleMessage);
break;
default:
break;
}
return 0;
}
void AsCommonCar::stCarAtHome() {
bool doDeltaX = _doDeltaX;
SetSpriteUpdate(nullptr);
_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, NM_SCENE_LEAVE, 0);
sendMessage(_parentScene, NM_KLAYMEN_STOP_CLIMBING, 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(nullptr);
_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, NM_SCENE_LEAVE, 0);
sendMessage(_parentScene, NM_KLAYMEN_CLIMB_LADDER, 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(nullptr);
_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, NM_SCENE_LEAVE, 0);
sendMessage(_parentScene, NM_KLAYMEN_STOP_CLIMBING, 0);
return;
}
if (_isBraking) {
if (_steps <= 0) {
sendMessage(this, NM_SCENE_LEAVE, 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, NM_SCENE_LEAVE, 0);
sendMessage(_parentScene, NM_KLAYMEN_STOP_CLIMBING, 0);
}
}
}
void AsCommonCar::suMoveToPrevPoint() {
int16 newX = _x, newY = _y;
if (_currPointIndex == 0 && _stepError == 0) {
_yMoveTotalSteps = 0;
sendMessage(this, NM_SCENE_LEAVE, 0);
sendMessage(_parentScene, NM_KLAYMEN_CLIMB_LADDER, 0);
return;
}
if (_isBraking) {
if (_steps <= 0) {
sendMessage(this, NM_SCENE_LEAVE, 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, NM_SCENE_LEAVE, 0);
sendMessage(_parentScene, NM_KLAYMEN_CLIMB_LADDER, 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;
}
}
}
}
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 NM_KLAYMEN_STAND_IDLE:
if (_isSittingInTeleporter)
GotoState(&Klaymen::stSitIdleTeleporter);
else
GotoState(&Klaymen::stTryStandIdle);
break;
case NM_KLAYMEN_PICKUP:
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 NM_KLAYMEN_TURN_TO_USE:
if (_isSittingInTeleporter)
GotoState(&Klaymen::stTurnToUseInTeleporter);
break;
case NM_KLAYMEN_RETURN_FROM_USE:
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;
default:
break;
}
return messageResult;
}
} // End of namespace Neverhood

View File

@@ -0,0 +1,125 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef NEVERHOOD_MODULES_MODULE1600_SPRITES_H
#define NEVERHOOD_MODULES_MODULE1600_SPRITES_H
#include "neverhood/neverhood.h"
#include "neverhood/module.h"
#include "neverhood/scene.h"
namespace Neverhood {
class AsCommonCar : public AnimatedSprite {
public:
AsCommonCar(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
~AsCommonCar() override;
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 KmScene1608 : public Klaymen {
public:
KmScene1608(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
protected:
uint32 xHandleMessage(int messageNum, const MessageParam &param) override;
};
} // End of namespace Neverhood
#endif /* NEVERHOOD_MODULES_MODULE1600_SPRITES_H */

View File

@@ -0,0 +1,242 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "neverhood/gamemodule.h"
#include "neverhood/modules/module1700.h"
#include "neverhood/modules/module1700_sprites.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(1, "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;
default:
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;
default:
break;
}
}
}
static const uint32 kScene1705FileHashes[] = {
0x910EA801, 0x920EA801, 0x940EA801,
0x980EA801, 0x800EA801, 0xB00EA801,
0xD00EA801, 0x100EA801, 0x900EA800,
0xD10EA801, 0x110EA801, 0x910EA800
};
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 NM_ANIMATION_UPDATE:
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;
default:
break;
}
return 0;
}
} // End of namespace Neverhood

View File

@@ -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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef NEVERHOOD_MODULES_MODULE1700_H
#define NEVERHOOD_MODULES_MODULE1700_H
#include "neverhood/neverhood.h"
#include "neverhood/module.h"
#include "neverhood/scene.h"
namespace Neverhood {
class Module1700 : public Module {
public:
Module1700(NeverhoodEngine *vm, Module *parentModule, int which);
~Module1700() override;
protected:
int _sceneNum;
void createScene(int sceneNum, int which);
void updateScene();
};
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 */

View File

@@ -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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "neverhood/modules/module1700_sprites.h"
namespace Neverhood {
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(nullptr);
}
_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 NM_KLAYMEN_USE_OBJECT:
setSubVar(VA_HAS_TAPE, _tapeIndex, 1);
setVisible(false);
SetMessageHandler(nullptr);
break;
default:
break;
}
return messageResult;
}
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 NM_ANIMATION_UPDATE:
_isSittingInTeleporter = param.asInteger() != 0;
messageResult = 1;
break;
case 0x4001:
case 0x4800:
startWalkToX(param.asPoint().x, false);
break;
case NM_KLAYMEN_STAND_IDLE:
if (_isSittingInTeleporter)
GotoState(&Klaymen::stSitIdleTeleporter);
else
GotoState(&Klaymen::stTryStandIdle);
break;
case 0x4803:
GotoState(&Klaymen::stFallSkipJump);
break;
case NM_KLAYMEN_PICKUP:
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 NM_KLAYMEN_TURN_TO_USE:
if (_isSittingInTeleporter) {
GotoState(&Klaymen::stTurnToUseInTeleporter);
}
break;
case NM_KLAYMEN_RETURN_FROM_USE:
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;
default:
break;
}
return messageResult;
}
} // End of namespace Neverhood

View File

@@ -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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef NEVERHOOD_MODULES_MODULE1700_SPRITES_H
#define NEVERHOOD_MODULES_MODULE1700_SPRITES_H
#include "neverhood/neverhood.h"
#include "neverhood/module.h"
#include "neverhood/scene.h"
namespace Neverhood {
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 KmScene1705 : public Klaymen {
public:
KmScene1705(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
protected:
uint32 xHandleMessage(int messageNum, const MessageParam &param) override;
};
} // End of namespace Neverhood
#endif /* NEVERHOOD_MODULES_MODULE1700_SPRITES_H */

View File

@@ -0,0 +1,187 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "neverhood/diskplayerscene.h"
#include "neverhood/menumodule.h"
#include "neverhood/navigationscene.h"
#include "neverhood/modules/module1800.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(1, "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;
default:
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;
default:
break;
}
} else {
switch (_sceneNum) {
case 0:
if (navigationScene()->isWalkingForward() && navigationScene()->getNavigationIndex() == 2)
_vm->_soundMan->setTwoSoundsPlayFlag(false);
break;
default:
break;
}
}
}
} // End of namespace Neverhood

View File

@@ -0,0 +1,45 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#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);
~Module1800() override;
protected:
int _sceneNum;
void createScene(int sceneNum, int which);
void updateScene();
};
} // End of namespace Neverhood
#endif /* NEVERHOOD_MODULES_MODULE1800_H */

View File

@@ -0,0 +1,264 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "neverhood/modules/module1900.h"
#include "neverhood/modules/module1900_sprites.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(1, "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;
default:
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;
default:
break;
}
}
}
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);
}
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 NM_MOUSE_CLICK:
if ((param.asPoint().x <= 20 || param.asPoint().x >= 620) &&
!_hasPlugInFailed && _moveDownCountdown == 0 && _moveUpCountdown == 0 && _countdown3 == 0) {
leaveScene(0);
}
break;
case NM_ANIMATION_UPDATE:
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;
default:
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

View File

@@ -0,0 +1,75 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef NEVERHOOD_MODULES_MODULE1900_H
#define NEVERHOOD_MODULES_MODULE1900_H
#include "neverhood/neverhood.h"
#include "neverhood/module.h"
#include "neverhood/scene.h"
namespace Neverhood {
class Module1900 : public Module {
public:
Module1900(NeverhoodEngine *vm, Module *parentModule, int which);
~Module1900() override;
protected:
int _sceneNum;
uint32 handleMessage(int messageNum, const MessageParam &param, Entity *sender);
void createScene(int sceneNum, int which);
void updateScene();
};
class Scene1901 : public Scene {
public:
Scene1901(NeverhoodEngine *vm, Module *parentModule, int which);
};
class AsScene1907Symbol;
class SsScene1907UpDownButton;
class AsScene1907WaterHint;
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 */

View File

@@ -0,0 +1,466 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "neverhood/modules/module1900.h"
#include "neverhood/modules/module1900_sprites.h"
namespace Neverhood {
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;
default:
break;
}
return messageResult;
}
uint32 AsScene1907Symbol::hmTryToPlugIn(int messageNum, const MessageParam &param, Entity *sender) {
uint32 messageResult = Sprite::handleMessage(messageNum, param, sender);
switch (messageNum) {
case NM_ANIMATION_STOP:
gotoNextState();
break;
default:
break;
}
return messageResult;
}
void AsScene1907Symbol::suTryToPlugIn() {
_currStep++;
_x -= _deltaX;
_y -= _deltaY;
if (_currStep == 16) {
_x -= _smallDeltaX;
_y -= _smallDeltaY;
SetSpriteUpdate(nullptr);
}
}
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(nullptr);
}
}
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(nullptr);
}
}
void AsScene1907Symbol::tryToPlugIn() {
_isPluggedIn = true;
_plugInTryCount++;
_newPositionIndex = _parentScene->getNextPosition();
_parentScene->setPositionFree(_currPositionIndex, true);
sendMessage(_parentScene, NM_PRIORITY_CHANGE, 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, NM_PRIORITY_CHANGE, 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(nullptr);
updateBounds();
playSound(2);
}
void AsScene1907Symbol::stPlugIn() {
playSound(0);
_currPositionIndex = _newPositionIndex;
stopAnimation();
SetMessageHandler(&AsScene1907Symbol::handleMessage);
SetSpriteUpdate(nullptr);
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;
break;
default:
break;
}
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 NM_ANIMATION_STOP:
gotoNextState();
break;
default:
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);
}
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 NM_KLAYMEN_STAND_IDLE:
GotoState(&Klaymen::stTryStandIdle);
break;
case 0x4817:
setDoDeltaX(param.asInteger());
gotoNextStateExt();
break;
case NM_KLAYMEN_TURN_TO_USE:
GotoState(&Klaymen::stTurnToUse);
break;
case NM_KLAYMEN_RETURN_FROM_USE:
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;
default:
break;
}
return 0;
}
} // End of namespace Neverhood

View File

@@ -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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef NEVERHOOD_MODULES_MODULE1900_SPRITES_H
#define NEVERHOOD_MODULES_MODULE1900_SPRITES_H
#include "neverhood/neverhood.h"
#include "neverhood/module.h"
#include "neverhood/scene.h"
namespace Neverhood {
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 KmScene1901 : public Klaymen {
public:
KmScene1901(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
protected:
uint32 xHandleMessage(int messageNum, const MessageParam &param) override;
};
} // End of namespace Neverhood
#endif /* NEVERHOOD_MODULES_MODULE1900_SPRITES_H */

View File

@@ -0,0 +1,165 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "neverhood/modules/module2000.h"
#include "neverhood/modules/module2000_sprites.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(1, "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;
default:
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;
default:
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 NM_ANIMATION_UPDATE:
if (param.asInteger()) {
setRectList(0x004B3680);
_klaymen->setKlaymenIdleTable3();
} else {
setRectList(0x004B3670);
_klaymen->setKlaymenIdleTable1();
}
break;
default:
break;
}
return 0;
}
} // End of namespace Neverhood

View File

@@ -0,0 +1,51 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef NEVERHOOD_MODULES_MODULE2000_H
#define NEVERHOOD_MODULES_MODULE2000_H
#include "neverhood/neverhood.h"
#include "neverhood/module.h"
#include "neverhood/scene.h"
namespace Neverhood {
class Module2000 : public Module {
public:
Module2000(NeverhoodEngine *vm, Module *parentModule, int which);
~Module2000() override;
protected:
int _sceneNum;
uint32 handleMessage(int messageNum, const MessageParam &param, Entity *sender);
void createScene(int sceneNum, int which);
void updateScene();
};
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 */

View File

@@ -0,0 +1,93 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "neverhood/modules/module2000_sprites.h"
namespace Neverhood {
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 NM_ANIMATION_UPDATE:
_isSittingInTeleporter = param.asInteger() != 0;
messageResult = 1;
break;
case 0x4001:
case 0x4800:
startWalkToX(param.asPoint().x, false);
break;
case NM_KLAYMEN_STAND_IDLE:
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 NM_KLAYMEN_TURN_TO_USE:
if (_isSittingInTeleporter)
GotoState(&Klaymen::stTurnToUseInTeleporter);
break;
case NM_KLAYMEN_RETURN_FROM_USE:
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;
default:
break;
}
return messageResult;
}
} // End of namespace Neverhood

View File

@@ -0,0 +1,40 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef NEVERHOOD_MODULES_MODULE2000_SPRITES_H
#define NEVERHOOD_MODULES_MODULE2000_SPRITES_H
#include "neverhood/neverhood.h"
#include "neverhood/module.h"
#include "neverhood/scene.h"
namespace Neverhood {
class KmScene2001 : public Klaymen {
public:
KmScene2001(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
protected:
uint32 xHandleMessage(int messageNum, const MessageParam &param) override;
};
} // End of namespace Neverhood
#endif /* NEVERHOOD_MODULES_MODULE2000_SPRITES_H */

View File

@@ -0,0 +1,223 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "neverhood/modules/module1200_sprites.h"
#include "neverhood/modules/module2100.h"
#include "neverhood/modules/module2100_sprites.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(1, "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;
default:
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;
default:
break;
}
}
}
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, 0x9048A093);
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, NM_KLAYMEN_CLOSE_DOOR, 0);
_doorStatus = 1;
}
} else {
if (_klaymen->getX() > 575)
_canAcceptInput = false;
if (--_countdown1 == 0) {
if (_klaymen->getX() < 480) {
sendMessage(_asDoor, NM_KLAYMEN_CLOSE_DOOR, 0);
_doorStatus = 1;
} else if (_klaymen->getX() >= 480 && _klaymen->getX() <= 575) {
_klaymen->setDoDeltaX(0);
setMessageList2(0x004B8F48);
sendMessage(_asDoor, NM_KLAYMEN_CLOSE_DOOR, 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 NM_ANIMATION_START:
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 NM_ANIMATION_UPDATE:
if (param.asInteger() != 0) {
setRectList(0x004B9008);
_klaymen->setKlaymenIdleTable3();
} else {
setRectList(0x004B8FF8);
_klaymen->setKlaymenIdleTable1();
}
break;
case 0x480B:
if (sender == _ssFloorButton && _doorStatus == 1) {
sendMessage(_asDoor, NM_KLAYMEN_OPEN_DOOR, 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;
default:
break;
}
return 0;
}
} // End of namespace Neverhood

View File

@@ -0,0 +1,59 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#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);
~Module2100() override;
protected:
int _sceneNum;
uint32 handleMessage(int messageNum, const MessageParam &param, Entity *sender);
void createScene(int sceneNum, int which);
void updateScene();
};
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 */

View File

@@ -0,0 +1,269 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "neverhood/modules/module2100_sprites.h"
namespace Neverhood {
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 NM_ANIMATION_STOP:
gotoNextState();
break;
case NM_KLAYMEN_OPEN_DOOR:
stOpenDoor();
break;
case NM_KLAYMEN_CLOSE_DOOR:
stCloseDoor();
break;
default:
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 NM_ANIMATION_STOP:
stopAnimation();
setVisible(false);
break;
default:
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, NM_PRIORITY_CHANGE, 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, NM_PRIORITY_CHANGE, 990);
loadSprite(_fileHash2, kSLFDefDrawOffset | kSLFDefPosition);
_countdown = 16;
playSound(0, _soundFileHash);
break;
default:
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 NM_ANIMATION_UPDATE:
_isSittingInTeleporter = param.asInteger() != 0;
messageResult = 1;
break;
case 0x4001:
case 0x4800:
startWalkToX(param.asPoint().x, false);
break;
case NM_KLAYMEN_STAND_IDLE:
if (_isSittingInTeleporter)
GotoState(&Klaymen::stSitIdleTeleporter);
else
GotoState(&Klaymen::stTryStandIdle);
break;
case 0x4811:
GotoState(&KmScene2101::stHitByDoor);
break;
case NM_KLAYMEN_PICKUP:
if (param.asInteger() == 2)
GotoState(&Klaymen::stPickUpNeedle);
else if (param.asInteger() == 1)
GotoState(&Klaymen::stPickUpTube);
else
GotoState(&Klaymen::stPickUpGeneric);
break;
case NM_KLAYMEN_PRESS_BUTTON:
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 NM_KLAYMEN_TURN_TO_USE:
if (_isSittingInTeleporter)
GotoState(&Klaymen::stTurnToUseInTeleporter);
break;
case NM_KLAYMEN_RETURN_FROM_USE:
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;
default:
break;
}
return messageResult;
}
uint32 KmScene2101::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 NM_ANIMATION_START:
if (param.asInteger() == 0x1A1A0785) {
playSound(0, 0x40F0A342);
} else if (param.asInteger() == 0x60428026) {
playSound(0, 0x40608A59);
}
break;
default:
break;
}
return messageResult;
}
void KmScene2101::stHitByDoor() {
_busyStatus = 1;
_acceptInput = false;
startAnimation(0x35AA8059, 0, -1);
SetUpdateHandler(&Klaymen::update);
SetMessageHandler(&KmScene2101::hmHitByDoor);
SetSpriteUpdate(&AnimatedSprite::updateDeltaXY);
playSound(0, 0x402E82D4);
}
} // End of namespace Neverhood

View File

@@ -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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef NEVERHOOD_MODULES_MODULE2100_SPRITES_H
#define NEVERHOOD_MODULES_MODULE2100_SPRITES_H
#include "neverhood/neverhood.h"
#include "neverhood/module.h"
#include "neverhood/scene.h"
namespace Neverhood {
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 KmScene2101 : public Klaymen {
public:
KmScene2101(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
void stHitByDoor();
uint32 hmHitByDoor(int messageNum, const MessageParam &param, Entity *sender);
protected:
uint32 xHandleMessage(int messageNum, const MessageParam &param) override;
};
} // End of namespace Neverhood
#endif /* NEVERHOOD_MODULES_MODULE2100_SPRITES_H */

File diff suppressed because it is too large Load Diff

View File

@@ -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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#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 {
class Module2200 : public Module {
public:
Module2200(NeverhoodEngine *vm, Module *parentModule, int which);
~Module2200() override;
protected:
int _sceneNum;
void createScene(int sceneNum, int which);
void updateScene();
void createHallOfRecordsScene(int which, uint32 hallOfRecordsInfoId);
bool shouldSkipHall();
};
class Scene2201 : public Scene {
public:
Scene2201(NeverhoodEngine *vm, Module *parentModule, int which);
~Scene2201() override;
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 Scene2202 : public Scene {
public:
Scene2202(NeverhoodEngine *vm, Module *parentModule, int which);
~Scene2202() override;
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 Scene2203 : public Scene {
public:
Scene2203(NeverhoodEngine *vm, Module *parentModule, int which);
~Scene2203() override;
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 SsCommonPressButton;
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 Scene2206 : public Scene {
public:
Scene2206(NeverhoodEngine *vm, Module *parentModule, int which);
~Scene2206() override;
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 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() override;
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() override;
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() override;
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() override;
protected:
uint32 handleMessage(int messageNum, const MessageParam &param, Entity *sender);
void readClickedColumn();
};
} // End of namespace Neverhood
#endif /* NEVERHOOD_MODULES_MODULE2200_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,274 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef NEVERHOOD_MODULES_MODULE2200_SPRITES_H
#define NEVERHOOD_MODULES_MODULE2200_SPRITES_H
#include "neverhood/neverhood.h"
#include "neverhood/module.h"
#include "neverhood/scene.h"
#include "neverhood/graphics.h"
namespace Neverhood {
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 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 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 SsScene2205DoorFrame : public StaticSprite {
public:
SsScene2205DoorFrame(NeverhoodEngine *vm);
protected:
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 AsScene2207Elevator : public AnimatedSprite {
public:
AsScene2207Elevator(NeverhoodEngine *vm, Scene *parentScene);
~AsScene2207Elevator() override;
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() override;
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 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) override;
};
class KmScene2203 : public Klaymen {
public:
KmScene2203(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
protected:
void stClayDoorOpen();
uint32 hmClayDoorOpen(int messageNum, const MessageParam &param, Entity *sender);
uint32 xHandleMessage(int messageNum, const MessageParam &param) override;
};
class KmScene2205 : public Klaymen {
public:
KmScene2205(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
protected:
void xUpdate() override;
uint32 xHandleMessage(int messageNum, const MessageParam &param) override;
};
class KmScene2206 : public Klaymen {
public:
KmScene2206(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
~KmScene2206() override;
protected:
void stRidePlatformDown();
void suRidePlatformDown();
void xUpdate() override;
uint32 xHandleMessage(int messageNum, const MessageParam &param) override;
};
class KmScene2207 : public Klaymen {
public:
KmScene2207(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
protected:
uint32 xHandleMessage(int messageNum, const MessageParam &param) override;
};
class KmScene2242 : public Klaymen {
public:
KmScene2242(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
protected:
void xUpdate() override;
uint32 xHandleMessage(int messageNum, const MessageParam &param) override;
};
class KmHallOfRecords : public Klaymen {
public:
KmHallOfRecords(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
protected:
void xUpdate() override;
uint32 xHandleMessage(int messageNum, const MessageParam &param) override;
};
class KmScene2247 : public Klaymen {
public:
KmScene2247(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
protected:
void xUpdate() override;
uint32 xHandleMessage(int messageNum, const MessageParam &param) override;
};
} // End of namespace Neverhood
#endif /* NEVERHOOD_MODULES_MODULE2200_SPRITES_H */

View File

@@ -0,0 +1,191 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "neverhood/navigationscene.h"
#include "neverhood/modules/module2300.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), _waterfallSoundVolume(0) {
_vm->_soundMan->addSoundList(0x1A214010, kModule2300SoundList);
_vm->_soundMan->setSoundListParams(kModule2300SoundList, true, 50, 600, 10, 150);
_isWaterfallRunning = getGlobalVar(V_WALL_BROKEN) != 1;
if (_isWaterfallRunning) {
_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(1, "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 (_isWaterfallRunning) {
_waterfallSoundVolume = 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, _waterfallSoundVolume);
createNavigationScene(0x004B68A8, which);
if (_isWaterfallRunning) {
_waterfallSoundVolume = 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;
default:
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() && !(_vm->isBigDemo() && _moduleResult == 4))
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;
default:
break;
}
} else {
switch (_sceneNum) {
case 1:
if (_isWaterfallRunning && navigationScene()->isWalkingForward() && navigationScene()->getNavigationIndex() == 4 &&
navigationScene()->getFrameNumber() % 2) {
_waterfallSoundVolume++;
_vm->_soundMan->setSoundVolume(0x90F0D1C3, _waterfallSoundVolume);
}
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 (_isWaterfallRunning && navigationScene()->isWalkingForward() && navigationScene()->getFrameNumber() % 2) {
_waterfallSoundVolume--;
_vm->_soundMan->setSoundVolume(0x90F0D1C3, _waterfallSoundVolume);
}
break;
default:
break;
}
}
}
} // End of namespace Neverhood

View File

@@ -0,0 +1,47 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#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);
~Module2300() override;
protected:
int _sceneNum;
int _waterfallSoundVolume;
bool _isWaterfallRunning;
void createScene(int sceneNum, int which);
void updateScene();
};
} // End of namespace Neverhood
#endif /* NEVERHOOD_MODULES_MODULE2300_H */

View File

@@ -0,0 +1,675 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "neverhood/modules/module1000_sprites.h"
#include "neverhood/modules/module1200_sprites.h"
#include "neverhood/modules/module2400.h"
#include "neverhood/modules/module2100_sprites.h"
#include "neverhood/modules/module2200_sprites.h"
#include "neverhood/modules/module2400_sprites.h"
#include "neverhood/modules/module2800_sprites.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, -1);
else
createScene(0, 0);
}
Module2400::~Module2400() {
_vm->_soundMan->deleteMusicGroup(0x202D1010);
}
void Module2400::createScene(int sceneNum, int which) {
debug(1, "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;
default:
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;
default:
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 NRect kScene2401Rects[] = {
{ 369, 331, 394, 389 },
{ 395, 331, 419, 389 },
{ 420, 331, 441, 389 },
{ 442, 331, 464, 389 },
{ 465, 331, 491, 389 }
};
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, NM_KLAYMEN_OPEN_DOOR, 0);
} else if (waterInside) {
playPipeSound(0xD0431020);
for (uint i = 0; i < 5; i++) {
sendMessage(_asWaterFlushing[i], NM_POSITION_CHANGE, 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(kScene2401FileHashes2[getSubVar(VA_CURR_WATER_PIPES_LEVEL, _pipeStatus - 5)]);
} else {
_ssWaterPipes[_pipeStatus]->setVisible(true);
_countdown1 = _pipeStatus == 4 ? 16 : 8;
playPipeSound(kScene2401FileHashes2[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 NM_ANIMATION_START:
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 NM_ANIMATION_UPDATE:
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, NM_POSITION_CHANGE, 0);
playSound(0, 0xE1130324);
}
break;
case NM_MOVE_TO_BACK:
_palette->addBasePalette(0xB103B604, 0, 65, 0);
_palette->startFadeToPalette(12);
break;
case NM_MOVE_TO_FRONT:
_palette->addBasePalette(0x91D3A391, 0, 65, 0);
_palette->startFadeToPalette(12);
break;
default:
break;
}
return messageResult;
}
void Scene2401::playPipeSound(uint32 fileHash) {
playSound(_soundToggle ? 0 : 1, fileHash);
_soundToggle = !_soundToggle;
}
static const uint32 kScene2402FileHashes[] = {
0xD0910020, 0xD0910038, 0xD0910008,
0xD0910068, 0xD09100A8
};
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, NM_KLAYMEN_OPEN_DOOR, 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 NM_ANIMATION_START:
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;
default:
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 NM_ANIMATION_START:
if (param.asInteger() == 0x040424D0)
sendEntityMessage(_klaymen, 0x1014, _ssButton);
else if (param.asInteger() == 0x180CE614)
sendEntityMessage(_klaymen, 0x1014, _asLightCord);
break;
case NM_ANIMATION_UPDATE:
_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 NM_KLAYMEN_LOWER_LEVER:
if (sender == _asLightCord)
leaveScene(2);
break;
case 0x4826:
if (sender == _asTape && !_isClimbingLadder) {
sendEntityMessage(_klaymen, 0x1014, _asTape);
setMessageList(0x004B5D98);
}
break;
default:
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 NM_ANIMATION_START:
if (param.asInteger() == 0x41062804) {
if (getGlobalVar(V_SPIKES_RETRACTED))
setMessageList(0x004B7758);
else
setMessageList(0x004B7738);
}
break;
case NM_ANIMATION_UPDATE:
_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;
default:
break;
}
return messageResult;
}
} // End of namespace Neverhood

View File

@@ -0,0 +1,111 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#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/diskplayerscene.h"
namespace Neverhood {
// Module2400
class Module2400 : public Module {
public:
Module2400(NeverhoodEngine *vm, Module *parentModule, int which);
~Module2400() override;
protected:
int _sceneNum;
void createScene(int sceneNum, int which);
void updateScene();
uint32 handleMessage(int messageNum, const MessageParam &param, Entity *sender);
};
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 Scene2402 : public Scene {
public:
Scene2402(NeverhoodEngine *vm, Module *parentModule, int which);
~Scene2402() override;
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 */

View File

@@ -0,0 +1,762 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "neverhood/modules/module2400_sprites.h"
namespace Neverhood {
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 NM_ANIMATION_START:
if (param.asInteger() == 0x120A0013)
playSound(0, kAsScene2401WaterSpitFileHashes1[_soundIndex]);
break;
case NM_ANIMATION_UPDATE:
_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 NM_ANIMATION_STOP:
stopAnimation();
setVisible(false);
break;
default:
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 NM_ANIMATION_START:
if (_isWaterFlowing && param.asInteger() == 0x02421405)
startAnimationByHash(0x10203116, 0x01084280, 0);
break;
case NM_POSITION_CHANGE:
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 NM_ANIMATION_STOP:
stopAnimation();
setVisible(false);
break;
default:
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 NM_ANIMATION_START:
if (_flushLoopCount > 0 && param.asInteger() == 0x02421405) {
startAnimationByHash(0xB8596884, 0x01084280, 0);
_flushLoopCount--;
}
break;
case NM_POSITION_CHANGE:
if (param.asInteger() > 0) {
_flushLoopCount = param.asInteger() - 1;
_countdown = _vm->_rnd->getRandomNumber(3) + 1;
}
break;
case NM_ANIMATION_STOP:
stopAnimation();
setVisible(false);
break;
default:
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 NM_ANIMATION_STOP:
gotoNextState();
break;
case NM_KLAYMEN_OPEN_DOOR:
if (!_isOpen) {
_countdown = 168;
_isOpen = true;
setVisible(true);
startAnimation(0x44687810, 0, -1);
playSound(0, calcHash("fxDoorOpen38"));
NextState(&AsScene2401Door::stDoorOpenFinished);
}
break;
default:
break;
}
return messageResult;
}
void AsScene2401Door::stDoorOpenFinished() {
stopAnimation();
setVisible(false);
}
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 NM_ANIMATION_UPDATE:
if (_isOpen)
_countdown = 144;
messageResult = _isOpen ? 1 : 0;
break;
case NM_ANIMATION_STOP:
gotoNextState();
break;
case NM_KLAYMEN_OPEN_DOOR:
_countdown = 144;
_isOpen = true;
setVisible(true);
startAnimation(0x80495831, 0, -1);
_newStickFrameIndex = STICK_LAST_FRAME;
playSound(0, calcHash("fxDoorOpen38"));
break;
default:
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 NM_ANIMATION_START:
if (param.asInteger() == 0x431EA0B0)
playSound(0);
break;
case NM_ANIMATION_STOP:
gotoNextState();
break;
default:
break;
}
return messageResult;
}
KmScene2401::KmScene2401(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y)
: Klaymen(vm, parentScene, x, y),
_canSpitPipe(false), _contSpitPipe(false), _readyToSpit(false),
_spitPipeIndex(0), _spitDestPipeIndex(0), _spitContDestPipeIndex(0) {
// 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 NM_KLAYMEN_STAND_IDLE:
GotoState(&Klaymen::stTryStandIdle);
break;
case NM_KLAYMEN_PRESS_BUTTON:
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(&KmScene2401::stTrySpitIntoPipe);
}
break;
case 0x483F:
startSpecialWalkRight(param.asInteger());
break;
case 0x4840:
startSpecialWalkLeft(param.asInteger());
break;
default:
break;
}
return messageResult;
}
uint32 KmScene2401::hmSpit(int messageNum, const MessageParam &param, Entity *sender) {
uint32 messageResult = Klaymen::hmLowLevelAnimation(messageNum, param, sender);
switch (messageNum) {
case NM_ANIMATION_START:
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;
default:
break;
}
return messageResult;
}
void KmScene2401::stTrySpitIntoPipe() {
if (_readyToSpit) {
_contSpitPipe = true;
_spitContDestPipeIndex = _spitPipeIndex;
if (_canSpitPipe)
spitIntoPipe();
} else if (!stStartAction(AnimationCallback(&KmScene2401::stTrySpitIntoPipe))) {
_busyStatus = 2;
_acceptInput = true;
_spitDestPipeIndex = _spitPipeIndex;
_readyToSpit = true;
_canSpitPipe = false;
_contSpitPipe = false;
startAnimation(0x1808B150, 0, -1);
SetUpdateHandler(&Klaymen::update);
SetMessageHandler(&KmScene2401::hmSpit);
SetSpriteUpdate(nullptr);
}
}
void KmScene2401::spitIntoPipe() {
_contSpitPipe = false;
_spitDestPipeIndex = _spitContDestPipeIndex;
_canSpitPipe = false;
_acceptInput = false;
startAnimation(0x1B08B553, 0, -1);
SetUpdateHandler(&Klaymen::update);
SetMessageHandler(&KmScene2401::hmSpit);
SetSpriteUpdate(nullptr);
NextState(&KmScene2401::stContSpitIntoPipe);
}
void KmScene2401::stContSpitIntoPipe() {
_canSpitPipe = true;
_acceptInput = true;
startAnimationByHash(0x1808B150, 0x16401CA6, 0);
SetUpdateHandler(&Klaymen::update);
SetMessageHandler(&KmScene2401::hmSpit);
SetSpriteUpdate(nullptr);
}
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 NM_KLAYMEN_STAND_IDLE:
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 NM_KLAYMEN_PICKUP:
GotoState(&Klaymen::stPickUpGeneric);
break;
case NM_KLAYMEN_PRESS_BUTTON:
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;
default:
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 NM_KLAYMEN_STAND_IDLE:
GotoState(&Klaymen::stTryStandIdle);
break;
case 0x480D:
GotoState(&Klaymen::stPullCord);
break;
case NM_KLAYMEN_PICKUP:
GotoState(&Klaymen::stPickUpGeneric);
break;
case NM_KLAYMEN_PRESS_BUTTON:
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;
default:
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 NM_KLAYMEN_STAND_IDLE:
GotoState(&Klaymen::stTryStandIdle);
break;
case 0x4804:
if (param.asInteger() != 0) {
_destX = param.asInteger();
GotoState(&Klaymen::stWalkingFirst);
} else
GotoState(&Klaymen::stPeekWall);
break;
case NM_KLAYMEN_PICKUP:
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 NM_KLAYMEN_INSERT_DISK:
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 NM_KLAYMEN_TURN_TO_USE:
GotoState(&Klaymen::stTurnToUse);
break;
case NM_KLAYMEN_RETURN_FROM_USE:
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;
default:
break;
}
return messageResult;
}
} // End of namespace Neverhood

View File

@@ -0,0 +1,138 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef NEVERHOOD_MODULES_MODULE2400_SPRITES_H
#define NEVERHOOD_MODULES_MODULE2400_SPRITES_H
#include "neverhood/neverhood.h"
#include "neverhood/module.h"
#include "neverhood/scene.h"
#include "neverhood/gamemodule.h"
namespace Neverhood {
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);
~AsScene2401FlowingWater() override;
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 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);
~AsScene2402TV() override;
protected:
Klaymen *_klaymen;
int _countdown1;
int _countdown2;
void upWait();
void upFocusKlaymen();
void stJokeFinished();
uint32 hmJoke(int messageNum, const MessageParam &param, Entity *sender);
};
class KmScene2401 : public Klaymen {
public:
KmScene2401(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
protected:
bool _canSpitPipe;
bool _contSpitPipe;
bool _readyToSpit;
uint32 _spitPipeIndex;
uint32 _spitDestPipeIndex;
uint32 _spitContDestPipeIndex;
void spitIntoPipe();
void stTrySpitIntoPipe();
void stContSpitIntoPipe();
uint32 hmSpit(int messageNum, const MessageParam &param, Entity *sender);
uint32 xHandleMessage(int messageNum, const MessageParam &param) override;
};
class KmScene2402 : public Klaymen {
public:
KmScene2402(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
protected:
uint32 xHandleMessage(int messageNum, const MessageParam &param) override;
};
class KmScene2403 : public Klaymen {
public:
KmScene2403(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
protected:
uint32 xHandleMessage(int messageNum, const MessageParam &param) override;
};
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) override;
};
} // End of namespace Neverhood
#endif /* NEVERHOOD_MODULES_MODULE2400_SPRITES_H */

View File

@@ -0,0 +1,515 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "neverhood/modules/module1600.h" // for Scene1608
#include "neverhood/modules/module1600_sprites.h"
#include "neverhood/modules/module2500.h"
#include "neverhood/modules/module2500_sprites.h"
#include "neverhood/modules/module2700.h" // for Scene2704
#include "neverhood/modules/module2700_sprites.h"
namespace Neverhood {
static const uint32 kScene2505StaticSprites[] = {
0x4000A226, 0
};
static const NRect kScene2505ClipRect = { 0, 0, 564, 480 };
static const uint32 kScene2506StaticSprites[] = {
0x4027AF02, 0
};
static const NRect kScene2506ClipRect = { 0, 0, 640, 441 };
static const uint32 kScene2508StaticSprites1[] = {
0x2F08E610, 0xD844E6A0, 0
};
static const NRect kScene2508ClipRect1 = { 0, 0, 594, 448 };
static const uint32 kScene2508StaticSprites2[] = {
0x2F08E610, 0
};
static const NRect kScene2508ClipRect2 = { 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(1, "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;
default:
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;
default:
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;
default:
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, NM_CAR_ENTER, 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, NM_CAR_ENTER, 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, NM_POSITION_CHANGE, _trackPoints->size() - 1);
if (testPoint.x < 0 || testPoint.x >= 640 || testPoint.y < 0 || testPoint.y >= 480)
sendMessage(_asCar, NM_CAR_MOVE_TO_PREV_POINT, 150);
} else {
NPoint testPoint = (*_trackPoints)[0];
sendMessage(_asCar, NM_POSITION_CHANGE, 0);
if (testPoint.x < 0 || testPoint.x >= 640 || testPoint.y < 0 || testPoint.y >= 480)
sendMessage(_asCar, NM_CAR_MOVE_TO_NEXT_POINT, 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, NM_CAR_ENTER, 0);
_asCar->handleUpdate();
_klaymen = nullptr;
_carStatus = 0;
}
updateKlaymenClipRect();
}
void Scene2501::upCarAtHome() {
Scene::update();
if (_mouseClicked) {
if (_mouseClickPos.x <= 210 && _asCar->getX() == 211 && _asCar->getY() == 400) {
sendMessage(_asCar, NM_CAR_LEAVE, 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 NM_ANIMATION_START:
if (param.asInteger() == 0x60842040)
_carStatus = 1;
break;
case 0x200D:
sendMessage(_parentModule, 0x200D, 0);
break;
default:
break;
}
return messageResult;
}
uint32 Scene2501::hmRidingCar(int messageNum, const MessageParam &param, Entity *sender) {
uint32 messageResult = Scene::handleMessage(messageNum, param, sender);
switch (messageNum) {
case NM_KLAYMEN_CLIMB_LADDER:
if (_tracks[_currTrackIndex]->which1 < 0 && _newTrackIndex >= 0)
changeTrack();
else if (_tracks[_currTrackIndex]->which1 == 0) {
SetMessageHandler(&Scene2501::hmCarAtHome);
SetUpdateHandler(&Scene2501::upCarAtHome);
sendMessage(_asCar, NM_CAR_AT_HOME, 1);
} else if (_tracks[_currTrackIndex]->which1 > 0)
leaveScene(_tracks[_currTrackIndex]->which1);
break;
case NM_KLAYMEN_STOP_CLIMBING:
if (_tracks[_currTrackIndex]->which2 < 0 && _newTrackIndex >= 0)
changeTrack();
else if (_tracks[_currTrackIndex]->which2 == 0) {
SetMessageHandler(&Scene2501::hmCarAtHome);
SetUpdateHandler(&Scene2501::upCarAtHome);
sendMessage(_asCar, NM_CAR_AT_HOME, 1);
} else if (_tracks[_currTrackIndex]->which2 > 0)
leaveScene(_tracks[_currTrackIndex]->which2);
break;
case 0x200D:
sendMessage(_parentModule, 0x200D, 0);
break;
default:
break;
}
return messageResult;
}
uint32 Scene2501::hmCarAtHome(int messageNum, const MessageParam &param, Entity *sender) {
uint32 messageResult = Scene::handleMessage(messageNum, param, sender);
switch (messageNum) {
case NM_CAR_LEAVE:
_carStatus = 2;
break;
case 0x200D:
sendMessage(_parentModule, 0x200D, 0);
break;
default:
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, NM_POSITION_CHANGE, _trackPoints->size() - 1);
else
sendMessage(_asCar, NM_POSITION_CHANGE, 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);
}
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 NM_MOUSE_CLICK:
if (param.asPoint().x <= 20 || param.asPoint().x >= 620)
leaveScene(0);
break;
default:
break;
}
return messageResult;
}
} // End of namespace Neverhood

View File

@@ -0,0 +1,88 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#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/module1600_sprites.h" // for Tracks
namespace Neverhood {
class Module2500 : public Module {
public:
Module2500(NeverhoodEngine *vm, Module *parentModule, int which);
~Module2500() override;
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 AsCommonCar;
class Scene2501 : public Scene {
public:
Scene2501(NeverhoodEngine *vm, Module *parentModule, int which);
~Scene2501() override;
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 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 */

View File

@@ -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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "neverhood/modules/module2500_sprites.h"
namespace Neverhood {
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;
default:
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 NM_ANIMATION_UPDATE:
_isSittingInTeleporter = param.asInteger() != 0;
messageResult = 1;
break;
case 0x4001:
case 0x4800:
startWalkToX(param.asPoint().x, false);
break;
case NM_KLAYMEN_STAND_IDLE:
if (_isSittingInTeleporter)
GotoState(&Klaymen::stSitIdleTeleporter);
else
GotoState(&Klaymen::stTryStandIdle);
break;
case 0x4817:
setDoDeltaX(param.asInteger());
gotoNextStateExt();
break;
case NM_KLAYMEN_TURN_TO_USE:
if (_isSittingInTeleporter)
GotoState(&Klaymen::stTurnToUseInTeleporter);
break;
case NM_KLAYMEN_RETURN_FROM_USE:
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;
default:
break;
}
return messageResult;
}
} // End of namespace Neverhood

View File

@@ -0,0 +1,50 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef NEVERHOOD_MODULES_MODULE2500_SPRITES_H
#define NEVERHOOD_MODULES_MODULE2500_SPRITES_H
#include "neverhood/neverhood.h"
#include "neverhood/module.h"
#include "neverhood/scene.h"
namespace Neverhood {
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 KmScene2501 : public Klaymen {
public:
KmScene2501(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
protected:
uint32 xHandleMessage(int messageNum, const MessageParam &param) override;
};
} // End of namespace Neverhood
#endif /* NEVERHOOD_MODULES_MODULE2500_SPRITES_H */

View File

@@ -0,0 +1,266 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "neverhood/modules/module2600.h"
#include "neverhood/modules/module2600_sprites.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(1, "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;
default:
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;
default:
break;
}
}
}
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 NM_MOUSE_CLICK:
if ((param.asPoint().x <= 20 || param.asPoint().x >= 620) && !_isBusy)
leaveScene(0);
break;
case NM_ANIMATION_UPDATE:
_isBusy = true;
break;
case 0x2001:
_isBusy = false;
sendMessage(_asWater, 0x2001, 0);
break;
case NM_POSITION_CHANGE:
_isBusy = false;
sendMessage(_asWater, NM_POSITION_CHANGE, 0);
break;
default:
break;
}
return 0;
}
} // End of namespace Neverhood

View File

@@ -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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#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);
~Module2600() override;
protected:
int _sceneNum;
void createScene(int sceneNum, int which);
void updateScene();
};
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 */

View File

@@ -0,0 +1,118 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "neverhood/modules/module2600_sprites.h"
namespace Neverhood {
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, NM_POSITION_CHANGE, 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;
default:
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, NM_POSITION_CHANGE, 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 NM_POSITION_CHANGE:
startAnimation(0x9C210C90, 0, -1);
setVisible(true);
_vm->_soundMan->playSoundLooping(0xDC2769B0);
break;
default:
break;
}
return messageResult;
}
} // End of namespace Neverhood

View File

@@ -0,0 +1,53 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef NEVERHOOD_MODULES_MODULE2600_SPRITES_H
#define NEVERHOOD_MODULES_MODULE2600_SPRITES_H
#include "neverhood/neverhood.h"
#include "neverhood/module.h"
#include "neverhood/scene.h"
namespace Neverhood {
// Module2600
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);
~AsScene2609Water() override;
protected:
uint32 handleMessage(int messageNum, const MessageParam &param, Entity *sender);
};
} // End of namespace Neverhood
#endif /* NEVERHOOD_MODULES_MODULE2600_SPRITES_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,150 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#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_sprites.h" // for Tracks
namespace Neverhood {
// Module2700
class Module2700 : public Module {
public:
Module2700(NeverhoodEngine *vm, Module *parentModule, int which);
~Module2700() override;
protected:
int _sceneNum;
int _soundIndex;
bool _radioMusicInitialized;
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 AsCommonCar;
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;
NPoint _newTrackDest;
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;
NPoint _newTrackDest;
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 */

View File

@@ -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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "neverhood/modules/module2700_sprites.h"
namespace Neverhood {
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, const Common::SharedPtr<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, const Common::SharedPtr<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, const Common::SharedPtr<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();
}
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;
default:
break;
}
return 0;
}
} // End of namespace Neverhood

View File

@@ -0,0 +1,73 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef NEVERHOOD_MODULES_MODULE2700_SPRITES_H
#define NEVERHOOD_MODULES_MODULE2700_SPRITES_H
#include "neverhood/neverhood.h"
#include "neverhood/module.h"
#include "neverhood/scene.h"
namespace Neverhood {
class SsCommonTrackShadowBackground : public StaticSprite {
public:
SsCommonTrackShadowBackground(NeverhoodEngine *vm, uint32 fileHash);
};
class AsCommonCarShadow : public AnimatedSprite {
public:
AsCommonCarShadow(NeverhoodEngine *vm, AnimatedSprite *asCar, const Common::SharedPtr<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, const Common::SharedPtr<BaseSurface> &shadowSurface, uint index);
protected:
uint _index;
Sprite *_asCar;
void update();
};
class AsCommonCarTrackShadow : public AnimatedSprite {
public:
AsCommonCarTrackShadow(NeverhoodEngine *vm, Sprite *asCar, const Common::SharedPtr<BaseSurface> &shadowSurface, int16 frameIndex);
protected:
Sprite *_asCar;
void update();
};
class KmScene2732 : public Klaymen {
public:
KmScene2732(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y);
protected:
uint32 xHandleMessage(int messageNum, const MessageParam &param) override;
};
} // End of namespace Neverhood
#endif /* NEVERHOOD_MODULES_MODULE2700_SPRITES_H */

Some files were not shown because too many files have changed in this diff Show More