diff options
Diffstat (limited to 'engines/neverhood')
79 files changed, 33168 insertions, 0 deletions
diff --git a/engines/neverhood/background.cpp b/engines/neverhood/background.cpp new file mode 100644 index 0000000000..e235eaa1b3 --- /dev/null +++ b/engines/neverhood/background.cpp @@ -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 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "neverhood/background.h" + +namespace Neverhood { + +// Background + +Background::Background(NeverhoodEngine *vm, int objectPriority) + : Entity(vm, objectPriority), _surface(NULL), _spriteResource(vm) { + // Empty +} + +Background::Background(NeverhoodEngine *vm, uint32 fileHash, int objectPriority, int surfacePriority) + : Entity(vm, objectPriority), _surface(NULL), _spriteResource(vm) { + + _spriteResource.load(fileHash); + createSurface(surfacePriority, _spriteResource.getDimensions().width, _spriteResource.getDimensions().height); + _surface->drawSpriteResource(_spriteResource); + +} + +Background::~Background() { + delete _surface; +} + +void Background::createSurface(int surfacePriority, int16 width, int16 height) { + _surface = new BaseSurface(_vm, surfacePriority, width, height); + _surface->setTransparent(false); + _spriteResource.getPosition().x = width; + _spriteResource.getPosition().y = height; +} + +void Background::load(uint32 fileHash) { + _spriteResource.load(fileHash); + if (_surface) + _surface->drawSpriteResource(_spriteResource); +} + +// DirtyBackground + +DirtyBackground::DirtyBackground(NeverhoodEngine *vm, const char *fileName, int objectPriority, int surfacePriority) + : Background(vm, objectPriority) { + + _spriteResource.load(calcHash(fileName)); + createSurface(surfacePriority, _spriteResource.getDimensions().width, _spriteResource.getDimensions().height); + _surface->drawSpriteResource(_spriteResource); +} + +DirtyBackground::DirtyBackground(NeverhoodEngine *vm, uint32 fileHash, int objectPriority, int surfacePriority) + : Background(vm, objectPriority) { + + _spriteResource.load(fileHash); + createSurface(surfacePriority, _spriteResource.getDimensions().width, _spriteResource.getDimensions().height); + _surface->drawSpriteResource(_spriteResource); +} + +void DirtyBackground::createSurface(int surfacePriority, int16 width, int16 height) { + + // TODO: Later use a DirtySurface once it is implemented + _surface = new BaseSurface(_vm, surfacePriority, width, height); + _surface->setTransparent(false); + _spriteResource.getPosition().x = width; + _spriteResource.getPosition().y = height; + +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/background.h b/engines/neverhood/background.h new file mode 100644 index 0000000000..8ac3581a51 --- /dev/null +++ b/engines/neverhood/background.h @@ -0,0 +1,57 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef NEVERHOOD_BACKGROUND_H +#define NEVERHOOD_BACKGROUND_H + +#include "neverhood/neverhood.h" +#include "neverhood/entity.h" +#include "neverhood/graphics.h" +#include "neverhood/resource.h" + +namespace Neverhood { + +class Background : public Entity { +public: + Background(NeverhoodEngine *vm, int objectPriority); + Background(NeverhoodEngine *vm, uint32 fileHash, int objectPriority, int surfacePriority); + virtual ~Background(); + BaseSurface *getSurface() { return _surface; } + void createSurface(int surfacePriority, int16 width, int16 height); + void load(uint32 fileHash); + SpriteResource& getSpriteResource() { return _spriteResource; } +protected: + BaseSurface *_surface; + SpriteResource _spriteResource; +}; + +class DirtyBackground : public Background { +public: + DirtyBackground(NeverhoodEngine *vm, const char *fileName, int objectPriority, int surfacePriority); + DirtyBackground(NeverhoodEngine *vm, uint32 fileHash, int objectPriority, int surfacePriority); + void createSurface(int surfacePriority, int16 width, int16 height); + +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_BACKGROUND_H */ diff --git a/engines/neverhood/blbarchive.cpp b/engines/neverhood/blbarchive.cpp new file mode 100644 index 0000000000..6a047cab46 --- /dev/null +++ b/engines/neverhood/blbarchive.cpp @@ -0,0 +1,116 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/dcl.h" +#include "neverhood/blbarchive.h" + +namespace Neverhood { + +BlbArchive::BlbArchive() : _extData(NULL) { +} + +BlbArchive::~BlbArchive() { + delete[] _extData; +} + +void BlbArchive::open(const Common::String &filename) { + BlbHeader header; + + _entries.clear(); + + if (!_fd.open(filename)) + error("BlbArchive::open() Could not open %s", filename.c_str()); + + header.id1 = _fd.readUint32LE(); + header.id2 = _fd.readUint16LE(); + header.extDataSize = _fd.readUint16LE(); + header.fileSize = _fd.readUint32LE(); + header.fileCount = _fd.readUint32LE(); + + if (header.id1 != 0x2004940 || header.id2 != 7 || header.fileSize != _fd.size()) + error("BlbArchive::open() %s seems to be corrupt", filename.c_str()); + + debug(2, "fileCount = %d", 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); + } + + // Load file records + for (uint i = 0; i < header.fileCount; i++) { + BlbArchiveEntry &entry = _entries[i]; + entry.type = _fd.readByte(); + entry.comprType = _fd.readByte(); + entry.extDataOfs = _fd.readUint16LE(); + entry.timeStamp = _fd.readUint32LE(); + entry.offset = _fd.readUint32LE(); + entry.diskSize = _fd.readUint32LE(); + entry.size = _fd.readUint32LE(); + debug(2, "%08X: %03d, %02X, %04X, %08X, %08X, %08X, %08X", + entry.fileHash, entry.type, entry.comprType, entry.extDataOfs, 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); + } + +} + +void BlbArchive::load(uint index, byte *buffer, uint32 size) { + BlbArchiveEntry &entry = _entries[index]; + + _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 + Common::decompressDCL(&_fd, buffer, entry.diskSize, entry.size); + break; + default: + ; + } + +} + +byte *BlbArchive::getEntryExtData(uint index) { + BlbArchiveEntry &entry = _entries[index]; + return (_extData && entry.extDataOfs != 0) ? &_extData[entry.extDataOfs - 1] : NULL; +} + +Common::SeekableReadStream *BlbArchive::createStream(uint index) { + const BlbArchiveEntry &entry = _entries[index]; + return new Common::SafeSubReadStream(&_fd, entry.offset, entry.offset + entry.diskSize); +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/blbarchive.h b/engines/neverhood/blbarchive.h new file mode 100644 index 0000000000..ddb3f0196b --- /dev/null +++ b/engines/neverhood/blbarchive.h @@ -0,0 +1,72 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef NEVERHOOD_BLBARCHIVE_H +#define NEVERHOOD_BLBARCHIVE_H + +#include "common/array.h" +#include "common/file.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; + uint16 extDataOfs; + uint32 timeStamp; + uint32 offset; + uint32 diskSize; + uint32 size; +}; + +class BlbArchive { +public: + BlbArchive(); + ~BlbArchive(); + void open(const Common::String &filename); + void load(uint index, byte *buffer, uint32 size); + byte *getEntryExtData(uint index); + 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); +private: + Common::File _fd; + Common::Array<BlbArchiveEntry> _entries; + byte *_extData; +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_BLBARCHIVE_H */ diff --git a/engines/neverhood/collisionman.cpp b/engines/neverhood/collisionman.cpp new file mode 100644 index 0000000000..a1314bfe0f --- /dev/null +++ b/engines/neverhood/collisionman.cpp @@ -0,0 +1,114 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "neverhood/collisionman.h" + +namespace Neverhood { + +static HitRect defaultHitRect = {NRect(), 0x5000}; + +CollisionMan::CollisionMan(NeverhoodEngine *vm) + : _vm(vm), _hitRects(NULL) { +} + +CollisionMan::~CollisionMan() { +} + +void CollisionMan::setHitRects(uint32 id) { + setHitRects(_vm->_staticData->getHitRectList(id)); +} + +void CollisionMan::setHitRects(HitRectList *hitRects) { + _hitRects = hitRects; + + // DEBUG + if (_hitRects) { + debug("CollisionMan::setHitRects() count = %d", _hitRects->size()); + for (HitRectList::iterator it = _hitRects->begin(); it != _hitRects->end(); it++) { + HitRect *hitRect = &(*it); + debug("(%d, %d, %d, %d) -> %04X", hitRect->rect.x1, hitRect->rect.y1, hitRect->rect.x2, hitRect->rect.y2, hitRect->type); + } + } + +} + +void CollisionMan::clearHitRects() { + _hitRects = NULL; +} + +HitRect *CollisionMan::findHitRectAtPos(int16 x, int16 y) { + if (_hitRects) { + for (HitRectList::iterator it = _hitRects->begin(); it != _hitRects->end(); it++) { + HitRect *hitRect = &(*it); + if (x >= hitRect->rect.x1 && x <= hitRect->rect.x2 && y >= hitRect->rect.y1 && y <= hitRect->rect.y2) + return hitRect; + } + } + return &defaultHitRect; +} + +void CollisionMan::addSprite(Sprite *sprite) { + int index = 0, insertIndex = -1; + for (Common::Array<Sprite*>::iterator iter = _sprites.begin(); iter != _sprites.end(); iter++) { + if ((*iter)->getPriority() > sprite->getPriority()) { + insertIndex = index; + break; + } + index++; + } + if (insertIndex >= 0) + _sprites.insert_at(insertIndex, sprite); + else + _sprites.push_back(sprite); +} + +void CollisionMan::removeSprite(Sprite *sprite) { + for (uint index = 0; index < _sprites.size(); index++) { + if (_sprites[index] == sprite) { + _sprites.remove_at(index); + break; + } + } +} + +void CollisionMan::clearSprites() { + _sprites.clear(); +} + +void CollisionMan::checkCollision(Sprite *sprite, uint16 flags, int messageNum, uint32 messageParam) { + for (Common::Array<Sprite*>::iterator iter = _sprites.begin(); iter != _sprites.end(); iter++) { + Sprite *collSprite = *iter; + if ((sprite->getFlags() & flags) && collSprite->checkCollision(sprite->getRect())) { + sprite->sendMessage(collSprite, messageNum, messageParam); + } + } +} + +void CollisionMan::save() { + // TODO +} + +void CollisionMan::restore() { + // TODO +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/collisionman.h b/engines/neverhood/collisionman.h new file mode 100644 index 0000000000..86359525b0 --- /dev/null +++ b/engines/neverhood/collisionman.h @@ -0,0 +1,57 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef NEVERHOOD_COLLISIONMAN_H +#define NEVERHOOD_COLLISIONMAN_H + +#include "neverhood/neverhood.h" +#include "neverhood/sprite.h" +#include "neverhood/staticdata.h" + +namespace Neverhood { + +class CollisionMan { +public: + CollisionMan(NeverhoodEngine *vm); + ~CollisionMan(); + void setHitRects(uint32 id); + void setHitRects(HitRectList *hitRects); + void clearHitRects(); + HitRect *findHitRectAtPos(int16 x, int16 y); + void addSprite(Sprite *sprite); + void removeSprite(Sprite *sprite); + void clearSprites(); + void checkCollision(Sprite *sprite, uint16 flags, int messageNum, uint32 messageParam); + void save(); + void restore(); + uint getSpriteCount() const { return _sprites.size(); } + Sprite *getSprite(uint index) const { return _sprites[index]; } +protected: + NeverhoodEngine *_vm; + HitRectList *_hitRects; + Common::Array<Sprite*> _sprites; +}; + + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_COLLISIONMAN_H */ diff --git a/engines/neverhood/detection.cpp b/engines/neverhood/detection.cpp new file mode 100644 index 0000000000..df9eca3d7f --- /dev/null +++ b/engines/neverhood/detection.cpp @@ -0,0 +1,282 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "base/plugins.h" + +#include "engines/advancedDetector.h" +#include "common/file.h" + +#include "neverhood/neverhood.h" + + +namespace Neverhood { + +struct NeverhoodGameDescription { + ADGameDescription desc; + + int gameID; + int gameType; + uint32 features; + uint16 version; +}; + +const char *NeverhoodEngine::getGameId() const { + return _gameDescription->desc.gameid; +} + +uint32 NeverhoodEngine::getFeatures() const { + return _gameDescription->features; +} + +Common::Platform NeverhoodEngine::getPlatform() const { + return _gameDescription->desc.platform; +} + +uint16 NeverhoodEngine::getVersion() const { + return _gameDescription->version; +} + +} + +static const PlainGameDescriptor neverhoodGames[] = { + {"neverhood", "The Neverhood Chronicles"}, + {0, 0} +}; + +namespace Neverhood { + +static const NeverhoodGameDescription gameDescriptions[] = { + + { + // Neverhood English version + // TODO: Maybe additional files are needed to properly detect different versions + { + "neverhood", + 0, + AD_ENTRY1s("hd.blb", "22958d968458c9ff221aee38577bb2b2", 4279716), + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_NO_FLAGS, + GUIO1(GUIO_NONE) + }, + 0, + 0, + 0, + 0, + }, + + { AD_TABLE_END_MARKER, 0, 0, 0, 0 } +}; + +/** + * The fallback game descriptor used by the Neverhood engine's fallbackDetector. + * Contents of this struct are to be overwritten by the fallbackDetector. + */ +static NeverhoodGameDescription g_fallbackDesc = { + { + "", + "", + AD_ENTRY1(0, 0), // This should always be AD_ENTRY1(0, 0) in the fallback descriptor + Common::UNK_LANG, + Common::kPlatformPC, + ADGF_NO_FLAGS, + GUIO_NONE + }, + 0, + 0, + 0, + 0, +}; + +} // End of namespace Neverhood + +class NeverhoodMetaEngine : public AdvancedMetaEngine { +public: + NeverhoodMetaEngine() : AdvancedMetaEngine(Neverhood::gameDescriptions, sizeof(Neverhood::NeverhoodGameDescription), neverhoodGames) { + _singleid = "neverhood"; + } + + virtual const char *getName() const { + return "Neverhood Engine"; + } + + virtual const char *getOriginalCopyright() const { + return "Neverhood Engine (C) Infogrames"; + } + + virtual bool hasFeature(MetaEngineFeature f) const; + virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const; + +#if 0 // Not used yet but let's keep it for later when it is + SaveStateList listSaves(const char *target) const; + virtual int getMaximumSaveSlot() const; + void removeSaveState(const char *target, int slot) const; + SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const; +#endif + + const ADGameDescription *fallbackDetect(const Common::FSList &fslist) const; + +}; + +bool NeverhoodMetaEngine::hasFeature(MetaEngineFeature f) const { + return + false; // Nothing yet :( +// (f == kSupportsListSaves) || +// (f == kSupportsLoadingDuringStartup) || +// (f == kSupportsDeleteSave) || +// (f == kSavesSupportMetaInfo) || +// (f == kSavesSupportThumbnail); +} + +bool Neverhood::NeverhoodEngine::hasFeature(EngineFeature f) const { + return + false; // Nothing yet :( +// (f == kSupportsRTL) || // TODO: Not yet... +// (f == kSupportsLoadingDuringRuntime) || +// (f == kSupportsSavingDuringRuntime); +} + +bool NeverhoodMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const { + const Neverhood::NeverhoodGameDescription *gd = (const Neverhood::NeverhoodGameDescription *)desc; + if (gd) { + *engine = new Neverhood::NeverhoodEngine(syst, gd); + } + return gd != 0; +} + +const ADGameDescription *NeverhoodMetaEngine::fallbackDetect(const Common::FSList &fslist) const { + // Set the default values for the fallback descriptor's ADGameDescription part. + Neverhood::g_fallbackDesc.desc.language = Common::UNK_LANG; + Neverhood::g_fallbackDesc.desc.platform = Common::kPlatformPC; + Neverhood::g_fallbackDesc.desc.flags = ADGF_NO_FLAGS; + + // Set default values for the fallback descriptor's NeverhoodGameDescription part. + Neverhood::g_fallbackDesc.gameID = 0; + Neverhood::g_fallbackDesc.features = 0; + Neverhood::g_fallbackDesc.version = 3; + + return NULL; +} + +#if 0 // Not used yet but let's keep it for later when it is + +SaveStateList NeverhoodMetaEngine::listSaves(const char *target) const { + Common::SaveFileManager *saveFileMan = g_system->getSavefileManager(); + Neverhood::NeverhoodEngine::SaveHeader header; + Common::String pattern = target; + pattern += ".???"; + + Common::StringArray filenames; + filenames = saveFileMan->listSavefiles(pattern.c_str()); + Common::sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..) + + SaveStateList saveList; + for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); file++) { + // Obtain the last 3 digits of the filename, since they correspond to the save slot + int slotNum = atoi(file->c_str() + file->size() - 3); + + if (slotNum >= 0 && slotNum <= 999) { + Common::InSaveFile *in = saveFileMan->openForLoading(file->c_str()); + if (in) { + if (Neverhood::NeverhoodEngine::readSaveHeader(in, false, header) == Neverhood::NeverhoodEngine::kRSHENoError) { + saveList.push_back(SaveStateDescriptor(slotNum, header.description)); + } + delete in; + } + } + } + + return saveList; +} + +int NeverhoodMetaEngine::getMaximumSaveSlot() const { + return 999; +} + +void NeverhoodMetaEngine::removeSaveState(const char *target, int slot) const { + // Slot 0 can't be deleted, it's for restarting the game(s) + if (slot == 0) + return; + + Common::SaveFileManager *saveFileMan = g_system->getSavefileManager(); + Common::String filename = Neverhood::NeverhoodEngine::getSavegameFilename(target, slot); + + saveFileMan->removeSavefile(filename.c_str()); + + Common::StringArray filenames; + Common::String pattern = target; + pattern += ".???"; + filenames = saveFileMan->listSavefiles(pattern.c_str()); + Common::sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..) + + 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); + + // Rename every slot greater than the deleted slot, + // Also do not rename quicksaves. + if (slotNum > slot && slotNum < 990) { + // FIXME: Our savefile renaming done here is inconsitent with what we do in + // GUI_v2::deleteMenu. While here we rename every slot with a greater equal + // number of the deleted slot to deleted slot, deleted slot + 1 etc., + // we only rename the following slots in GUI_v2::deleteMenu until a slot + // is missing. + saveFileMan->renameSavefile(file->c_str(), filename.c_str()); + + filename = Neverhood::NeverhoodEngine::getSavegameFilename(target, ++slot); + } + } + +} + +SaveStateDescriptor NeverhoodMetaEngine::querySaveMetaInfos(const char *target, int slot) const { + Common::String filename = Neverhood::NeverhoodEngine::getSavegameFilename(target, slot); + Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(filename.c_str()); + + if (in) { + Neverhood::NeverhoodEngine::SaveHeader header; + Neverhood::NeverhoodEngine::kReadSaveHeaderError error; + + error = Neverhood::NeverhoodEngine::readSaveHeader(in, true, header); + delete in; + + if (error == Neverhood::NeverhoodEngine::kRSHENoError) { + SaveStateDescriptor desc(slot, header.description); + + desc.setDeletableFlag(false); + desc.setWriteProtectedFlag(false); + desc.setThumbnail(header.thumbnail); + + return desc; + } + } + + return SaveStateDescriptor(); +} + +#endif + +#if PLUGIN_ENABLED_DYNAMIC(NEVERHOOD) + REGISTER_PLUGIN_DYNAMIC(NEVERHOOD, PLUGIN_TYPE_ENGINE, NeverhoodMetaEngine); +#else + REGISTER_PLUGIN_STATIC(NEVERHOOD, PLUGIN_TYPE_ENGINE, NeverhoodMetaEngine); +#endif diff --git a/engines/neverhood/diskplayerscene.cpp b/engines/neverhood/diskplayerscene.cpp new file mode 100644 index 0000000000..4fdc736dc5 --- /dev/null +++ b/engines/neverhood/diskplayerscene.cpp @@ -0,0 +1,563 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "neverhood/diskplayerscene.h" +#include "neverhood/mouse.h" + +namespace Neverhood { + +// TODO: Maybe move hash tables into neverhood.dat + +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 +}; + +Class494::Class494(NeverhoodEngine *vm) + : AnimatedSprite(vm, 1100) { + + createSurface1(0x100B90B4, 1200); + _x = 211; + _y = 195; + setFileHash(0x100B90B4, 0, -1); + _newHashListIndex = 0; + _needRefresh = true; + updatePosition(); + _surface->setVisible(false); +} + +uint32 Class494::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + removeCallbacks(); + break; + } + return messageResult; +} + +void Class494::sub43BE00() { + stopAnimation(); + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&Sprite::handleMessage); + _surface->setVisible(false); +} + +void Class494::sub43BE20() { + setFileHash(0x100B90B4, 0, -1); + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&Class494::handleMessage); + NextState(&Class494::sub43BE00); + _surface->setVisible(true); +} + +DiskplayerPlayButton::DiskplayerPlayButton(NeverhoodEngine *vm, DiskplayerScene *diskplayerScene) + : StaticSprite(vm, 1400), _soundResource1(vm), _soundResource2(vm), + _diskplayerScene(diskplayerScene), _isPlaying(false) { + + _spriteResource.load2(0x24A4A664); + createSurface(400, _spriteResource.getDimensions().width, _spriteResource.getDimensions().height); + _drawRect.x = 0; + _drawRect.y = 0; + _drawRect.width = _spriteResource.getDimensions().width; + _drawRect.height = _spriteResource.getDimensions().height; + _deltaRect.x = 0; + _deltaRect.y = 0; + _deltaRect.width = _spriteResource.getDimensions().width; + _deltaRect.height = _spriteResource.getDimensions().height; + _x = _spriteResource.getPosition().x; + _y = _spriteResource.getPosition().y; + _surface->setVisible(false); + processDelta(); + _needRefresh = true; + StaticSprite::update(); + _soundResource1.load(0x44043000); + _soundResource2.load(0x44045000); + SetMessageHandler(&DiskplayerPlayButton::handleMessage); +} + +uint32 DiskplayerPlayButton::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = 0; + Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + if (!_diskplayerScene->getFlag3()) { + if (_isPlaying) { + sendMessage(_diskplayerScene, 0x2001, 0); + release(); + } else { + sendMessage(_diskplayerScene, 0x2000, 0); + press(); + } + } + StaticSprite::update(); + messageResult = 1; + break; + } + return messageResult; +} + +void DiskplayerPlayButton::press() { + if (!_isPlaying) { + _surface->setVisible(true); + StaticSprite::update(); + _soundResource1.play(); + _isPlaying = true; + } +} + +void DiskplayerPlayButton::release() { + if (_isPlaying) { + _surface->setVisible(false); + StaticSprite::update(); + _soundResource2.play(); + _isPlaying = false; + } +} + +DiskplayerSlot::DiskplayerSlot(NeverhoodEngine *vm, DiskplayerScene *diskplayerScene, int elementIndex, int value) + : Entity(vm, 0), _diskplayerScene(diskplayerScene), _soundResource(vm), _elementIndex(elementIndex), + _value(value), _flag2(false), _flag(false), _countdown(0), _initialCountdown(2), + _inactiveSlot(NULL), _appearSlot(NULL), _activeSlot(NULL) { + + if (value != 0 && elementIndex < 20) { + _inactiveSlot = _diskplayerScene->addSprite(new StaticSprite(_vm, kDiskplayerSlotFileHashes1[_elementIndex], 1100)); + _appearSlot = _diskplayerScene->addSprite(new StaticSprite(_vm, kDiskplayerSlotFileHashes2[_elementIndex], 1000)); + _activeSlot = _diskplayerScene->addSprite(new StaticSprite(_vm, kDiskplayerSlotFileHashes3[_elementIndex], 1100)); + _inactiveSlot->getSurface()->setVisible(false); + _appearSlot->getSurface()->setVisible(false); + _activeSlot->getSurface()->setVisible(false); + _soundResource.load(0x46210074); + // TODO sound panning stuff + } else if (elementIndex != 20) { + _activeSlot = _diskplayerScene->addSprite(new StaticSprite(_vm, kDiskplayerSlotFileHashes4[_elementIndex], 1100)); + _activeSlot->getSurface()->setVisible(false); + } + SetUpdateHandler(&DiskplayerSlot::update); +} + +void DiskplayerSlot::update() { + if (_countdown != 0 && (--_countdown == 0)) { + if (_flag) { + if (_inactiveSlot) { + _inactiveSlot->getSurface()->setVisible(true); + } + if (_activeSlot) { + _activeSlot->getSurface()->setVisible(false); + } + _countdown = _initialCountdown / 2; + } else { + if (_inactiveSlot) { + _inactiveSlot->getSurface()->setVisible(false); + } + if (_activeSlot) { + _activeSlot->getSurface()->setVisible(true); + } + _countdown = _initialCountdown; + } + _flag = !_flag; + } +} + +void DiskplayerSlot::appear() { + if (_inactiveSlot) { + _inactiveSlot->getSurface()->setVisible(true); + } + if (_appearSlot) { + _appearSlot->getSurface()->setVisible(true); + } + if (_inactiveSlot) { + _soundResource.play(); + } +} + +void DiskplayerSlot::play() { + if (!_flag2) { + if (_inactiveSlot) { + _inactiveSlot->getSurface()->setVisible(false); + } + if (_activeSlot) { + _activeSlot->getSurface()->setVisible(true); + } + _flag = true; + _countdown = 0; + } +} + +void DiskplayerSlot::activate() { + if (!_flag2) { + _countdown = _initialCountdown; + } +} + +void DiskplayerSlot::stop() { + if (!_flag2) { + if (_inactiveSlot) { + _inactiveSlot->getSurface()->setVisible(true); + } + if (_activeSlot) { + _activeSlot->getSurface()->setVisible(false); + } + _flag = false; + _countdown = 0; + } +} + +DiskplayerScene::DiskplayerScene(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule, true), _which(which), _diskIndex(0), _appearCountdown(0), _tuneInCountdown(0), + _fullFlag(false), _flag3(false), _inputDisabled(true), _updateStatus(0) { + + int count = 0; + + _surfaceFlag = true; + + setBackground(0x8A000044); + setPalette(kDiskplayerPaletteFileHashes[_which]); + _playButton = new DiskplayerPlayButton(_vm, this); + addSprite(_playButton); + _vm->_collisionMan->addSprite(_playButton); + _class494 = new Class494(_vm); + addSprite(_class494); + + // DEBUG: Give all disks + for (int i = 0; i < 19; i++) { + setSubVar(0x02720344, i, 1); + } + + for (int i = 0; i < 20; i++) { + _diskAvailable[i] = 0; + if (getSubVar(0x02720344, i)) + count++; + } + + for (int i = 0; i < count; i++) { + _diskAvailable[kDiskplayerInitArray[i] - 1] = 1; + } + + for (int i = 0; i < 20; i++) { + _diskSlots[i] = new DiskplayerSlot(_vm, this, i, _diskAvailable[i]); + addEntity(_diskSlots[i]); + } + + _fullFlag = count == 20; + + if (_fullFlag && !getGlobalVar(0xC0780812)) + _flag3 = true; + + _flag4 = _flag3; + + _class650 = new DiskplayerSlot(_vm, this, 20, 0); + addEntity(_class650); + + insertMouse435(0x000408A8, 20, 620); + showMouse(false); + + _smackerPlayer = new SmackerPlayer(_vm, this, 0x08288103, false, true); + addEntity(_smackerPlayer); + addSurface(_smackerPlayer->getSurface()); + _smackerPlayer->setDrawPos(154, 86); + // TODO _smackerPlayer->gotoFrame(0); + + _palette->usePalette(); + + SetMessageHandler(&DiskplayerScene::handleMessage); + SetUpdateHandler(&DiskplayerScene::update); + _appearCountdown = 6; + +} + +void DiskplayerScene::update() { + Scene::update(); + + debug("_updateStatus = %d", _updateStatus); + + if (_updateStatus == 1) { + if (_smackerPlayer->getFrameNumber() == _smackerPlayer->getFrameCount() - 1) { + if (_diskAvailable[_diskIndex]) { + playDisk(); + } else { + playStatic(); + } + } + } else if (_updateStatus == 2) { + if (_smackerPlayer->getFrameNumber() == _smackerPlayer->getFrameCount() - 1) { + _diskSlots[_diskIndex]->stop(); + _diskIndex++; + if (_fullFlag) { + if (_diskIndex == 20) { + if (_flag3) { + playDisk(); + _updateStatus = 3; + } else { + _diskIndex = 0; + stop(); + } + } else { + playDisk(); + } + } else { + if (_diskIndex == 20) { + _diskIndex = 0; + stop(); + } else { + tuneIn(); + } + } + } + } else if (_updateStatus == 3) { + if (_smackerPlayer->getFrameNumber() == 133) { + _class494->sub43BE20(); + setGlobalVar(0xC0780812, 1); + } else if (_smackerPlayer->getFrameNumber() == _smackerPlayer->getFrameCount() - 1) { + for (int i = 0; i < 20; i++) { + _diskSlots[i]->setFlag2(false); + _diskSlots[i]->stop(); + } + _diskIndex = 0; + stop(); + showMouse(true); + _flag3 = false; + } + } + + if (_appearCountdown != 0 && (--_appearCountdown == 0)) { + _diskSlots[_diskIndex]->appear(); + if (_flag3) { + _diskSlots[_diskIndex]->activate(); + _diskSlots[_diskIndex]->setFlag2(true); + } + _diskIndex++; + while (_diskAvailable[_diskIndex] == 0 && _diskIndex < 19) + _diskIndex++; + if (_diskIndex < 20) { + _appearCountdown = 1; + } else { + _diskIndex = 0; + _inputDisabled = false; + if (_flag3) { + _playButton->press(); + _tuneInCountdown = 2; + } else { + showMouse(true); + _diskSlots[_diskIndex]->activate(); + } + } + } + + if (_tuneInCountdown != 0 && (--_tuneInCountdown == 0)) { + playDisk(); + } + +} + +uint32 DiskplayerScene::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = 0; + Scene::handleMessage(messageNum, param, sender); + if (!_inputDisabled) { + switch (messageNum) { + case 0x0001: + // TODO: Debug/Cheat + if (param.asPoint().x <= 20 || param.asPoint().x >= 620) { + sendMessage(_parentModule, 0x1009, 0); + } else if (!_flag3 && + 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 == 2) { + if (_diskAvailable[_diskIndex]) { + playDisk(); + } else { + playStatic(); + } + } + } + break; + // case 0x000D: TODO: Debug/Cheat + case 0x2000: + tuneIn(); + break; + case 0x2001: + stop(); + break; + } + } + return messageResult; +} + +void DiskplayerScene::stop() { + _smackerPlayer->open(0x08288103, true); + _palette->usePalette(); + _playButton->release(); + _updateStatus = 0; + _diskSlots[_diskIndex]->activate(); +} + +void DiskplayerScene::tuneIn() { + _smackerPlayer->open(0x900001C1, false); + _palette->usePalette(); + _playButton->release(); + _updateStatus = 1; + _diskSlots[_diskIndex]->activate(); +} + +void DiskplayerScene::playDisk() { + _smackerPlayer->open(kDiskplayerSmackerFileHashes[_diskIndex], false); + _palette->usePalette(); + _updateStatus = 2; + _diskSlots[_diskIndex]->play(); +} + +void DiskplayerScene::playStatic() { + _smackerPlayer->open(0x90000101, false); + _palette->usePalette(); + _playButton->release(); + _updateStatus = 2; + _diskSlots[_diskIndex]->activate(); +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/diskplayerscene.h b/engines/neverhood/diskplayerscene.h new file mode 100644 index 0000000000..7969a7a03b --- /dev/null +++ b/engines/neverhood/diskplayerscene.h @@ -0,0 +1,110 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef NEVERHOOD_DISKPLAYERSCENE_H +#define NEVERHOOD_DISKPLAYERSCENE_H + +#include "neverhood/neverhood.h" +#include "neverhood/resourceman.h" +#include "neverhood/scene.h" +#include "neverhood/smackerplayer.h" + +namespace Neverhood { + +class DiskplayerScene; + +class Class494 : public AnimatedSprite { +public: + Class494(NeverhoodEngine *vm); + void sub43BE20(); +protected: + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void sub43BE00(); +}; + +class DiskplayerPlayButton : public StaticSprite { +public: + DiskplayerPlayButton(NeverhoodEngine *vm, DiskplayerScene *diskplayerScene); + void press(); + void release(); +protected: + DiskplayerScene *_diskplayerScene; + SoundResource _soundResource1; + SoundResource _soundResource2; + bool _isPlaying; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class DiskplayerSlot : public Entity { +public: + DiskplayerSlot(NeverhoodEngine *vm, DiskplayerScene *diskplayerScene, int elementIndex, int value); + void activate(); + void stop(); + void appear(); + void play(); + void setFlag2(bool value) { _flag2 = value; } +protected: + DiskplayerScene *_diskplayerScene; + SoundResource _soundResource; + Sprite *_inactiveSlot; + Sprite *_appearSlot; + Sprite *_activeSlot; + int _elementIndex; + int _initialCountdown; + int _countdown; + bool _flag2; + int _value; + bool _flag; + void update(); +}; + +class DiskplayerScene : public Scene { +public: + DiskplayerScene(NeverhoodEngine *vm, Module *parentModule, int which); + bool getFlag3() const { return _flag3; } +protected: + SmackerPlayer *_smackerPlayer; + DiskplayerPlayButton *_playButton; + Class494 *_class494; + DiskplayerSlot *_diskSlots[20]; + DiskplayerSlot *_class650; + int _updateStatus; + byte _diskAvailable[20]; + bool _flag4; + int _which; + int _diskIndex; + int _appearCountdown; + int _tuneInCountdown; + bool _fullFlag; + bool _inputDisabled; + bool _flag3; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void stop(); + void tuneIn(); + void playDisk(); + void playStatic(); +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_DISKPLAYERSCENE_H */ diff --git a/engines/neverhood/entity.h b/engines/neverhood/entity.h new file mode 100644 index 0000000000..9256c13ba8 --- /dev/null +++ b/engines/neverhood/entity.h @@ -0,0 +1,146 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef NEVERHOOD_ENTITY_H +#define NEVERHOOD_ENTITY_H + +#include "common/str.h" +#include "neverhood/neverhood.h" +#include "neverhood/gamevars.h" +#include "neverhood/graphics.h" + +namespace Neverhood { + +class Entity; + +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 { + assert(_type == mptInteger); + return _integer; + } + NPoint 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 *asEntity() const { + assert(_type == mptEntity); + return _entity; + } +protected: + union { + uint32 _integer; + NPoint _point; + Entity *_entity; + // TODO: Other types... + }; + MessageParamType _type; + // TODO: Constructors for the param types... +}; + +// TODO: Disable heavy debug stuff in release mode + +#define SetUpdateHandler(handler) _updateHandlerCb = static_cast <void (Entity::*)(void)> (handler); debug(2, "SetUpdateHandler(" #handler ")"); _updateHandlerCbName = #handler +#define SetMessageHandler(handler) _messageHandlerCb = static_cast <uint32 (Entity::*)(int messageNum, const MessageParam ¶m, Entity *sender)> (handler); debug(2, "SetMessageHandler(" #handler ")"); _messageHandlerCbName = #handler + +class Entity { +public: + Common::String _name; // Entity name for debugging purposes + Common::String _updateHandlerCbName; + Common::String _messageHandlerCbName; + Entity(NeverhoodEngine *vm, int priority) + : _vm(vm), _updateHandlerCb(NULL), _messageHandlerCb(NULL), _priority(priority), _name("Entity") { + } + virtual ~Entity() { + } + virtual void draw() { + } + void handleUpdate() { + //debug("Entity(%s).handleUpdate", _name.c_str()); + debug(2, "handleUpdate() -> [%s]", _updateHandlerCbName.c_str()); + if (_updateHandlerCb) + (this->*_updateHandlerCb)(); + } + bool hasMessageHandler() const { return _messageHandlerCb != NULL; } + uint32 receiveMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + debug(2, "receiveMessage(%04X) -> [%s]", messageNum, _messageHandlerCbName.c_str()); + return _messageHandlerCb ? (this->*_messageHandlerCb)(messageNum, param, sender) : 0; + } + // 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 ¶m) { + return receiver ? receiver->receiveMessage(messageNum, param, this) : 0; + } + uint32 sendMessage(Entity *receiver, int messageNum, uint32 param) { + return sendMessage(receiver, messageNum, MessageParam(param)); + } + uint32 sendPointMessage(Entity *receiver, int messageNum, const NPoint ¶m) { + return sendMessage(receiver, messageNum, MessageParam(param)); + } + uint32 sendEntityMessage(Entity *receiver, int messageNum, Entity *param) { + return sendMessage(receiver, messageNum, MessageParam((Entity*)param)); + } + int getPriority() const { return _priority; } + // Shortcuts for game variable access + uint32 getGlobalVar(uint32 nameHash) { + return _vm->_gameVars->getGlobalVar(nameHash); + } + void setGlobalVar(uint32 nameHash, uint32 value) { + _vm->_gameVars->setGlobalVar(nameHash, value); + } + uint32 getSubVar(uint32 nameHash, uint32 subNameHash) { + return _vm->_gameVars->getSubVar(nameHash, subNameHash); + } + void setSubVar(uint32 nameHash, uint32 subNameHash, uint32 value) { + _vm->_gameVars->setSubVar(nameHash, subNameHash, value); + } + void incGlobalVar(uint32 nameHash, int incrValue) { + setGlobalVar(nameHash, getGlobalVar(nameHash) + incrValue); + } + void incSubVar(uint32 nameHash, uint32 subNameHash, int incrValue) { + setSubVar(nameHash, subNameHash, getSubVar(nameHash, subNameHash) + incrValue); + } +protected: + void (Entity::*_updateHandlerCb)(); + uint32 (Entity::*_messageHandlerCb)(int messageNum, const MessageParam ¶m, Entity *sender); + NeverhoodEngine *_vm; + int _priority; +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_ENTITY_H */ diff --git a/engines/neverhood/gamemodule.cpp b/engines/neverhood/gamemodule.cpp new file mode 100644 index 0000000000..95fe521e23 --- /dev/null +++ b/engines/neverhood/gamemodule.cpp @@ -0,0 +1,509 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "neverhood/gamemodule.h" + +#include "neverhood/graphics.h" +#include "neverhood/module1000.h" +#include "neverhood/module1100.h" +#include "neverhood/module1200.h" +#include "neverhood/module1300.h" +#include "neverhood/module1400.h" +#include "neverhood/module1500.h" +#include "neverhood/module1600.h" +#include "neverhood/module1700.h" +#include "neverhood/module1800.h" +#include "neverhood/module1900.h" +#include "neverhood/module2000.h" +#include "neverhood/module2100.h" +#include "neverhood/module2200.h" +#include "neverhood/module2300.h" +#include "neverhood/module2600.h" +#include "neverhood/module2700.h" +#include "neverhood/module3000.h" + +namespace Neverhood { + +GameModule::GameModule(NeverhoodEngine *vm) + : Module(vm, NULL), _moduleNum(-1) { + + // Other initializations moved to actual engine class + // TODO + // TODO Sound1ChList_sub_407F70(0x2D0031, 0x8861079); + SetMessageHandler(&GameModule::handleMessage); +} + +GameModule::~GameModule() { + + // TODO Sound1ChList_sub_407AF0(0x2D0031); + delete _childObject; + _childObject = NULL; + // TODO: Set palette to black but probably not neccessary + // TODO Sound1ChList_sub_408480(); + // TODO Set debug vars (maybe) +} + +void GameModule::handleMouseMove(int16 x, int16 y) { + if (_childObject) { + NPoint mousePos; + mousePos.x = x; + mousePos.y = y; + debug(2, "GameModule::handleMouseMove(%d, %d)", x, y); + sendPointMessage(_childObject, 0, mousePos); + } +} + +void GameModule::handleMouseDown(int16 x, int16 y) { + if (_childObject) { + NPoint mousePos; + mousePos.x = x; + mousePos.y = y; + debug(2, "GameModule::handleMouseDown(%d, %d)", x, y); + sendPointMessage(_childObject, 1, mousePos); + } +} + +void GameModule::initScene1307Vars() { + + // Exit if it's already initialized + if (getSubVar(0x40050052, 0x25400B10)) + return; + + for (uint i = 0; i < 3; i++) { + bool more; + do { + more = false; + setSubVar(0x0C10A000, i, _vm->_rnd->getRandomNumber(16 - 1)); + if (i > 0) { + for (uint j = 0; j < i && !more; j++) { + more = getSubVar(0x0C10A000, j) == getSubVar(0x0C10A000, i); + } + } + } while (more); + } + + for (uint i = 0; i < 3; i++) { + bool more; + do { + more = false; + setSubVar(0xA010B810, i, _vm->_rnd->getRandomNumber(16 - 1)); + if (i > 0) { + for (uint j = 0; j < i && !more; j++) { + more = getSubVar(0xA010B810, j) == getSubVar(0xA010B810, i); + } + } + if (getSubVar(0xA010B810, i) == getSubVar(0x0C10A000, i)) + more = true; + } while (more); + } + + setSubVar(0x40050052, 0x25400B10, 1); + +} + +void GameModule::initScene1405Vars() { + + // TODO: Give better names + + byte array44[3]; + byte array3C[10]; + byte array30[48]; + uint32 index3 = 48; + uint32 index2 = 9; + uint32 index1 = 2; + uint32 rndIndex; + + // Exit if it's already initialized + if (getSubVar(0x40050052, 0xC8606803)) + return; + + for (uint32 i = 0; i < 3; i++) + setSubVar(0x61084036, i, 1); + + for (byte i = 0; i < 3; i++) + array44[i] = i; + + for (byte i = 0; i < 10; i++) + array3C[i] = i; + + for (byte i = 0; i < 48; i++) + array30[i] = i; + + rndIndex = _vm->_rnd->getRandomNumber(3 - 1); + + setSubVar(0x13100631, array44[rndIndex], 5); + + for (byte i = 5; i < 9; i++) + array3C[i] = array3C[i + 1]; + + while (rndIndex < 2) { + array44[rndIndex] = array44[rndIndex + 1]; + rndIndex++; + } + + for (int i = 0; i < 2; i++) { + uint32 rndIndex1 = _vm->_rnd->getRandomNumber(index2 - 1); // si + uint32 rndIndex2 = _vm->_rnd->getRandomNumber(index1 - 1); // di + setSubVar(0x13100631, array44[rndIndex2], array3C[rndIndex1]); + index2--; + while (rndIndex1 < index2) { + array3C[rndIndex1] = array3C[rndIndex1 + 1]; + rndIndex1++; + } + index1--; + while (rndIndex2 < index1) { + array44[rndIndex2] = array44[rndIndex2 + 1]; + rndIndex2++; + } + } + + for (uint32 i = 0; i < 3; i++) { + uint32 rndValue = _vm->_rnd->getRandomNumber(4 - 1) * 2 + 2; + uint32 index4 = 0; + setSubVar(0x7500993A, i, rndValue); + while (index4 < rndValue) { + uint32 rndIndex3 = _vm->_rnd->getRandomNumber(index3 - 1); + setSubVar(0x0C65F80B, array30[rndIndex3], getSubVar(0x13100631, i)); + index3--; + while (rndIndex3 < index3) { + array30[rndIndex3] = array30[rndIndex3 + 1]; + rndIndex3++; + } + index4++; + } + } + + uint32 index5 = 0; + while (index3 != 0) { + uint32 rndIndex4 = _vm->_rnd->getRandomNumber(index3 - 1); + index1 = array3C[index5]; + setSubVar(0x0C65F80B, array30[rndIndex4], index1); + index3--; + while (rndIndex4 < index3) { + array30[rndIndex4] = array30[rndIndex4 + 1]; + rndIndex4++; + } + uint32 rndIndex5 = _vm->_rnd->getRandomNumber(index3 - 1); + setSubVar(0x0C65F80B, array30[rndIndex5], index1); + index3--; + while (rndIndex5 < index3) { + array30[rndIndex5] = array30[rndIndex5 + 1]; + rndIndex5++; + } + index5++; + if (index5 >= index2) + index5 = 0; + + } + + setSubVar(0x40050052, 0xC8606803, 1); + +} + +void GameModule::initScene3009Vars() { + if (!getSubVar(0x40050052, 0x8C9819C2)) { + for (int i = 0; i < 3; i++) { + setSubVar(0x00504B86, i, _vm->_rnd->getRandomNumber(12 - 1)); + setSubVar(0x0A4C0A9A, i, _vm->_rnd->getRandomNumber(12 - 1)); + } + setSubVar(0x40050052, 0x8C9819C2, 1); + } +} + +uint32 GameModule::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Module::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x0800: + _someFlag1 = true; + return messageResult; + case 0x1009: + _moduleResult = param.asInteger(); + _done = true; + return messageResult; + case 0x100A: + // Unused resource preloading message + return messageResult; + case 0x101F: + _field2C = true; + return messageResult; + case 0x1023: + // Unused resource preloading message + return messageResult; + } + return messageResult; +} + +void GameModule::startup() { + // TODO: Displaying of error text probably not needed in ScummVM +// createModule(1500, 0); // Logos and intro video //Real +#if 0 + _vm->gameState().sceneNum = 0; + createModule(1200, -1); +#endif +#if 0 + _vm->gameState().sceneNum = 0; + createModule(1800, -1); +#endif +#if 0 + _vm->gameState().sceneNum = 0; + createModule(2000, -1); +#endif +#if 0 + _vm->gameState().sceneNum = 4; + createModule(2200, -1); +#endif +#if 0 + _vm->gameState().sceneNum = 0; + createModule(1000, -1); +#endif +#if 0 + _vm->gameState().sceneNum = 1; + createModule(1000, -1); +#endif +#if 0 + _vm->gameState().sceneNum = 8; + _vm->gameState().which = 1; + createModule(1600, -1); +#endif +#if 0 + _vm->gameState().sceneNum = 6; + createModule(1900, -1); +#endif +#if 0 + _vm->gameState().sceneNum = 0; + createModule(2100, 3); +#endif +#if 0 + _vm->gameState().sceneNum = 8; + createModule(2600, -1); +#endif +#if 1 + _vm->gameState().which = 0; + _vm->gameState().sceneNum = 1; + createModule(2700, -1); +#endif +} + +void GameModule::createModule(int moduleNum, int which) { + debug("GameModule::createModule(%d, %d)", moduleNum, which); + _moduleNum = moduleNum; + switch (_moduleNum) { + case 1000: + setGlobalVar(0x91080831, 0x03294419); + _childObject = new Module1000(_vm, this, which); + break; + case 1100: + setGlobalVar(0x91080831, 0x0002C818); + _childObject = new Module1100(_vm, this, which); + break; + case 1200: + setGlobalVar(0x91080831, 0x00478311); + _childObject = new Module1200(_vm, this, which); + break; + case 1300: + setGlobalVar(0x91080831, 0x0061C090); + _childObject = new Module1300(_vm, this, which); + break; + case 1400: + setGlobalVar(0x91080831, 0x00AD0012); + _childObject = new Module1400(_vm, this, which); + break; + case 1500: + _someFlag1 = false; + setGlobalVar(0x91080831, 0x00F10114); + _childObject = new Module1500(_vm, this, which, true); + break; + case 1600: + setGlobalVar(0x91080831, 0x01A008D8); + _childObject = new Module1600(_vm, this, which); + break; + case 1700: + setGlobalVar(0x91080831, 0x04212331); + _childObject = new Module1700(_vm, this, which); + break; + case 1800: + setGlobalVar(0x91080831, 0x04A14718); + _childObject = new Module1800(_vm, this, which); + break; + case 1900: + setGlobalVar(0x91080831, 0x04E1C09C); + _childObject = new Module1900(_vm, this, which); + break; + case 2000: + setGlobalVar(0x91080831, 0x08250000); + _childObject = new Module2000(_vm, this, which); + break; + case 2100: + setGlobalVar(0x91080831, 0x10A10C14); + _childObject = new Module2100(_vm, this, which); + break; + case 2200: + setGlobalVar(0x91080831, 0x11391412); + _childObject = new Module2200(_vm, this, which); + break; + case 2300: + setGlobalVar(0x91080831, 0x1A214010); + _childObject = new Module2300(_vm, this, which); + break; + case 2600: + setGlobalVar(0x91080831, 0x40271018); + _childObject = new Module2600(_vm, this, which); + break; + case 2700: + setGlobalVar(0x91080831, 0x42212411); + _childObject = new Module2700(_vm, this, which); + break; + case 3000: + setGlobalVar(0x91080831, 0x81293110); + _childObject = new Module3000(_vm, this, which); + break; + default: + error("GameModule::createModule() Could not create module %d", moduleNum); + } + SetUpdateHandler(&GameModule::updateModule); + _childObject->handleUpdate(); +} + +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(0xD0A14D10, 1); + createModule(1300, 0); + } + break; + case 1300: + if (_moduleResult == 1) { + // TODO _gameState.clear(); + // TODO GameModule_handleKeyEscape + } else { + createModule(2900, 0); + } + break; + case 1400: + if (_moduleResult == 1) { + error("WEIRD!"); + } else { + 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) { + // TODO GameState_clear(); + // TODO GameModule_handleKeyEscape(); + } 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: + if (_moduleResult == 1) { + createModule(2200, 0); + } else if (_moduleResult == 2) { + createModule(1200, 0); + } else if (_moduleResult == 3) { + createModule(2400, 0); + } else if (_moduleResult == 4) { + createModule(3000, 0); + } else { + createModule(1000, 1); + } + break; + case 2600: + if (_moduleResult == 1) { + createModule(2500, 0); + } else { + createModule(1200, 1); + } + break; + case 2700: + createModule(1800, 2); + break; + case 3000: + if (_moduleResult == 1) { + createModule(1900, 0); + } else if (_moduleResult == 2) { + // WEIRD: Sets the errorFlag + } else if (_moduleResult == 3) { + createModule(1800, 3); + } else if (_moduleResult == 4) { + createModule(3000, 0); + } else { + createModule(2300, 4); + } + break; + } + } +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/gamemodule.h b/engines/neverhood/gamemodule.h new file mode 100644 index 0000000000..6da7123125 --- /dev/null +++ b/engines/neverhood/gamemodule.h @@ -0,0 +1,56 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +// TODO: I couldn't come up with a better name than 'Module' so far + +#ifndef NEVERHOOD_GAMEMODULE_H +#define NEVERHOOD_GAMEMODULE_H + +#include "neverhood/neverhood.h" +#include "neverhood/module.h" + +namespace Neverhood { + +class GameModule : public Module { +public: + GameModule(NeverhoodEngine *vm); + virtual ~GameModule(); + void startup(); + void handleMouseMove(int16 x, int16 y); + void handleMouseDown(int16 x, int16 y); + void initScene1307Vars(); + void initScene1405Vars(); + void initScene3009Vars(); +protected: + Entity *_prevChildObject; + bool _someFlag1; + bool _field2C; + uint32 _counter; + int _moduleNum; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void createModule(int moduleNum, int which); + void updateModule(); +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_MODULE_H */ diff --git a/engines/neverhood/gamevars.cpp b/engines/neverhood/gamevars.cpp new file mode 100644 index 0000000000..3e4e60438a --- /dev/null +++ b/engines/neverhood/gamevars.cpp @@ -0,0 +1,114 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "neverhood/gamevars.h" + +namespace Neverhood { + +GameVars::GameVars() { + addVar(0, 0); +} + +GameVars::~GameVars() { +} + +uint32 GameVars::getGlobalVar(uint32 nameHash) { + //debug("GameVars::getGlobalVar(%08X)", nameHash); + int16 varIndex = findSubVarIndex(0, nameHash); + return varIndex != -1 ? _vars[varIndex].value : 0; +} + +void GameVars::setGlobalVar(uint32 nameHash, uint32 value) { + //debug("GameVars::setGlobalVar(%08X, %d)", nameHash, value); + _vars[getSubVarIndex(0, nameHash)].value = value; +} + +uint32 GameVars::getSubVar(uint32 nameHash, uint32 subNameHash) { + //debug("GameVars::getSubVar(%08X, %08X)", nameHash, 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) { + //debug("GameVars::setSubVar(%08X, %08X, %d)", nameHash, subNameHash, value); + int16 varIndex = getSubVarIndex(0, nameHash); + //debug(" varIndex = %d", varIndex); + int16 subVarIndex = getSubVarIndex(varIndex, subNameHash); + //debug(" subVarIndex = %d", subVarIndex); + _vars[subVarIndex].value = value; + //_vars[getSubVarIndex(getSubVarIndex(0, nameHash), subNameHash)].value = value; +} + +int16 GameVars::addVar(uint32 nameHash, uint32 value) { + //debug("GameVars::addVar(%08X, %d)", nameHash, 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) { + //debug("GameVars::findSubVarIndex(%d, %08X)", varIndex, 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) { + //debug("GameVars::addSubVar(%d, %08X, %d)", varIndex, subNameHash, 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) { + //debug("GameVars::getSubVarIndex(%d, %08X)", varIndex, subNameHash); + int16 subVarIndex = findSubVarIndex(varIndex, subNameHash); + if (subVarIndex == -1) { + subVarIndex = addSubVar(varIndex, subNameHash, 0); + debug("need to create: subVarIndex = %d", subVarIndex); + } + return subVarIndex; +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/gamevars.h b/engines/neverhood/gamevars.h new file mode 100644 index 0000000000..863aa1b2c1 --- /dev/null +++ b/engines/neverhood/gamevars.h @@ -0,0 +1,57 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef NEVERHOOD_GAMEVARS_H +#define NEVERHOOD_GAMEVARS_H + +#include "common/array.h" +#include "neverhood/neverhood.h" + +namespace Neverhood { + +struct GameVar { + uint32 nameHash; + uint32 value; + int16 firstIndex, nextIndex; +}; + +class GameVars { +public: + GameVars(); + ~GameVars(); + // TODO void load(???); + // TODO void save(???); + uint32 getGlobalVar(uint32 nameHash); + void setGlobalVar(uint32 nameHash, uint32 value); + uint32 getSubVar(uint32 nameHash, uint32 subNameHash); + void setSubVar(uint32 nameHash, uint32 subNameHash, uint32 value); +protected: + Common::Array<GameVar> _vars; + int16 addVar(uint32 nameHash, uint32 value); + int16 findSubVarIndex(int16 varIndex, uint32 subNameHash); + int16 addSubVar(int16 varIndex, uint32 subNameHash, uint32 value); + int16 getSubVarIndex(int16 varIndex, uint32 subNameHash); +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_GAMEVARS_H */ diff --git a/engines/neverhood/graphics.cpp b/engines/neverhood/graphics.cpp new file mode 100644 index 0000000000..5bb9424f57 --- /dev/null +++ b/engines/neverhood/graphics.cpp @@ -0,0 +1,321 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "neverhood/graphics.h" +#include "neverhood/resource.h" +#include "neverhood/screen.h" + +namespace Neverhood { + +BaseSurface::BaseSurface(NeverhoodEngine *vm, int priority, int16 width, int16 height) + : _vm(vm), _priority(priority), _visible(true), _transparent(true) { + + _drawRect.x = 0; + _drawRect.y = 0; + _drawRect.width = width; + _drawRect.height = height; + // TODO: Check if _sysRect is needed at all in the reimplementation... + _sysRect.x = 0; + _sysRect.y = 0; + _sysRect.width = (width + 3) & 0xFFFC; // align by 4 bytes + _sysRect.height = height; + _clipRect.x1 = 0; + _clipRect.y1 = 0; + _clipRect.x2 = 640; + _clipRect.y2 = 480; + _surface = new Graphics::Surface(); + _surface->create(_sysRect.width, _sysRect.height, Graphics::PixelFormat::createFormatCLUT8()); +} + +BaseSurface::~BaseSurface() { + delete _surface; +} + +void BaseSurface::draw() { + if (_surface && _visible && _drawRect.width > 0 && _drawRect.height > 0) { + if (_sysRect.x == 0 && _sysRect.y == 0) { + _vm->_screen->drawSurface2(_surface, _drawRect, _clipRect, _transparent); + } else { + _vm->_screen->drawUnk(_surface, _drawRect, _sysRect, _clipRect, _transparent); + } + } +} + +void BaseSurface::addDirtyRect() { + // TODO +} + +void BaseSurface::clear() { + _surface->fillRect(Common::Rect(0, 0, _surface->w, _surface->h), 0); +} + +void BaseSurface::drawSpriteResource(SpriteResource &spriteResource) { + if (spriteResource.getDimensions().width <= _drawRect.width && + spriteResource.getDimensions().height <= _drawRect.height) { + clear(); + spriteResource.draw((byte*)_surface->pixels, _surface->pitch, false, false); + } +} + +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((byte*)_surface->pixels, _surface->pitch, flipX, flipY); + } + } +} + +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, (byte*)_surface->pixels, _surface->pitch, flipX, flipY); + } + } +} + +void BaseSurface::drawMouseCursorResource(MouseCursorResource &mouseCursorResource, int frameNum) { + if (frameNum < 3) { + mouseCursorResource.draw(frameNum, (byte*)_surface->pixels, _surface->pitch); + } +} + +void BaseSurface::copyFrom(Graphics::Surface *sourceSurface, int16 x, int16 y, NDrawRect &sourceRect, bool transparent) { + // TODO: Clipping + byte *source = (byte*)sourceSurface->getBasePtr(sourceRect.x, sourceRect.y); + byte *dest = (byte*)_surface->getBasePtr(x, y); + int height = sourceRect.height; + if (!transparent) { + while (height--) { + memcpy(dest, source, sourceRect.width); + source += sourceSurface->pitch; + dest += _surface->pitch; + } + } else { + while (height--) { + for (int xc = 0; xc < sourceRect.width; xc++) + if (source[xc] != 0) + dest[xc] = source[xc]; + source += sourceSurface->pitch; + dest += _surface->pitch; + } + } +} + +// FontSurface + +FontSurface::FontSurface(NeverhoodEngine *vm, NPointArray *tracking, uint16 numRows, byte firstChar, uint16 charWidth, uint16 charHeight) + : BaseSurface(vm, 0, charWidth * 16, charHeight * numRows), _tracking(tracking), _numRows(numRows), _firstChar(firstChar), + _charWidth(charWidth), _charHeight(charHeight) { +} + +void FontSurface::drawChar(BaseSurface *destSurface, int16 x, int16 y, byte chr) { + NDrawRect sourceRect; + chr -= _firstChar; + sourceRect.x = (chr % 16) * _charWidth; + sourceRect.y = (chr / 16) * _charHeight; + sourceRect.width = _charWidth; + sourceRect.height = _charHeight; + destSurface->copyFrom(_surface, x, y, sourceRect, true); +} + +void FontSurface::drawString(BaseSurface *destSurface, int16 x, int16 y, const byte *string) { + for (; *string != 0; string++) { + drawChar(destSurface, x, y, *string); + x += (*_tracking)[*string - _firstChar].x; + } +} + +// Misc + +void parseBitmapResource(byte *sprite, bool *rle, NDimensions *dimensions, NPoint *position, byte **palette, byte **pixels) { + + uint16 flags; + + flags = READ_LE_UINT16(sprite); + sprite += 2; + + if (rle) + *rle = flags & 1; + + if (flags & 2) { + 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 & 4) { + 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 & 8) { + if (palette) + *palette = sprite; + sprite += 1024; + } else if (palette) + *palette = NULL; + + if (flags & 0x10) { + if (pixels) + *pixels = sprite; + } else if (pixels) + *pixels = NULL; + +} + +void unpackSpriteRle(byte *source, int width, int height, byte *dest, int destPitch, bool flipX, bool flipY) { + + // TODO: Flip Y + + int16 rows, chunks; + int16 skip, copy; + + rows = READ_LE_UINT16(source); + chunks = READ_LE_UINT16(source + 2); + source += 4; + + do { + if (chunks == 0) { + dest += rows * destPitch; + } else { + while (rows-- > 0) { + uint16 rowChunks = chunks; + while (rowChunks-- > 0) { + skip = READ_LE_UINT16(source); + copy = READ_LE_UINT16(source + 2); + source += 4; + if (!flipX) { + memcpy(dest + skip, source, copy); + } else { + byte *flipDest = dest + width - skip - 1; + for (int xc = 0; xc < copy; xc++) { + *flipDest-- = source[xc]; + } + } + source += copy; + } + dest += destPitch; + } + } + rows = READ_LE_UINT16(source); + chunks = READ_LE_UINT16(source + 2); + source += 4; + } while (rows > 0); + +} + +void unpackSpriteRleRepl(byte *source, int width, int height, byte *dest, int destPitch, byte oldColor, byte newColor, bool flipX, bool flipY) { + + // TODO: Flip Y + + int16 rows, chunks; + int16 skip, copy; + + 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) { + for (int xc = 0; xc < copy; xc++) { + dest[skip + xc] = source[xc] == oldColor ? newColor : source[xc]; + } + } else { + byte *flipDest = dest + width - skip - 1; + for (int xc = 0; xc < copy; xc++) { + *flipDest-- = source[xc] == oldColor ? newColor : source[xc]; + } + } + source += copy; + } + dest += destPitch; + } + } + rows = READ_LE_UINT16(source); + chunks = READ_LE_UINT16(source + 2); + source += 4; + } while (rows > 0); + +} + +void unpackSpriteNormal(byte *source, int width, int height, byte *dest, int destPitch, bool flipX, bool flipY) { + + // TODO: Flip Y + + int sourcePitch = (width + 3) & 0xFFFC; + + 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) { + int16 deltaX = ABS(x1 - x2); + int16 deltaY = ABS(y1 - y2); + return sqrt((double)(deltaX * deltaX + deltaY * deltaY)); +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/graphics.h b/engines/neverhood/graphics.h new file mode 100644 index 0000000000..c2adb11913 --- /dev/null +++ b/engines/neverhood/graphics.h @@ -0,0 +1,134 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef NEVERHOOD_GRAPHICS_H +#define NEVERHOOD_GRAPHICS_H + +#include "common/array.h" +#include "common/file.h" +#include "graphics/surface.h" +#include "neverhood/neverhood.h" + +namespace Neverhood { + +struct NPoint { + int16 x, y; +}; + +typedef Common::Array<NPoint> NPointArray; + +struct NDimensions { + int16 width, height; +}; + +struct NRect { + int16 x1, y1, x2, y2; + NRect() : x1(0), y1(0), x2(0), y2(0) {} + NRect(int16 x01, int16 y01, int16 x02, int16 y02) : x1(x01), y1(y01), x2(x02), y2(y02) {} + void set(int16 x01, int16 y01, int16 x02, int16 y02) { + x1 = x01; + y1 = y01; + x2 = x02; + y2 = y02; + } +}; + +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; + +// NOTE: "Restore" methods aren't need in the reimplementation as they're DirectDraw-specific + +class BaseSurface { +public: + BaseSurface(NeverhoodEngine *vm, int priority, int16 width, int16 height); + virtual ~BaseSurface(); + virtual void draw(); + virtual void addDirtyRect(); + 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, bool transparent); + 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; } + bool getVisible() const { return _visible; } + void setVisible(bool value) { _visible = value; } + void setTransparent(bool value) { _transparent = value; } + Graphics::Surface *getSurface() { return _surface; } +protected: + NeverhoodEngine *_vm; + int _priority; + bool _visible; + Graphics::Surface *_surface; + NDrawRect _drawRect; + NDrawRect _sysRect; + NRect _clipRect; + bool _transparent; +}; + +class FontSurface : public BaseSurface { +public: + FontSurface(NeverhoodEngine *vm, NPointArray *tracking, uint16 numRows, byte firstChar, uint16 charWidth, uint16 charHeight); + void drawChar(BaseSurface *destSurface, int16 x, int16 y, byte chr); + void drawString(BaseSurface *destSurface, int16 x, int16 y, const byte *string); +protected: + NPointArray *_tracking; + uint16 _numRows; + byte _firstChar; + uint16 _charWidth; + uint16 _charHeight; +}; + +// Misc + +void parseBitmapResource(byte *sprite, bool *rle, NDimensions *dimensions, NPoint *position, byte **palette, byte **pixels); +void unpackSpriteRle(byte *source, int width, int height, byte *dest, int destPitch, bool flipX, bool flipY); +void unpackSpriteRleRepl(byte *source, int width, int height, byte *dest, int destPitch, byte oldColor, byte newColor, bool flipX, bool flipY); +void unpackSpriteNormal(byte *source, int width, int height, byte *dest, int destPitch, bool flipX, bool flipY); +int calcDistance(int16 x1, int16 y1, int16 x2, int16 y2); + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_GRAPHICS_H */ diff --git a/engines/neverhood/klayman.cpp b/engines/neverhood/klayman.cpp new file mode 100644 index 0000000000..567788b729 --- /dev/null +++ b/engines/neverhood/klayman.cpp @@ -0,0 +1,4921 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "neverhood/klayman.h" +#include "neverhood/collisionman.h" +#include "neverhood/resourceman.h" +#include "neverhood/staticdata.h" + +namespace Neverhood { + +static const KlaymanTableItem klaymanTable1[] = { + {1, &Klayman::stDoIdlePickEar}, + {1, &Klayman::sub41FDA0}, + {1, &Klayman::sub41FDF0}, + {1, &Klayman::stDoIdleChest}, + {1, &Klayman::sub41FEB0} +}; + +static const KlaymanTableItem klaymanTable2[] = { + {1, &Klayman::stDoIdlePickEar}, + {1, &Klayman::sub41FDA0}, + {1, &Klayman::stDoIdleChest}, + {1, &Klayman::sub41FEB0} +}; + +#if 0 +static const KlaymanTableItem klaymanTable3[] = { + {1, &Klayman::sub421430}, + {1, &Klayman::sub421480} +}; +#endif + +static const KlaymanTableItem klaymanTable4[] = { + {1, &Klayman::sub41FDA0}, + {1, &Klayman::stDoIdleChest}, + {1, &Klayman::sub41FEB0}, +}; + +// Klayman + +Klayman::Klayman(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y, int surfacePriority, int objectPriority, NRectArray *clipRects) + : AnimatedSprite(vm, objectPriority), _soundResource1(vm), _soundResource2(vm), + _counterMax(0), _counter(0), _flagE4(false), _counter3Max(0), _flagF8(false), _counter1(0), + _counter2(0), /*_field118(0), */_status2(0), _flagE5(true), _attachedSprite(NULL), _flagE1(false), + _status3(1), _parentScene(parentScene), _flagE2(false), _flagE3(false), _flagF6(false), _flagF7(false), + _flagFA(false), _statusE0(0), _field114(0), _resourceHandle(-1), _soundFlag(false) { + + // TODO DirtySurface + createSurface(surfacePriority, 320, 200); + _x = x; + _y = y; + _x4 = x; + _y4 = y; + _flags = 2; + setKlaymanTable1(); + stTryStandIdle(); + SetUpdateHandler(&Klayman::update); +} + +void Klayman::xUpdate() { + // Empty +} + +uint32 Klayman::xHandleMessage(int messageNum, const MessageParam ¶m) { + switch (messageNum) { + case 0x4001: + case 0x4800: + sub41C930(param.asPoint().x, false); + break; + case 0x4004: + GotoState(&Klayman::stTryStandIdle); + break; + case 0x4818: + sub41C930(_dataResource.getPoint(param.asInteger()).x, false); + break; + } + return 0; +} + +void Klayman::update() { + AnimatedSprite::update(); + xUpdate(); +} + +void Klayman::setKlaymanTable(const KlaymanTableItem *table, int tableCount) { + _table = table; + _tableCount = tableCount; + _tableMaxValue = 0; + for (int i = 0; i < tableCount; i++) { + _tableMaxValue += table[i].value; + } +} + +void Klayman::setKlaymanTable1() { + setKlaymanTable(klaymanTable1, ARRAYSIZE(klaymanTable1)); +} + +void Klayman::setKlaymanTable2() { + setKlaymanTable(klaymanTable2, ARRAYSIZE(klaymanTable2)); +} + +void Klayman::setKlaymanTable3() { + // TODO setKlaymanTable(klaymanTable3, ARRAYSIZE(klaymanTable3)); +} + +void Klayman::stDoIdlePickEar() { + sub41D320(0x5B20C814, AnimationCallback(&Klayman::stIdlePickEar)); +} + +void Klayman::stIdlePickEar() { + _status2 = 1; + _flagE5 = true; + setFileHash(0x5B20C814, 0, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::hmIdlePickEar); + SetSpriteCallback(NULL); + NextState(&Klayman::stStandAround); + FinalizeState(&Klayman::evIdlePickEarDone); +} + +uint32 Klayman::hmIdlePickEar(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = handleMessage41D480(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x04DBC02C) { + _soundResource1.play(0x44528AA1); + } + break; + } + return messageResult; +} + +void Klayman::evIdlePickEarDone() { + _soundResource1.stop(); +} + +void Klayman::sub41FDA0() { + sub41D320(0xD122C137, AnimationCallback(&Klayman::sub41FDB0)); +} + +void Klayman::sub41FDB0() { + _status2 = 1; + _flagE5 = true; + setFileHash(0xD122C137, 0, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::handleMessage41E980); + SetSpriteCallback(NULL); + NextState(&Klayman::stStandAround); +} + +uint32 Klayman::handleMessage41E980(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = handleMessage41D480(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x808A0008) { + _soundResource1.play(0xD948A340); + } + break; + } + return messageResult; +} + +void Klayman::sub41FDF0() { + sub41D320(0x543CD054, AnimationCallback(&Klayman::sub41FE00)); +} + +void Klayman::sub41FE00() { + _status2 = 1; + _flagE5 = true; + setFileHash(0x543CD054, 0, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::handleMessage41E9E0); + SetSpriteCallback(NULL); + NextState(&Klayman::stStandAround); + FinalizeState(&Klayman::sub41FE50); +} + +void Klayman::sub41FE50() { + _soundResource1.stop(); +} + +uint32 Klayman::handleMessage41E9E0(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = handleMessage41D480(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x5A0F0104) { + _soundResource1.play(0x7970A100); + } else if (param.asInteger() == 0x9A9A0109) { + _soundResource1.play(0xD170CF04); + } else if (param.asInteger() == 0x989A2169) { + _soundResource1.play(0xD073CF14); + } + break; + } + return messageResult; +} + +void Klayman::stDoIdleChest() { + sub41D320(0x40A0C034, AnimationCallback(&Klayman::stIdleChest)); +} + +void Klayman::stIdleChest() { + _status2 = 1; + _flagE5 = true; + setFileHash(0x40A0C034, 0, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::hmIdleChest); + SetSpriteCallback(NULL); + NextState(&Klayman::stStandAround); +} + +uint32 Klayman::hmIdleChest(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = handleMessage41D480(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x0D2A0288) { + _soundResource1.play(0xD192A368); + } + break; + } + return messageResult; +} + +void Klayman::sub41FEB0() { + sub41D320(0x5120E137, AnimationCallback(&Klayman::sub41FEC0)); +} + +void Klayman::sub41FEC0() { + _status2 = 1; + _flagE5 = true; + setFileHash(0x5120E137, 0, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::handleMessage41EFE0); + SetSpriteCallback(NULL); + NextState(&Klayman::stStandAround); +} + +uint32 Klayman::handleMessage41EFE0(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = handleMessage41D480(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0xC006000C) { + _soundResource1.play(0x9D406340); + } else if (param.asInteger() == 0x2E4A2940) { + _soundResource1.play(0x53A4A1D4); + } else if (param.asInteger() == 0xAA0A0860) { + _soundResource1.play(0x5BE0A3C6); + } else if (param.asInteger() == 0xC0180260) { + _soundResource1.play(0x5D418366); + } + break; + } + return messageResult; +} + +void Klayman::sub421350() { + _status2 = 0; + _flagE5 = true; + setFileHash(0x582EC138, 0, -1); + _counter = 0; + SetSpriteCallback(NULL); + SetUpdateHandler(&Klayman::update41D1C0); + SetMessageHandler(&Klayman::handleMessage41D360); + _counter3 = 0; + _counterMax = 8; + _counter3Max = _vm->_rnd->getRandomNumber(64 - 1) + 24; +} + +void Klayman::update41D1C0() { + update(); + _counter++; + if (_counter >= _counterMax) { + _counter = 0; + if (_table) { + int randomValue = _vm->_rnd->getRandomNumber(_tableMaxValue); + for (int i = 0; i < _tableCount; i++) { + if (randomValue < _table[_tableCount].value) { + (this->*(_table[_tableCount].callback))(); + _counterMax = _vm->_rnd->getRandomNumber(128) + 24; + break; + } + randomValue -= _table[_tableCount].value; + } + } + } else { + _counter3++; + if (_counter3 >= _counter3Max) { + _counter3 = 0; + _counter3Max = _vm->_rnd->getRandomNumber(64) + 24; + stIdleSitBlink(); + } + } +} + +void Klayman::stIdleSitBlink() { + _status2 = 0; + _flagE5 = true; + setFileHash(0x5C24C018, 0, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::handleMessage41D480); + SetSpriteCallback(NULL); + NextState(&Klayman::stIdleSitBlinkSecond); +} + +void Klayman::stIdleSitBlinkSecond() { + _status2 = 0; + _flagE5 = true; + setFileHash(0x5C24C018, 0, -1); + SetUpdateHandler(&Klayman::update41D1C0); + SetMessageHandler(&Klayman::handleMessage41D360); + SetSpriteCallback(NULL); +} + +void Klayman::stPickUpNeedle() { + setDoDeltaX(_attachedSprite->getX() < _x ? 1 : 0); + if (!stStartAction(AnimationCallback(&Klayman::stPickUpNeedle))) { + _status2 = 1; + _flagE5 = false; + setFileHash(0x1449C169, 0, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::hmPickUpGeneric); + SetSpriteCallback(NULL); + } +} + +void Klayman::sub41FFF0() { + setDoDeltaX(_attachedSprite->getX() < _x ? 1 : 0); + if (!stStartAction(AnimationCallback(&Klayman::sub41FFF0))) { + _status2 = 1; + _flagE5 = false; + setFileHash(0x0018C032, 0, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::handleMessage41D640); + SetSpriteCallback(NULL); + } +} + +uint32 Klayman::handleMessage41D640(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = handleMessage41D480(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0xC1380080) { + if (_attachedSprite) { + sendMessage(_attachedSprite, 0x4806, 0); + _soundResource1.play(0xC8004340); + } + } else if (param.asInteger() == 0x02B20220) { + _soundResource1.play(0xC5408620); + } else if (param.asInteger() == 0x03020231) { + _soundResource1.play(0xD4C08010); + } else if (param.asInteger() == 0x67221A03) { + _soundResource1.play(0x44051000); + } else if (param.asInteger() == 0x925A0C1E) { + _soundResource1.play(0x40E5884D); + } + break; + } + return messageResult; +} + +void Klayman::sub4214D0() { + _status2 = 0; + _flagE5 = false; + setFileHash(0xD229823D, 0, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::handleMessage41D480); + SetSpriteCallback(NULL); +} + +void Klayman::sub421510() { + _status2 = 0; + _flagE5 = false; + setFileHash(0x9A2801E0, 0, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::handleMessage41D480); + SetSpriteCallback(NULL); +} + +void Klayman::stStepOver() { + if (!stStartAction(AnimationCallback(&Klayman::stStepOver))) { + _status2 = 2; + _flagE5 = false; + setFileHash(0x004AA310, 0, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::handleMessage41EC70); + SetSpriteCallback(&Klayman::spriteUpdate41F230); + } +} + +void Klayman::stSitInTeleporter() { + if (!stStartAction(AnimationCallback(&Klayman::stSitInTeleporter))) { + _status2 = 0; + _flagE5 = false; + setFileHash(0x392A0330, 0, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::handleMessage41EAB0); + SetSpriteCallback(&Klayman::spriteUpdate41F230); + } +} + +uint32 Klayman::handleMessage41EAB0(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = handleMessage41D480(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x001A2832) { + _soundResource1.play(0xC0E4884C); + } + break; + } + return messageResult; +} + +void Klayman::sub421310() { + _status2 = 0; + _flagE5 = false; + setFileHash(0x913AB120, 0, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::handleMessage41D480); + SetSpriteCallback(&Klayman::spriteUpdate41F230); +} + +///////////////////////////////////////////////////////////////// + +void Klayman::sub41CE70() { + _x4 = _x; + if (!_flagE1 && !_flagE2 && !_flagE3) { + gotoState(NULL); + sub41C7B0(); + } +} + +void Klayman::sub41D320(uint32 fileHash, AnimationCb callback) { + _resourceHandle = _vm->_res->useResource(fileHash); + if (_resourceHandle != -1) { + // TODO _vm->_res->moveToFront(_resourceHandle); + NextState(callback); + SetUpdateHandler(&Klayman::update41D2B0); + } +} + +void Klayman::update41D2B0() { + // TODO Check if this odd stuff is needed or just some cache optimization + if (_vm->_res->isResourceDataValid(_resourceHandle)) { + sub41C7B0(); + // TODO _vm->_res->moveToBack(_resourceHandle); + _vm->_res->unuseResource(_resourceHandle); + _resourceHandle = -1; + } else { + // TODO _vm->_res->moveToFront(_resourceHandle); + } + update(); +} + +bool Klayman::stStartActionFromIdle(AnimationCb callback) { + if (_status2 == 2) { + _status2 = 1; + _flagE5 = false; + setFileHash(0x9A7020B8, 0, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::handleMessage41F140); + SetSpriteCallback(NULL); + NextState(callback); + return true; + } + return false; +} + +void Klayman::sub41C7B0() { + if (_finalizeStateCb) { + AnimationCb cb = _finalizeStateCb; + _finalizeStateCb = NULL; + (this->*cb)(); + } + if (_nextStateCb) { + AnimationCb cb = _nextStateCb; + _nextStateCb = NULL; + (this->*cb)(); +#if 0 // TODO + } else if (_callbackList) { + removeCallbackList(); +#endif + } else { + sendMessage(_parentScene, 0x1006, 0); + } +} + +void Klayman::sub41C770() { + _flagFA = false; + _status3 = 1; +} + +void Klayman::sub41C790() { + if (_flagFA) + _status3 = 0; +} + +void Klayman::stTryStandIdle() { + if (!stStartActionFromIdle(AnimationCallback(&Klayman::stTryStandIdle))) { + _status2 = 1; + _flagE5 = true; + setFileHash(0x5420E254, 0, -1); + _counter = 0; + _counter3 = 0; + _counter3Max = _vm->_rnd->getRandomNumber(64) + 24; + SetUpdateHandler(&Klayman::update41D0F0); + SetMessageHandler(&Klayman::handleMessage41D360); + SetSpriteCallback(NULL); + } +} + +void Klayman::update41D0F0() { + update(); + _counter++; + if (_counter >= 720) { + _counter = 0; + if (_table) { + int randomValue = _vm->_rnd->getRandomNumber(_tableMaxValue); + for (int i = 0; i < _tableCount; i++) { + if (randomValue < _table[_tableCount].value) { + (this->*(_table[_tableCount].callback))(); + break; + } + randomValue -= _table[_tableCount].value; + } + } + } else { + _counter3++; + if (_counter3 >= _counter3Max) { + _counter3 = 0; + _counter3Max = _vm->_rnd->getRandomNumber(64) + 24; + stStand(); + } + } +} + +uint32 Klayman::handleMessage41D360(int messageNum, const MessageParam ¶m, Entity *sender) { + Sprite::handleMessage(messageNum, param, sender); + uint32 messageResult = xHandleMessage(messageNum, param); + switch (messageNum) { + case 0x1008: + messageResult = _flagE5; + break; + case 0x1014: + _attachedSprite = (Sprite*)(param.asEntity()); + break; + case 0x1019: + sub41C7B0(); + break; + case 0x101C: + sub41C770(); + break; + case 0x1021: + sub41C790(); + break; + case 0x481C: + _status3 = param.asInteger(); + _flagFA = true; + messageResult = 1; + break; + case 0x482C: + if (param.asInteger() != 0) { + debug("#################################################"); + // TODO _rectResource.getRectangle2(param.asInteger(), &_field118, &_field114,); + } else { + debug("#################################################"); + // TODO _field114 = 0; + } + break; + } + return messageResult; +} + +void Klayman::stStand() { + _status2 = 1; + _flagE5 = true; + setFileHash(0x5900C41E, 0, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::handleMessage41D480); + SetSpriteCallback(NULL); + NextState(&Klayman::stStandAround); +} + +uint32 Klayman::handleMessage41D480(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = handleMessage41D360(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + sub41C7B0(); + break; + } + return messageResult; +} + +void Klayman::stStandAround() { + _status2 = 1; + _flagE5 = true; + setFileHash(0x5420E254, 0, -1); + SetUpdateHandler(&Klayman::update41D0F0); + SetMessageHandler(&Klayman::handleMessage41D360); + SetSpriteCallback(NULL); +} + +uint32 Klayman::handleMessage41F140(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = handleMessage41D480(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x271AA210) { + _soundResource1.play(0x4924AAC4); + } else if (param.asInteger() == 0x2B22AA81) { + _soundResource1.play(0x0A2AA8E0); + } + break; + } + return messageResult; +} + + +void Klayman::sub41C930(int16 x, bool flag) { + int16 xdiff = ABS(x - _x); + if (x == _x) { + _x4 = x; + if (!_flagE1 && !_flagE2 && !_flagE3) { + gotoState(NULL); + sub41C7B0(); + } + } else if (xdiff <= 36 && !_flagE1 && !_flagE2 && !_flagE3) { + _x4 = x; + gotoState(NULL); + sub41C7B0(); + } else if (xdiff <= 42 && _status3 != 3) { + if (_flagE2 && ((!_doDeltaX && x - _x > 0) || (_doDeltaX && x - _x < 0)) && ABS(_x4 - _x) > xdiff) { + _x4 = x; + } else { + _x4 = x; + GotoState(&Klayman::stSneak); + } + } else if (_flagE1 && ((!_doDeltaX && x - _x > 0) || (_doDeltaX && x - _x < 0))) { + _x4 = x; + } else if (flag) { + _x4 = x; + error("// TODO AnimatedSprite_GotoState(&Klayman::sub421550));"); + // TODO AnimatedSprite_GotoState(&Klayman::sub421550); + } else { + _x4 = x; + GotoState(&Klayman::stStartWalking); + } +} + +void Klayman::stWakeUp() { + _status2 = 1; + _flagE5 = false; + setFileHash(0x527AC970, 0, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::handleMessage41D480); + SetSpriteCallback(NULL); +} + +void Klayman::stSleeping() { + _status2 = 0; + _flagE5 = true; + setFileHash(0x5A38C110, 0, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::hmSleeping); + SetSpriteCallback(NULL); +} + +uint32 Klayman::hmSleeping(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = handleMessage41D360(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x03060012) { + _soundResource1.play(0xC0238244); + } + break; + } + return messageResult; +} + +bool Klayman::stStartAction(AnimationCb callback3) { + if (_status2 == 1) { + _status2 = 2; + _flagE5 = false; + setFileHash(0x5C7080D4, 0, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::handleMessage41F140); + SetSpriteCallback(&Klayman::spriteUpdate41F250); + NextState(callback3); + return true; + } else { + _x = _x4; + return false; + } +} + +void Klayman::spriteUpdate41F250() { + + int16 xdiff = _x4 - _x; + + if (_doDeltaX) { + _x -= _deltaX; + } else { + _x += _deltaX; + } + _deltaX = 0; + + if (_doDeltaY) { + _y -= _deltaY; + } else { + _y += _deltaY; + } + _deltaY = 0; + + if (_flag) { + if (xdiff > 6) + _x += 6; + else if (xdiff < -6) + _x -= 6; + else + _x = _x4; + } + + processDelta(); + +} + +void Klayman::spriteUpdate41F5F0() { + + int16 xdiff = _x4 - _x; + + if (_frameIndex == 9) { + if (xdiff > 26) + _deltaX += xdiff - 26; + else if (xdiff < -26) + _deltaX -= xdiff + 26; + } + + if (xdiff > _deltaX) + xdiff = _deltaX; + else if (xdiff < -_deltaX) + xdiff = -_deltaX; + _deltaX = 0; + + if (_x4 != _x) { + HitRect *hitRectPrev = _vm->_collisionMan->findHitRectAtPos(_x, _y); + _x += xdiff; + if (_field114) { + error("// TODO Klayman_sub_41CF70"); + // TODO Klayman_sub_41CF70 + } else { + HitRect *hitRectNext = _vm->_collisionMan->findHitRectAtPos(_x, _y); + if (hitRectNext->type == 0x5002) { + _y = MAX<int16>(hitRectNext->rect.y1, hitRectNext->rect.y2 - (hitRectNext->rect.x2 - _x) / 2); + } else if (hitRectNext->type == 0x5003) { + _y = MAX<int16>(hitRectNext->rect.y1, hitRectNext->rect.y2 - (_x - hitRectNext->rect.x1) / 2); + } else if (hitRectPrev->type == 0x5002) { + if (xdiff > 0) { + _y = hitRectPrev->rect.y2; + } else { + _y = hitRectPrev->rect.y1; + } + } else if (hitRectPrev->type == 0x5003) { + if (xdiff < 0) { + _y = hitRectPrev->rect.y2; + } else { + _y = hitRectPrev->rect.y1; + } + } + } + processDelta(); + } + +} + +void Klayman::stSneak() { + _status2 = 1; + _flagE2 = true; + _flagE5 = true; + setDoDeltaX(_x4 < _x ? 1 : 0); + setFileHash(0x5C48C506, 0, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::handleMessage41DD80); + SetSpriteCallback(&Klayman::spriteUpdate41F5F0); + FinalizeState(&Klayman::stSneakDone); +} + +void Klayman::stSneakDone() { + _flagE2 = false; +} + +uint32 Klayman::handleMessage41DD80(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = handleMessage41D360(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x32180101) { + _soundResource1.play(0x4924AAC4); + } else if (param.asInteger() == 0x0A2A9098) { + _soundResource1.play(0x0A2AA8E0); + } else if (param.asInteger() == 0x32188010) { + if (_soundFlag) { + _soundResource1.play(0x48498E46); + } else { + _soundResource1.play(0x405002D8); + } + } else if (param.asInteger() == 0x02A2909C) { + if (_soundFlag) { + _soundResource1.play(0x50399F64); + } else { + _soundResource1.play(0x0460E2FA); + } + } + break; + case 0x3002: + _x = _x4; + sub41C7B0(); + break; + } + return messageResult; +} + +void Klayman::sub41CD70(int16 x) { + if (x > _x) { + if (ABS(x - _x) <= 105) { + sub41CAC0(x); + } else { + sub41C930(x, false); + } + } else if (x == _x) { + _x4 = x; + gotoState(NULL); + sub41C7B0(); + } else { + sub41C930(x, false); + } +} + +void Klayman::stStartWalking() { + if (!stStartActionFromIdle(AnimationCallback(&Klayman::stStartWalking))) { + _status2 = 0; + _flagE1 = true; + _flagE5 = true; + setDoDeltaX(_x4 < _x ? 1 : 0); + setFileHash(0x242C0198, 0, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::handleMessage41EC70); + SetSpriteCallback(&Klayman::spriteUpdate41F320); + FinalizeState(&Klayman::stStartWalkingDone); + NextState(&Klayman::stWalking); + } +} + +void Klayman::stStartWalkingDone() { + _flagE1 = false; +} + +uint32 Klayman::handleMessage41EC70(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = handleMessage41D480(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x32180101) { + if (_soundFlag) { + _soundResource1.play(0x48498E46); + } else { + _soundResource1.play(0x405002D8); + } + } else if (param.asInteger() == 0x0A2A9098) { + if (_soundFlag) { + _soundResource1.play(0x50399F64); + } else { + _soundResource1.play(0x0460E2FA); + } + } + break; + } + return messageResult; +} + +void Klayman::stWalking() { + _status2 = 0; + _flagE1 = true; + _flagE5 = true; + setFileHash(0x1A249001, 0, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::handleMessage41EB70); + SetSpriteCallback(&Klayman::spriteUpdate41F300); + FinalizeState(&Klayman::stStartWalkingDone); + NextState(&Klayman::sub41FA40); +} + +void Klayman::spriteUpdate41F300() { + SetSpriteCallback(&Klayman::spriteUpdate41F320); + _deltaX = 0; +} + +uint32 Klayman::handleMessage41EB70(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = handleMessage41D360(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x32180101) { + if (_soundFlag) { + _soundResource1.play(0x48498E46); + } else { + _soundResource1.play(0x405002D8); + } + } else if (param.asInteger() == 0x0A2A9098) { + if (_soundFlag) { + _soundResource1.play(0x50399F64); + } else { + _soundResource1.play(0x0460E2FA); + } + } + break; + } + return messageResult; +} + +void Klayman::sub41FA40() { + if (_status3 == 2) { + sub41C7B0(); + } else if (_status3 == 3) { + sub420F20(); + } else { + _flagE2 = true; + _flagE5 = true; + if (ABS(_x4 - _x) <= 42 && _frameIndex >= 5 && _frameIndex <= 11) { + if (_status3 == 0) { + _status2 = 1; + setFileHash(0xF234EE31, 0, -1); + } else { + _status2 = 2; + setFileHash(0xF135CC21, 0, -1); + } + } else if (ABS(_x4 - _x) <= 10 && (_frameIndex >= 12 || _frameIndex <= 4)) { + if (_status3 == 0) { + _status2 = 1; + setFileHash(0x8604A152, 0, -1); + } else { + _status2 = 2; + setFileHash(0xA246A132, 0, -1); + } + } + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::handleMessage41DD80); + SetSpriteCallback(&Klayman::spriteUpdate41F5F0); + FinalizeState(&Klayman::stSneakDone); + } +} + +void Klayman::spriteUpdate41F320() { + int16 xdiff = ABS(_x4 - _x); + int16 xdelta = _x4 - _x; + + if (xdelta > _deltaX) + xdelta = _deltaX; + else if (xdelta < -_deltaX) + xdelta = -_deltaX; + + _deltaX = 0; + + if (xdiff == 0) { + sendMessage(this, 0x1019, 0); + } else if (_status3 != 2 && _status3 != 3 && xdiff <= 42 && _frameIndex >= 5 && _frameIndex <= 11) { + sendMessage(this, 0x1019, 0); + } else if (_status3 != 2 && _status3 != 3 && xdiff <= 10 && (_frameIndex >= 12 || _frameIndex <= 4)) { + sendMessage(this, 0x1019, 0); + } else if (_status3 == 3 && xdiff < 30) { + sendMessage(this, 0x1019, 0); + } else if (_status3 == 3 && xdiff < 150 && _frameIndex >= 6) { + sendMessage(this, 0x1019, 0); + } else { + HitRect *hitRectPrev = _vm->_collisionMan->findHitRectAtPos(_x, _y); + _x += xdelta; + if (_field114) { + error("_field114"); + // TODO Klayman_sub_41CF70 + } else { + HitRect *hitRectNext = _vm->_collisionMan->findHitRectAtPos(_x, _y); + if (hitRectNext->type == 0x5002) { + _y = MAX<int16>(hitRectNext->rect.y1, hitRectNext->rect.y2 - (hitRectNext->rect.x2 - _x) / 2); + } else if (hitRectNext->type == 0x5003) { + _y = MAX<int16>(hitRectNext->rect.y1, hitRectNext->rect.y2 - (_x - hitRectNext->rect.x1) / 2); + } else if (hitRectPrev->type == 0x5002) { + if (xdelta > 0) { + _y = hitRectPrev->rect.y2; + } else { + _y = hitRectPrev->rect.y1; + } + } else if (hitRectPrev->type == 0x5003) { + if (xdelta < 0) { + _y = hitRectPrev->rect.y2; + } else { + _y = hitRectPrev->rect.y1; + } + } else if (_flagF6 && xdelta != 0) { + if (hitRectNext->type == 0x5000) { + _y++; + } else if (hitRectNext->type == 0x5001 && _y > hitRectNext->rect.y1) { + _y--; + } + } + } + processDelta(); + } + +} + +uint32 Klayman::handleMessage41E210(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = handleMessage41D480(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x4AB28209) { + sendMessage(_attachedSprite, 0x482A, 0); + } else if (param.asInteger() == 0x88001184) { + sendMessage(_attachedSprite, 0x482B, 0); + } + break; + } + return messageResult; +} + +void Klayman::stPickUpGeneric() { + setDoDeltaX(_attachedSprite->getX() < _x ? 1 : 0); + if (!stStartAction(AnimationCallback(&Klayman::stPickUpGeneric))) { + _status2 = 1; + _flagE5 = false; + setFileHash(0x1C28C178, 0, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::hmPickUpGeneric); + SetSpriteCallback(NULL); + } +} + +uint32 Klayman::hmPickUpGeneric(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = handleMessage41D480(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0xC1380080) { + if (_attachedSprite) { + sendMessage(_attachedSprite, 0x4806, 0); + } + _soundResource1.play(0x40208200); + } else if (param.asInteger() == 0x02B20220) { + _soundResource1.play(0xC5408620); + } else if (param.asInteger() == 0x03020231) { + _soundResource1.play(0xD4C08010); + } else if (param.asInteger() == 0x67221A03) { + _soundResource1.play(0x44051000); + } else if (param.asInteger() == 0x2EAE0303) { + _soundResource1.play(0x03630300); + } else if (param.asInteger() == 0x61CE4467) { + _soundResource1.play(0x03630300); + } + break; + } + return messageResult; + +} + +void Klayman::stTurnPressButton() { + if (!stStartAction(AnimationCallback(&Klayman::stTurnPressButton))) { + _status2 = 2; + _flagE5 = true; + setFileHash(0x1C02B03D, 0, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::hmPressButton); + SetSpriteCallback(NULL); + } +} + +uint32 Klayman::hmPressButton(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = handleMessage41D480(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x0D01B294) { + if (_attachedSprite) { + sendMessage(_attachedSprite, 0x480B, 0); + } + } else if (param.asInteger() == 0x32180101) { + _soundResource1.play(0x4924AAC4); + } else if (param.asInteger() == 0x0A2A9098) { + _soundResource1.play(0x0A2AA8E0); + } + break; + } + return messageResult; +} + +void Klayman::stStampFloorButton() { + if (!stStartAction(AnimationCallback(&Klayman::stStampFloorButton))) { + _status2 = 2; + _flagE5 = true; + setFileHash(0x1C16B033, 0, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::hmPressButton); + SetSpriteCallback(NULL); + } +} + +void Klayman::stPressButtonSide() { + if (!stStartActionFromIdle(AnimationCallback(&Klayman::stPressButtonSide))) { + _status2 = 1; + _flagE5 = true; + setFileHash(0x1CD89029, 0, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::hmPressButton); + SetSpriteCallback(&Klayman::spriteUpdate41F250); + } +} + +void Klayman::sub41CD00(int16 x) { + if (_x > x) { + if (_x - x <= 105) { + sub41CAC0(x); + } else { + sub41C930(x, false); + } + } else if (_x < x) { + sub41C930(x, false); + } else { + _x4 = x; + gotoState(NULL); + sub41C7B0(); + } +} + +void Klayman::sub41CC40(int16 x1, int16 x2) { + if (_x > x1) { + if (_x == x1 + x2) { + _x4 = x1 + x2; + gotoState(NULL); + sub41C7B0(); + } else if (_x < x1 + x2) { + sub41CAC0(x1 + x2); + } else { + sub41C930(x1 + x2, false); + } + } else { + if (_x == x1 - x2) { + _x4 = x1 - x2; + gotoState(NULL); + sub41C7B0(); + } else if (_x > x1 - x2) { + sub41CAC0(x1 - x2); + } else { + sub41C930(x1 - x2, false); + } + } +} + +void Klayman::sub41CAC0(int16 x) { + int16 xdiff = ABS(x - _x); + if (x == _x) { + _x4 = x; + if (!_flagE1 && !_flagE2 && !_flagE3) { + gotoState(NULL); + sub41C7B0(); + } + } else if (xdiff <= 36 && !_flagE1 && !_flagE2 && !_flagE3) { + _x4 = x; + gotoState(NULL); + sub41C7B0(); + } else if (xdiff <= 42 && _status3 != 3) { + if (_flagE2 && ((!_doDeltaX && x - _x > 0) || (_doDeltaX && x - _x < 0)) && ABS(_x4 - _x) > xdiff) { + _x4 = x; + } else { + _x4 = x; + GotoState(&Klayman::stSneak); + } + } else if (_flagE3 && ((!_doDeltaX && x - _x > 0) || (_doDeltaX && x - _x < 0))) { + _x4 = x; + } else { + _x4 = x; + GotoState(&Klayman::stLargeStep); + } +} + +void Klayman::stLargeStep() { + _status2 = 2; + _flagE3 = true; + _flagE5 = true; + setDoDeltaX(_x4 >= _x ? 1 : 0); + setFileHash(0x08B28116, 0, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::hmLargeStep); + SetSpriteCallback(&Klayman::suLargeStep); + FinalizeState(&Klayman::stLargeStepDone); +} + +void Klayman::stLargeStepDone() { + _flagE3 = false; +} + +void Klayman::suLargeStep() { + int16 xdiff = _x4 - _x; + + if (_doDeltaX) { + _deltaX = -_deltaX; + } + + if (_frameIndex == 7) { + _deltaX = xdiff; + } + + if ((xdiff > 0 && xdiff > _deltaX) || (xdiff < 0 && xdiff < _deltaX)) + xdiff = _deltaX; + + _deltaX = 0; + + if (_x != _x4) { + HitRect *hitRectPrev = _vm->_collisionMan->findHitRectAtPos(_x, _y); + _x += xdiff; + if (_field114) { + error("// TODO Klayman_sub_41CF70();"); + // TODO Klayman_sub_41CF70(); + } else { + HitRect *hitRectNext = _vm->_collisionMan->findHitRectAtPos(_x, _y); + if (hitRectNext->type == 0x5002) { + _y = MAX<int16>(hitRectNext->rect.y1, hitRectNext->rect.y2 - (hitRectNext->rect.x2 - _x) / 2); + } else if (hitRectNext->type == 0x5003) { + _y = MAX<int16>(hitRectNext->rect.y1, hitRectNext->rect.y2 - (_x - hitRectNext->rect.x1) / 2); + } else if (hitRectPrev->type == 0x5002) { + if (xdiff > 0) { + _y = hitRectPrev->rect.y2; + } else { + _y = hitRectPrev->rect.y1; + } + } else if (hitRectPrev->type == 0x5003) { + if (xdiff < 0) { + _y = hitRectPrev->rect.y2; + } else { + _y = hitRectPrev->rect.y1; + } + } + } + processDelta(); + } +} + +uint32 Klayman::hmLargeStep(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = handleMessage41D360(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x32180101) { + _soundResource1.play(0x4924AAC4); + } else if (param.asInteger() == 0x0A2A9098) { + _soundResource1.play(0x0A2AA8E0); + } + break; + case 0x3002: + _x = _x4; + sub41C7B0(); + break; + } + return messageResult; +} + +void Klayman::stWonderAboutHalf() { + _status2 = 0; + _flagE5 = true; + setFileHash(0xD820A114, 0, 10); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::handleMessage41D480); + SetSpriteCallback(NULL); +} + +void Klayman::stWonderAboutAfter() { + _status2 = 1; + _flagE5 = true; + setFileHash(0xD820A114, 30, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::handleMessage41D480); + SetSpriteCallback(NULL); +} + +void Klayman::stTurnToUseHalf() { + _status2 = 0; + _flagE5 = true; + setFileHash(0x9B250AD2, 0, 7); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::handleMessage41EEF0); + SetSpriteCallback(NULL); +} + +uint32 Klayman::handleMessage41EEF0(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = handleMessage41D480(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x32180101) { + _soundResource1.play(0x4924AAC4); + } else if (param.asInteger() == 0x0A2A9098) { + _soundResource1.play(0x0A2AA8E0); + } + break; + } + return messageResult; +} + +void Klayman::stTurnAwayFromUse() { + _status2 = 1; + _flagE5 = true; + setFileHash(0x98F88391, 4, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::handleMessage41EEF0); + SetSpriteCallback(NULL); +} + +void Klayman::stWonderAbout() { + _status2 = 1; + _flagE5 = true; + setFileHash(0xD820A114, 0, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::handleMessage41D480); + SetSpriteCallback(NULL); +} + +void Klayman::stPeekWall() { + _status2 = 1; + _flagE5 = true; + setFileHash(0xAC20C012, 0, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::hmPeekWall); + SetSpriteCallback(NULL); +} + +uint32 Klayman::hmPeekWall(int messageNum, const MessageParam ¶m, Entity *sender) { + int16 speedUpFrameIndex; + switch (messageNum) { + case 0x1008: + speedUpFrameIndex = getFrameIndex(kKlaymanSpeedUpHash); + if (_frameIndex < speedUpFrameIndex) + setFileHash(0xAC20C012, speedUpFrameIndex, -1); + return 0; + case 0x100D: + if (param.asInteger() == 0x32180101) { + _soundResource1.play(0x405002D8); + } else if (param.asInteger() == 0x0A2A9098) { + _soundResource1.play(0x0460E2FA); + } + break; + } + return handleMessage41D480(messageNum, param, sender); +} + +void Klayman::sub420210() { + if (!stStartAction(AnimationCallback(&Klayman::sub420210))) { + _status2 = 0; + setFileHash(0xD82890BA, 0, -1); + sub4201C0(); + } +} + +void Klayman::sub4201C0() { + _flagE5 = false; + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::handleMessage41D790); + SetSpriteCallback(&Klayman::spriteUpdate41F230); + NextState(&Klayman::sub420340); + sendMessage(_attachedSprite, 0x482B, 0); +} + +uint32 Klayman::handleMessage41D790(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = handleMessage41D480(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x168050A0) { + if (_attachedSprite) { + sendMessage(_attachedSprite, 0x4806, 0); + } + _flagE5 = true; + } else if (param.asInteger() == 0x320AC306) { + _soundResource1.play(0x5860C640); + } else if (param.asInteger() == 0x4AB28209) { + sendMessage(_attachedSprite, 0x482A, 0); + } else if (param.asInteger() == 0x88001184) { + sendMessage(_attachedSprite, 0x482B, 0); + } + break; + } + return messageResult; +} + +void Klayman::spriteUpdate41F230() { + AnimatedSprite::updateDeltaXY(); + _x4 = _x; +} + +void Klayman::sub420340() { + _status2 = 0; + _flagE5 = true; + setFileHash(0x4829E0B8, 0, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::handleMessage41D360); + SetSpriteCallback(NULL); +} + +void Klayman::sub420250() { + if (!stStartAction(AnimationCallback(&Klayman::sub420250))) { + _status2 = 0; + setFileHash(0x900980B2, 0, -1); + sub4201C0(); + } +} + +void Klayman::sub420290() { + if (!stStartAction(AnimationCallback(&Klayman::sub420290))) { + _status2 = 0; + _flagE5 = false; + setFileHash(0xBA1910B2, 0, -1); + SetUpdateHandler(&Klayman::update); + SetSpriteCallback(&Klayman::spriteUpdate41F230); + SetMessageHandler(&Klayman::handleMessage41D880); + NextState(&Klayman::sub420380); + sendMessage(_attachedSprite, 0x482B, 0); + } +} + +uint32 Klayman::handleMessage41D880(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = handleMessage41D480(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x168050A0) { + if (_attachedSprite) { + sendMessage(_attachedSprite, 0x4806, 0); + } + } else if (param.asInteger() == 0x320AC306) { + _soundResource1.play(0x5860C640); + } else if (param.asInteger() == 0x4AB28209) { + sendMessage(_attachedSprite, 0x482A, 0); + } else if (param.asInteger() == 0x88001184) { + sendMessage(_attachedSprite, 0x482B, 0); + } + break; + } + return messageResult; +} + +void Klayman::sub420380() { + _status2 = 0; + _flagE5 = true; + setFileHash(0x4A293FB0, 0, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::handleMessage41DAA0); + SetSpriteCallback(NULL); +} + +uint32 Klayman::handleMessage41DAA0(int messageNum, const MessageParam ¶m, Entity *sender) { + if (messageNum == 0x1008) { + sub4203C0(); + return 0; + } + return handleMessage41D360(messageNum, param, sender); +} + +void Klayman::sub4203C0() { + _status2 = 1; + _flagE5 = false; + if (_attachedSprite) { + sendMessage(_attachedSprite, 0x4807, 0); + _attachedSprite = NULL; + } + setFileHash(0xB869A4B9, 0, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::handleMessage41D480); + SetSpriteCallback(NULL); +} + +void Klayman::sub420300() { + if (!stStartAction(AnimationCallback(&Klayman::sub420300))) { + _status2 = 0; + setFileHash(0xB8699832, 0, -1); + sub4201C0(); + } +} + +void Klayman::sub41CCE0(int16 x) { + sub41CC40(_attachedSprite->getX(), x); +} + +void Klayman::sub420970() { + _status2 = 0; + _flagE5 = true; + _statusE0 = 3; + setFileHash2(0x3A292504, 0x01084280, 0); + _fileHash4 = 0x01084280; + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::handleMessage41D360); + SetSpriteCallback(NULL); + sub41C7B0(); +} + +void Klayman::sub4209D0() { + if (!stStartAction(AnimationCallback(&Klayman::sub4209D0))) { + _status2 = 0; + if (_y4 < _y) { + if (_statusE0 == 1) { + _statusE0 = 2; + sub420BC0(); + } else { + sub41C7B0(); + } + } else if (_statusE0 == 0) { + _statusE0 = 2; + _flagE5 = false; + setFileHash(0x122D1505, 0, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::handleMessage41E0D0); + SetSpriteCallback(&Klayman::spriteUpdate41F230); + } else if (_statusE0 == 3) { + _statusE0 = 2; + _flagE5 = false; + setFileHash2(0x122D1505, 0x01084280, 0); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::handleMessage41E0D0); + SetSpriteCallback(&Klayman::spriteUpdate41F230); + } else if (_statusE0 == 1) { + _statusE0 = 2; + _flagE5 = true; + setFileHash(0x122D1505, 29 - _frameIndex, -1); + } + } +} + +void Klayman::sub420BC0() { + _status2 = 2; + if (_statusE0 == 1) { + _statusE0 = 0; + _flagE5 = false; + setFileHash2(0x3A292504, 0x02421405, 0); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::handleMessage41DFD0); + SetSpriteCallback(&Klayman::spriteUpdate41F230); + } else if (_statusE0 == 1) { + _statusE0 = 0; + _flagE5 = false; + setFileHash2(0x122D1505, 0x02421405, 0); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::handleMessage41DFD0); + SetSpriteCallback(&Klayman::spriteUpdate41F230); + } else { + sub41C7B0(); + } +} + +uint32 Klayman::handleMessage41DFD0(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = handleMessage41D480(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x489B025C) { + _soundResource1.play(0x52C4C2D7); + } else if (param.asInteger() == 0x400A0E64) { + _soundResource1.play(0x50E081D9); + } else if (param.asInteger() == 0x32180101) { + _soundResource1.play(0x405002D8); + } else if (param.asInteger() == 0x0A2A9098) { + _soundResource1.play(0x0460E2FA); + } + break; + } + return messageResult; +} + +uint32 Klayman::handleMessage41E0D0(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = handleMessage41D360(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x01084280) { + _flagE5 = true; + } else if (param.asInteger() == 0x489B025C) { + _soundResource1.play(0x52C4C2D7); + } else if (param.asInteger() == 0x400A0E64) { + _soundResource1.play(0x50E081D9); + } else if (param.asInteger() == 0x02421405) { + if (_statusE0 == 1) { + setFileHash2(0x3A292504, 0x01084280, 0); + } else { + setFileHash2(0x122D1505, 0x01084280, 0); + } + if (_statusE0 == 1) { + if (_y4 >= _y - 30) { + sendMessage(this, 0x1019, 0); + } + } else { + if (_y4 <= _y) { + sendMessage(this, 0x1019, 0); + } + } + } + break; + } + return messageResult; +} + +void Klayman::sub420AD0() { + if (!stStartAction(AnimationCallback(&Klayman::sub420AD0))) { + _status2 = 0; + if (_y4 >= _y - 30) { + sub41C7B0(); + } else if (_statusE0 == 0) { + _statusE0 = 1; + _flagE5 = false; + setFileHash(0x3A292504, 0, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::handleMessage41E0D0); + SetSpriteCallback(&Klayman::spriteUpdate41F230); + } else if (_statusE0 == 3) { + _statusE0 = 1; + _flagE5 = true; + setFileHash2(0x3A292504, 0x01084280, 0); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::handleMessage41E0D0); + SetSpriteCallback(&Klayman::spriteUpdate41F230); + } else if (_statusE0 == 2) { + _statusE0 = 1; + _flagE5 = true; + setFileHash(0x3A292504, 29 - _frameIndex, -1); + } + } +} + +void Klayman::sub421030() { + _status2 = 2; + _flagE5 = false; + setFileHash2(0xF229C003, 0x14884392, 0); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::handleMessage41E490); + SetSpriteCallback(&Klayman::spriteUpdate41F230); +} + +uint32 Klayman::handleMessage41E490(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = handleMessage41D480(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x80C110B5) { + sendMessage(_parentScene, 0x482A, 0); + } else if (param.asInteger() == 0x110010D1) { + sendMessage(_parentScene, 0x482B, 0); + } else if (param.asInteger() == 0x32180101) { + if (_soundFlag) { + _soundResource1.play(0x48498E46); + } else { + _soundResource1.play(0x405002D8); + } + } else if (param.asInteger() == 0x0A2A9098) { + if (_soundFlag) { + _soundResource1.play(0x50399F64); + } else { + _soundResource1.play(0x0460E2FA); + } + } + break; + } + return messageResult; +} + +void Klayman::sub420FE0() { + if (!stStartAction(AnimationCallback(&Klayman::sub420FE0))) { + _status2 = 2; + _flagE5 = false; + setFileHash(0xF229C003, 0, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::handleMessage41E490); + SetSpriteCallback(&Klayman::spriteUpdate41F230); + } +} + +void Klayman::sub4210C0() { + if (!stStartAction(AnimationCallback(&Klayman::sub4210C0))) { + _status2 = 0; + _flagE5 = false; + setFileHash2(0xCA221107, 0x8520108C, 0); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::handleMessage41E490); + SetSpriteCallback(&Klayman::spriteUpdate41F230); + } +} + +void Klayman::sub421070() { + if (!stStartAction(AnimationCallback(&Klayman::sub421070))) { + _status2 = 2; + _flagE5 = false; + setFileHash(0xCA221107, 0, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::handleMessage41E490); + SetSpriteCallback(&Klayman::spriteUpdate41F230); + } +} + +void Klayman::stLandOnFeet() { + _status2 = 1; + _flagE5 = true; + setFileHash(0x18118554, 0, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::handleMessage41E290); + SetSpriteCallback(NULL); +} + +uint32 Klayman::handleMessage41E290(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = handleMessage41D480(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x320AC306) { + _soundResource1.play(0x5860C640); + } + break; + } + return messageResult; +} + +void Klayman::sub420ED0() { + if (!stStartAction(AnimationCallback(&Klayman::sub420ED0))) { + _status2 = 2; + _flagE5 = false; + setFileHash(0x91540140, 0, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::handleMessage41E2F0); + SetSpriteCallback(&Klayman::spriteUpdate41F230); + } +} + +uint32 Klayman::handleMessage41E2F0(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = handleMessage41D480(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0xC61A0119) { + _soundResource1.play(0x402338C2); + } else if (param.asInteger() == 0x32180101) { + _soundResource1.play(0x4924AAC4); + } else if (param.asInteger() == 0x0A2A9098) { + _soundResource1.play(0x0A2AA8E0); + } + break; + } + return messageResult; +} + +void Klayman::sub420750() { + if (!stStartAction(AnimationCallback(&Klayman::sub420750))) { + _status2 = 2; + _flagE5 = false; + setFileHash(0x5CCCB330, 0, -1); + SetUpdateHandler(&Klayman::update); + SetSpriteCallback(&Klayman::spriteUpdate41F230); + SetMessageHandler(&Klayman::handleMessage41DD20); + } +} + +void Klayman::stTurnToUse() { + if (!stStartAction(AnimationCallback(&Klayman::stTurnToUse))) { + _status2 = 2; + _flagE5 = false; + setFileHash(0x9B250AD2, 0, -1); + SetUpdateHandler(&Klayman::update); + SetSpriteCallback(&Klayman::spriteUpdate41F230); + SetMessageHandler(&Klayman::handleMessage41EEF0); + } +} + +void Klayman::sub4207F0() { + _status2 = 2; + _flagE5 = false; + setFileHash(0x98F88391, 0, -1); + SetUpdateHandler(&Klayman::update); + SetSpriteCallback(&Klayman::spriteUpdate41F230); + SetMessageHandler(&Klayman::handleMessage41EEF0); +} + +void Klayman::sub420F20() { + _flagF8 = false; + _flagE5 = false; + setFileHash(0x11A8E012, 0, -1); + SetUpdateHandler(&Klayman::update); + SetSpriteCallback(&Klayman::spriteUpdate41F5A0); + SetMessageHandler(&Klayman::handleMessage41EC70); +} + +void Klayman::spriteUpdate41F5A0() { + if (!_flagF8 && ABS(_x4 - _x) < 80) { + sendMessage(_parentScene, 0x4829, 0); + _flagF8 = true; + } + AnimatedSprite::updateDeltaXY(); +} + +void Klayman::stMoveObjectSkipTurnFaceObject() { + setDoDeltaX(_attachedSprite->getX() < _x ? 1 : 0); + _flagE4 = false; + _flagE5 = true; + setFileHash2(0x0C1CA072, 0x01084280, 0); + SetUpdateHandler(&Klayman::update); + SetSpriteCallback(&Klayman::spriteUpdate41F230); + SetMessageHandler(&Klayman::handleMessage41D970); +} + +void Klayman::sub420660() { + sendMessage(_attachedSprite, 0x4807, 0); +} + +uint32 Klayman::handleMessage41D970(int messageNum, const MessageParam ¶m, Entity *sender) { + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x01084280) { + if (_attachedSprite) + sendMessage(_attachedSprite, 0x480B, _doDeltaX ? 1 : 0); + } else if (param.asInteger() == 0x02421405) { + if (_flagE4 && sendMessage(_attachedSprite, 0x480C, _doDeltaX ? 1 : 0) != 0) { + stMoveObjectSkipTurn(); + } else { + FinalizeState(&Klayman::sub420660); + SetMessageHandler(&Klayman::handleMessage41D480); + } + } else if (param.asInteger() == 0x32180101) { + _soundResource1.play(0x405002D8); + } else if (param.asInteger() == 0x0A2A9098) { + _soundResource1.play(0x0460E2FA); + } + break; + case 0x480A: + _flagE4 = true; + return 0; + } + return handleMessage41D480(messageNum, param, sender); +} + +void Klayman::stMoveObjectSkipTurn() { + _flagE4 = false; + _flagE5 = true; + setFileHash2(0x0C1CA072, 0x01084280, 0); + SetUpdateHandler(&Klayman::update); + SetSpriteCallback(&Klayman::spriteUpdate41F230); + SetMessageHandler(&Klayman::handleMessage41D970); +} + +void Klayman::stMoveObjectFaceObject() { + setDoDeltaX(_attachedSprite->getX() < _x ? 1 : 0); + if (!stStartAction(AnimationCallback(&Klayman::stMoveObjectFaceObject))) { + _status2 = 2; + _flagE4 = false; + _flagE5 = true; + setFileHash(0x0C1CA072, 0, -1); + SetUpdateHandler(&Klayman::update); + SetSpriteCallback(&Klayman::spriteUpdate41F230); + SetMessageHandler(&Klayman::handleMessage41D970); + } +} + +void Klayman::sub420C50() { + if (!stStartAction(AnimationCallback(&Klayman::sub420C50))) { + _status2 = 0; + if (_flagF7) { + stReleaseLeverUp(); + } else { + sendMessage(_attachedSprite, 0x482B, 0); + setFileHash(0x0C303040, 0, -1); + SetUpdateHandler(&Klayman::update); + SetSpriteCallback(&Klayman::spriteUpdate41F230); + SetMessageHandler(&Klayman::handleMessage41E210); + NextState(&Klayman::stPullLeverDown); + _flagE5 = false; + } + } +} + +// Exactly the same code as sub420DA0 which was removed +void Klayman::stPullLeverDown() { + setFileHash(0x0D318140, 0, -1); + sendMessage(_attachedSprite, 0x480F, 0); + NextState(&Klayman::stHoldLeverDown); +} + +void Klayman::stHoldLeverDown() { + setFileHash(0x4464A440, 0, -1); + SetUpdateHandler(&Klayman::update); + SetSpriteCallback(&Klayman::spriteUpdate41F230); + SetMessageHandler(&Klayman::handleMessage41D360); + _flagF7 = true; + _flagE5 = true; +} + +void Klayman::stReleaseLeverUp() { + setFileHash(0x09018068, 0, -1); + SetUpdateHandler(&Klayman::update); + SetSpriteCallback(&Klayman::spriteUpdate41F230); + SetMessageHandler(&Klayman::handleMessage41E210); + sendMessage(_attachedSprite, 0x4807, 0); + NextState(&Klayman::stPullLeverDown); + _flagE5 = false; +} + +void Klayman::sub420E20() { + if (_flagF7) { + _status2 = 2; + setFileHash(0x09018068, 0, -1); + SetUpdateHandler(&Klayman::update); + SetSpriteCallback(&Klayman::spriteUpdate41F230); + SetMessageHandler(&Klayman::handleMessage41E210); + sendMessage(_attachedSprite, 0x4807, 0); + NextState(&Klayman::sub420E90); + _flagE5 = false; + _flagF7 = false; + } else { + sub41C7B0(); + } +} + +void Klayman::sub420E90() { + setFileHash(0x0928C048, 0, -1); + FinalizeState(&Klayman::sub420EB0); +} + +void Klayman::sub420EB0() { + sendMessage(_attachedSprite, 0x482A, 0); +} + +void Klayman::sub420680() { + if (!stStartActionFromIdle(AnimationCallback(&Klayman::sub420680))) { + _status2 = 2; + _counter2 = 0; + for (uint32 i = 0; i < 20; i++) { + if (getSubVar(0x02038314, i)) { + setSubVar(0x02720344, i, 1); + setSubVar(0x02038314, i, 0); + _counter2++; + } + } + if (_counter2 == 0) { + gotoState(NULL); + sub41C7B0(); + } else { + setFileHash(0xD8C8D100, 0, -1); + SetUpdateHandler(&Klayman::update); + SetSpriteCallback(&Klayman::spriteUpdate41F250); + SetMessageHandler(&Klayman::handleMessage41DB90); + _flagE5 = false; + _counter2--; + } + } +} + +uint32 Klayman::handleMessage41DB90(int messageNum, const MessageParam ¶m, Entity *sender) { + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x06040580) { + if (_counter2 == 0) { + // TODO: Calc calcHash value somewhere else + setFileHash3(0xD8C8D100, calcHash("GoToStartLoop/Finish"), 0); + } + } else if (_counter2 != 0 && param.asInteger() == calcHash("GoToStartLoop/Finish")) { + _counter2--; + setFileHash2(0xD8C8D100, 0x01084280, 0); + } else if (param.asInteger() == 0x062A1510) { + _soundResource1.play(0x41688704); + } else if (param.asInteger() == 0x02B20220) { + _soundResource1.play(0xC5408620); + } else if (param.asInteger() == 0x0A720138) { + _soundResource1.play(0xD4C08010); + } else if (param.asInteger() == 0xB613A180) { + _soundResource1.play(0x44051000); + } else if (param.asInteger() == 0x0E040501) { + _soundResource1.play(0xC6A129C1); + } + } + return handleMessage41D480(messageNum, param, sender); +} + +uint32 Klayman::handleMessage41DD20(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = handleMessage41D480(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x040D4186) { + if (_attachedSprite) { + sendMessage(_attachedSprite, 0x4808, 0); + } + } + break; + } + return messageResult; +} + +//############################################################################## + +// KmScene1001 + +KmScene1001::KmScene1001(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y) + : Klayman(vm, parentScene, x, y, 1000, 1000) { +} + +uint32 KmScene1001::xHandleMessage(int messageNum, const MessageParam ¶m) { + switch (messageNum) { + case 0x4001: + case 0x4800: + sub41C930(param.asPoint().x, false); + break; + case 0x4004: + GotoState(&Klayman::stTryStandIdle); + break; + case 0x4804: + if (param.asInteger() == 2) + GotoState(&Klayman::stSleeping); + break; + case 0x480D: + GotoState(&KmScene1001::sub44FA50); + break; + case 0x4812: + GotoState(&Klayman::stPickUpGeneric); + break; + case 0x4816: + if (param.asInteger() == 1) { + GotoState(&Klayman::stTurnPressButton); + } else if (param.asInteger() == 2) { + GotoState(&Klayman::stStampFloorButton); + } else { + GotoState(&Klayman::stPressButtonSide); + } + break; + case 0x4817: + setDoDeltaX(param.asInteger()); + sub41C7B0(); + break; + case 0x481B: + // TODO: It's not really a point but an x1/x2 pair + if (param.asPoint().y != 0) { + sub41CC40(param.asPoint().y, param.asPoint().x); + } else { + sub41CCE0(param.asPoint().x); + } + break; + case 0x481F: + if (param.asInteger() == 0) { + GotoState(&Klayman::stWonderAboutHalf); + } else if (param.asInteger() == 1) { + GotoState(&Klayman::stWonderAboutAfter); + } else if (param.asInteger() == 3) { + GotoState(&Klayman::stTurnToUseHalf); + } else if (param.asInteger() == 4) { + GotoState(&Klayman::stTurnAwayFromUse); + } else { + GotoState(&Klayman::stWonderAbout); + } + break; + case 0x482D: + setDoDeltaX(_x > (int16)param.asInteger() ? 1 : 0); + sub41C7B0(); + break; + case 0x4836: + if (param.asInteger() == 1) { + sendMessage(_parentScene, 0x2002, 0); + GotoState(&Klayman::stWakeUp); + } + break; + case 0x483F: + sub41CD00(param.asInteger()); + break; + case 0x4840: + sub41CD70(param.asInteger()); + break; + } + return 0; +} + +void KmScene1001::sub44FA50() { + if (!stStartAction(AnimationCallback(&KmScene1001::sub44FA50))) { + _status2 = 2; + _flagE5 = false; + setFileHash(0x00648953, 0, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&KmScene1001::handleMessage44FA00); + SetSpriteCallback(&AnimatedSprite::updateDeltaXY); + } +} + +uint32 KmScene1001::handleMessage44FA00(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Klayman::handleMessage41E210(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x4AB28209) { + sendMessage(_attachedSprite, 0x480F, 0); + } + break; + } + return messageResult; +} + +// KmScene1002 + +KmScene1002::KmScene1002(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y, Sprite *class599, Sprite *ssLadderArch) + : Klayman(vm, parentScene, x, y, 1000, 1000), _otherSprite(NULL), _class599(class599), _ssLadderArch(ssLadderArch), + _status(0) { + + setKlaymanTable1(); + +} + +void KmScene1002::xUpdate() { + if (_x >= 250 && _x <= 435 && _y >= 420) { + if (_status == 0) { + // TODO setKlaymanTable(stru_4B44C8); + _status = 1; + } + } else if (_status == 1) { + setKlaymanTable1(); + _status = 0; + } +} + +uint32 KmScene1002::xHandleMessage(int messageNum, const MessageParam ¶m) { + switch (messageNum) { + case 0x2001: + GotoState(&KmScene1002::sub449E90); + break; + case 0x2007: + _otherSprite = (Sprite*)param.asEntity(); + break; + case 0x4001: + case 0x4800: + sub41C930(param.asPoint().x, false); + break; + case 0x4004: + GotoState(&Klayman::stTryStandIdle); + break; + case 0x4803: + if (param.asInteger() == 1) { + GotoState(&KmScene1002::stJumpAndFall); + } else if (param.asInteger() == 2) { + GotoState(&KmScene1002::stDropFromRing); + } + break; + case 0x4804: + GotoState(&Klayman::stPeekWall); + break; + case 0x4805: + switch (param.asInteger()) { + case 1: + GotoState(&Klayman::sub420210); + break; + case 2: + GotoState(&Klayman::sub420250); + break; + case 3: + GotoState(&Klayman::sub420290); + break; + case 4: + GotoState(&Klayman::sub420300); + break; + } + break; + case 0x480A: + GotoState(&KmScene1002::stMoveVenusFlyTrap); + break; + case 0x480D: + GotoState(&KmScene1002::sub449E20); + break; + case 0x4816: + if (param.asInteger() == 0) { + GotoState(&KmScene1002::stPressDoorButton); + } + break; + case 0x4817: + setDoDeltaX(param.asInteger()); + sub41C7B0(); + break; + case 0x481B: + sub41CCE0(param.asInteger()); + break; + case 0x4820: + sendMessage(_parentScene, 0x2005, 0); + GotoState(&Klayman::sub420970); + break; + case 0x4821: + sendMessage(_parentScene, 0x2005, 0); + _y4 = param.asInteger(); + GotoState(&Klayman::sub4209D0); + break; + case 0x4822: + sendMessage(_parentScene, 0x2005, 0); + _y4 = param.asInteger(); + GotoState(&Klayman::sub420AD0); + break; + case 0x4823: + sendMessage(_parentScene, 0x2006, 0); + GotoState(&Klayman::sub420BC0); + break; + case 0x482E: + if (param.asInteger() == 1) { + GotoState(&Klayman::sub421030); + } else { + GotoState(&Klayman::sub420FE0); + } + break; + case 0x482F: + if (param.asInteger() == 1) { + GotoState(&Klayman::sub4210C0); + } else { + GotoState(&Klayman::sub421070); + } + break; + case 0x483F: + sub41CD00(param.asInteger()); + break; + case 0x4840: + sub41CD70(param.asInteger()); + break; + } + return 0; +} + +void KmScene1002::update4497D0() { + Klayman::update(); + if (_counter1 != 0 && (--_counter1 == 0)) { + _surface->setVisible(true); + SetUpdateHandler(&Klayman::update); + } +} + +uint32 KmScene1002::handleMessage449800(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = handleMessage41D480(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x168050A0) { + if (_attachedSprite) { + sendMessage(_attachedSprite, 0x480F, 0); + } + } else if (param.asInteger() == 0x586B0300) { + if (_otherSprite) { + sendMessage(_otherSprite, 0x480E, 1); + } + } else if (param.asInteger() == 0x4AB28209) { + if (_attachedSprite) { + sendMessage(_attachedSprite, 0x482A, 0); + } + } else if (param.asInteger() == 0x88001184) { + if (_attachedSprite) { + sendMessage(_attachedSprite, 0x482B, 0); + } + } + break; + } + return messageResult; +} + +uint32 KmScene1002::handleMessage4498E0(int messageNum, const MessageParam ¶m, Entity *sender) { + switch (messageNum) { + case 0x4811: + _soundResource1.play(0x5252A0E4); + setDoDeltaX(((Sprite*)sender)->isDoDeltaX() ? 1 : 0); + if (_doDeltaX) { + _x = ((Sprite*)sender)->getX() - 75; + } else { + _x = ((Sprite*)sender)->getX() + 75; + } + _y = ((Sprite*)sender)->getY() - 200; + if (param.asInteger() == 0) { + sub449EF0(); + } else if (param.asInteger() == 1) { + sub44A0D0(); + } else if (param.asInteger() == 2) { + stSpitOutFall(); + } + break; + } + return 0; +} + +uint32 KmScene1002::hmPressDoorButton(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = handleMessage41D480(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x942D2081) { + _flagE5 = false; + sendMessage(_attachedSprite, 0x2003, 0); + } else if (param.asInteger() == 0xDA600012) { + stHitByBoxingGlove(); + } else if (param.asInteger() == 0x0D01B294) { + _flagE5 = false; + sendMessage(_attachedSprite, 0x480B, 0); + } + break; + } + return messageResult; +} + +uint32 KmScene1002::hmMoveVenusFlyTrap(int messageNum, const MessageParam ¶m, Entity *sender) { + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x01084280) { + if (_attachedSprite) { + sendMessage(_attachedSprite, 0x480B, (uint32)_doDeltaX); + } + } else if (param.asInteger() == 0x02421405) { + if (_flagE4) { + if (_attachedSprite) { + if (sendMessage(_attachedSprite, 0x480C, (uint32)_doDeltaX) != 0) { + stContinueMovingVenusFlyTrap(); + } + } + } else { + SetMessageHandler(&KmScene1002::handleMessage449BA0); + } + } else if (param.asInteger() == 0x4AB28209) { + sendMessage(_attachedSprite, 0x482A, 0); + } else if (param.asInteger() == 0x88001184) { + sendMessage(_attachedSprite, 0x482B, 0); + } else if (param.asInteger() == 0x32180101) { + _soundResource1.play(0x405002D8); + } else if (param.asInteger() == 0x0A2A9098) { + _soundResource1.play(0x0460E2FA); + } + break; + case 0x480A: + _flagE4 = true; + return 0; + } + return handleMessage41D480(messageNum, param, sender); +} + +uint32 KmScene1002::handleMessage449BA0(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = handleMessage41D480(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x4AB28209) { + sendMessage(_attachedSprite, 0x482A, 0); + } else if (param.asInteger() == 0x88001184) { + sendMessage(_attachedSprite, 0x482B, 0); + } else if (param.asInteger() == 0x32180101) { + _soundResource1.play(0x405002D8); + } else if (param.asInteger() == 0x0A2A9098) { + _soundResource1.play(0x0460E2FA); + } + break; + } + return messageResult; +} + +uint32 KmScene1002::handleMessage449C90(int messageNum, const MessageParam ¶m, Entity *sender) { + int16 speedUpFrameIndex; + uint32 messageResult = handleMessage41D480(messageNum, param, sender); + switch (messageNum) { + case 0x1008: + speedUpFrameIndex = getFrameIndex(kKlaymanSpeedUpHash); + if (_frameIndex < speedUpFrameIndex) { + setFileHash(0x35AA8059, speedUpFrameIndex, -1); + _y = 435; + } + messageResult = 0; + break; + case 0x100D: + if (param.asInteger() == 0x1A1A0785) { + _soundResource1.play(0x40F0A342); + } else if (param.asInteger() == 0x60428026) { + _soundResource1.play(0x40608A59); + } + break; + } + return messageResult; +} + +uint32 KmScene1002::handleMessage449D60(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = handleMessage41D360(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x1307050A) { + _soundResource1.play(0x40428A09); + } + break; + } + return messageResult; +} + +void KmScene1002::suFallDown() { + AnimatedSprite::updateDeltaXY(); + HitRect *hitRect = _vm->_collisionMan->findHitRectAtPos(_x, _y + 10); + if (hitRect->type == 0x5001) { + _y = hitRect->rect.y1; + processDelta(); + sendMessage(this, 0x1019, 0); + } + _vm->_collisionMan->checkCollision(this, 0xFFFF, 0x4810, 0); +} + +void KmScene1002::sub449E20() { + if (!stStartAction(AnimationCallback(&KmScene1002::sub449E20))) { + _status2 = 2; + _flagE5 = false; + setFileHash(0x584984B4, 0, -1); + SetUpdateHandler(&Klayman::update); + SetSpriteCallback(&AnimatedSprite::updateDeltaXY); + SetMessageHandler(&KmScene1002::handleMessage449800); + NextState(&Klayman::stLandOnFeet); + sendMessage(_attachedSprite, 0x482B, 0); + } +} + +void KmScene1002::sub449E90() { + _soundResource1.play(0x56548280); + _status2 = 0; + _flagE5 = false; + _surface->setVisible(false); + setFileHash(0x5420E254, 0, -1); + SetUpdateHandler(&Klayman::update); + SetSpriteCallback(NULL); + SetMessageHandler(&KmScene1002::handleMessage4498E0); +} + +void KmScene1002::sub449EF0() { + _counter1 = 1; + _status2 = 0; + _flagE5 = false; + setFileHash(0x000BAB02, 0, -1); + SetUpdateHandler(&KmScene1002::update4497D0); + // Weird stuff happening + SetMessageHandler(&Klayman::handleMessage41D360); + //SetMessageHandler(&Klayman::handleMessage41D480); + SetSpriteCallback(&KmScene1002::suFallDown); + NextState(&KmScene1002::sub449F70); + sendMessage(_class599, 0x482A, 0); + sendMessage(_ssLadderArch, 0x482A, 0); +} + +void KmScene1002::sub449F70() { + sendMessage(_parentScene, 0x1024, 1); + _soundResource1.play(0x41648271); + _status2 = 1; + _flagE5 = false; + _flagE1 = false; + setFileHash2(0x000BAB02, 0x88003000, 0); + SetUpdateHandler(&Klayman::update); + SetSpriteCallback(NULL); + SetMessageHandler(&KmScene1002::handleMessage41D480); + NextState(&KmScene1002::sub44A230); + sendMessage(_parentScene, 0x2002, 0); + // TODO _callbackList = NULL; + _attachedSprite = NULL; + sendMessage(_class599, 0x482B, 0); + sendMessage(_ssLadderArch, 0x482B, 0); +} + +void KmScene1002::stSpitOutFall() { + _counter1 = 1; + _status2 = 0; + _flagE5 = false; + setFileHash(0x9308C132, 0, -1); + SetUpdateHandler(&KmScene1002::update4497D0); + SetSpriteCallback(&KmScene1002::suFallDown); + SetMessageHandler(&Klayman::handleMessage41D480); + NextState(&KmScene1002::sub449F70); + sendMessage(_class599, 0x482A, 0); + sendMessage(_ssLadderArch, 0x482A, 0); +} + +void KmScene1002::sub44A0D0() { + _counter1 = 1; + _status2 = 0; + _flagE5 = false; + setFileHash(0x0013A206, 0, -1); + SetUpdateHandler(&KmScene1002::update4497D0); + SetMessageHandler(&Klayman::handleMessage41D360); + SetSpriteCallback(&KmScene1002::suFallDown); + NextState(&KmScene1002::sub44A150); + sendMessage(_class599, 0x482A, 0); + sendMessage(_ssLadderArch, 0x482A, 0); +} + +void KmScene1002::sub44A150() { + sendMessage(_parentScene, 0x1024, 1); + _soundResource1.play(0x41648271); + _status2 = 1; + _flagE5 = false; + _flagE1 = false; + setFileHash2(0x0013A206, 0x88003000, 0); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&KmScene1002::handleMessage41D480); + SetSpriteCallback(NULL); + NextState(&KmScene1002::sub44A230); + sendMessage(_parentScene, 0x2002, 0); + // TODO _callbackList = NULL; + _attachedSprite = NULL; + sendMessage(_class599, 0x482B, 0); + sendMessage(_ssLadderArch, 0x482B, 0); +} + +void KmScene1002::sub44A230() { + setDoDeltaX(2); + stTryStandIdle(); +} + +void KmScene1002::stJumpAndFall() { + if (!stStartAction(AnimationCallback(&KmScene1002::stJumpAndFall))) { + sendMessage(_parentScene, 0x1024, 3); + _status2 = 2; + _flagE5 = false; + setFileHash(0xB93AB151, 0, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&KmScene1002::handleMessage449D60); + SetSpriteCallback(&KmScene1002::suFallDown); + NextState(&Klayman::stLandOnFeet); + } +} + +void KmScene1002::stDropFromRing() { + if (_attachedSprite) { + _x = _attachedSprite->getX(); + sendMessage(_attachedSprite, 0x4807, 0); + _attachedSprite = NULL; + } + _status2 = 2; + _flagE5 = false; + setFileHash(0x586984B1, 0, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&KmScene1002::handleMessage41D360); + SetSpriteCallback(&KmScene1002::suFallDown); + NextState(&Klayman::stLandOnFeet); +} + +void KmScene1002::stPressDoorButton() { + _status2 = 2; + _flagE5 = true; + setDoDeltaX(0); + setFileHash(0x1CD89029, 0, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&KmScene1002::hmPressDoorButton); + SetSpriteCallback(&Klayman::spriteUpdate41F250); +} + +void KmScene1002::stHitByBoxingGlove() { + _status2 = 1; + _flagE5 = false; + setFileHash(0x35AA8059, 0, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&KmScene1002::handleMessage449C90); + SetSpriteCallback(&AnimatedSprite::updateDeltaXY); + FinalizeState(&KmScene1002::stHitByBoxingGloveDone); +} + +void KmScene1002::stHitByBoxingGloveDone() { + sendMessage(_parentScene, 0x1024, 1); +} + +void KmScene1002::stMoveVenusFlyTrap() { + if (!stStartAction(AnimationCallback(&KmScene1002::stMoveVenusFlyTrap))) { + _status2 = 2; + _flagE4 = false; + _flagE5 = true; + setDoDeltaX(_attachedSprite->getX() < _x ? 1 : 0); + setFileHash(0x5C01A870, 0, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&KmScene1002::hmMoveVenusFlyTrap); + SetSpriteCallback(&AnimatedSprite::updateDeltaXY); + FinalizeState(&KmScene1002::stMoveVenusFlyTrapDone); + } +} + +void KmScene1002::stContinueMovingVenusFlyTrap() { + _flagE4 = false; + _flagE5 = true; + setFileHash2(0x5C01A870, 0x01084280, 0); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&KmScene1002::hmMoveVenusFlyTrap); + SetSpriteCallback(&AnimatedSprite::updateDeltaXY); + FinalizeState(&KmScene1002::stMoveVenusFlyTrapDone); +} + +void KmScene1002::stMoveVenusFlyTrapDone() { + sendMessage(_attachedSprite, 0x482A, 0); +} + +// KmScene1004 + +KmScene1004::KmScene1004(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y) + : Klayman(vm, parentScene, x, y, 1000, 1000) { + + _dataResource.load(0x01900A04); +} + +uint32 KmScene1004::xHandleMessage(int messageNum, const MessageParam ¶m) { + switch (messageNum) { + case 0x4001: + case 0x4800: + sub41C930(param.asPoint().x, false); + break; + case 0x4004: + GotoState(&Klayman::stTryStandIdle); + break; + case 0x4817: + setDoDeltaX(param.asInteger()); + sub41C7B0(); + break; + case 0x4818: + sub41C930(_dataResource.getPoint(param.asInteger()).x, false); + break; + case 0x481E: + GotoState(&KmScene1004::stReadNote); + break; + case 0x4820: + sendMessage(_parentScene, 0x2000, 0); + GotoState(&Klayman::sub420970); + break; + case 0x4821: + sendMessage(_parentScene, 0x2000, 0); + _y4 = param.asInteger(); + GotoState(&Klayman::sub4209D0); + break; + case 0x4822: + sendMessage(_parentScene, 0x2000, 0); + _y4 = param.asInteger(); + GotoState(&Klayman::sub420AD0); + break; + case 0x4823: + sendMessage(_parentScene, 0x2001, 0); + GotoState(&Klayman::sub420BC0); + break; + case 0x4824: + sendMessage(_parentScene, 0x2000, 0); + _y4 = _dataResource.getPoint(param.asInteger()).y; + GotoState(&Klayman::sub4209D0); + break; + case 0x4825: + sendMessage(_parentScene, 0x2000, 0); + _y4 = _dataResource.getPoint(param.asInteger()).y; + GotoState(&Klayman::sub420AD0); + break; + case 0x4828: + GotoState(&Klayman::sub420ED0); + break; + case 0x483F: + sub41CD00(param.asInteger()); + break; + case 0x4840: + sub41CD70(param.asInteger()); + break; + } + return 0; +} + +uint32 KmScene1004::hmReadNote(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = handleMessage41D480(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x04684052) { + _flagE5 = true; + sendMessage(_parentScene, 0x2002, 0); + } + break; + } + return messageResult; +} + +void KmScene1004::stReadNote() { + _status2 = 2; + _flagE5 = false; + setFileHash(0x123E9C9F, 0, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&KmScene1004::hmReadNote); + SetSpriteCallback(&AnimatedSprite::updateDeltaXY); +} + +KmScene1109::KmScene1109(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y) + : Klayman(vm, parentScene, x, y, 1000, 1000), _flag1(false) { + + // Empty +} + +uint32 KmScene1109::xHandleMessage(int messageNum, const MessageParam ¶m) { + switch (messageNum) { + case 0x2000: + _flag1 = param.asInteger() != 0; + break; + case 0x4001: + case 0x4800: + sub41C930(param.asPoint().x, false); + break; + case 0x4004: + if (_flag1) + GotoState(&Klayman::sub421350); + else + GotoState(&Klayman::stTryStandIdle); + break; + case 0x4804: + if (param.asInteger() != 0) { + _x4 = param.asInteger(); + GotoState(&Klayman::stWalking); + } else { + GotoState(&Klayman::stPeekWall); + } + break; + case 0x4817: + setDoDeltaX(param.asInteger()); + sub41C7B0(); + break; + case 0x481D: + if (_flag1) + GotoState(&Klayman::sub4214D0); + break; + case 0x481E: + if (_flag) + GotoState(&Klayman::sub421510); + break; + case 0x4834: + GotoState(&Klayman::stStepOver); + break; + case 0x4835: + sendMessage(_parentScene, 0x2000, 1); + _flag1 = true; + GotoState(&Klayman::stSitInTeleporter); + break; + case 0x4836: + sendMessage(_parentScene, 0x2000, 0); + _flag1 = false; + GotoState(&Klayman::sub421310); + break; + case 0x483D: + sub461F30(); + break; + case 0x483E: + sub461F70(); + break; + } + return 0; +} + +uint32 KmScene1109::handleMessage461EA0(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Klayman::handleMessage41D480(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x4E0A2C24) { + _soundResource1.play(0x85B10BB8); + } else if (param.asInteger() == 0x4E6A0CA0) { + _soundResource1.play(0xC5B709B0); + } + break; + } + return messageResult; +} + +void KmScene1109::sub461F30() { + _status2 = 0; + _flagE5 = false; + SetUpdateHandler(&Klayman::update); + SetSpriteCallback(NULL); + SetMessageHandler(&KmScene1109::handleMessage461EA0); + setFileHash(0x2C2A4A1C, 0, -1); +} + +void KmScene1109::sub461F70() { + _status2 = 0; + _flagE5 = false; + SetUpdateHandler(&Klayman::update); + SetSpriteCallback(NULL); + SetMessageHandler(&KmScene1109::handleMessage461EA0); + setFileHash(0x3C2E4245, 0, -1); +} + +// KmScene1201 + +KmScene1201::KmScene1201(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y, Entity *class464) + : Klayman(vm, parentScene, x, y, 1000, 1000), _class464(class464), _countdown(0) { + + // TODO setKlaymanTable(dword_4AEF10, 3); + _flagF6 = true; + +} + +uint32 KmScene1201::xHandleMessage(int messageNum, const MessageParam ¶m) { + switch (messageNum) { + case 0x4001: + case 0x4800: + sub41C930(param.asPoint().x, false); + break; + case 0x4004: + GotoState(&Klayman::stTryStandIdle); + break; + case 0x480A: + GotoState(&KmScene1201::sub40DF00); + break; + case 0x4812: + GotoState(&Klayman::stPickUpGeneric); + break; + case 0x4813: + GotoState(&KmScene1201::stFetchMatch); + break; + case 0x4814: + GotoState(&KmScene1201::stTumbleHeadless); + break; + case 0x4815: + GotoState(&KmScene1201::sub40E040); + break; + case 0x4816: + if (param.asInteger() == 0) { + GotoState(&Klayman::stPressButtonSide); + } + break; + case 0x4817: + setDoDeltaX(param.asInteger()); + sub41C7B0(); + break; + case 0x481B: + if (param.asPoint().y != 0) { + sub41CC40(param.asPoint().y, param.asPoint().x); + } else { + sub41CCE0(param.asPoint().x); + } + break; + case 0x481D: + GotoState(&Klayman::stTurnToUse); + break; + case 0x481E: + GotoState(&Klayman::sub4207F0); + break; + case 0x481F: + GotoState(&Klayman::stWonderAbout); + break; + case 0x482D: + setDoDeltaX(_x > (int16)param.asInteger() ? 1 : 0); + sub41C7B0(); + break; + case 0x483F: + sub41CD00(param.asInteger()); + break; + case 0x4840: + sub41CD70(param.asInteger()); + break; + } + return 0; +} + +void KmScene1201::update40DBE0() { + if (_x >= 380) + sub41C7B0(); + Klayman::update(); +} + +uint32 KmScene1201::hmMatch(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Klayman::handleMessage41D480(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x51281850) { + setGlobalVar(0x20A0C516, 1); + } else if (param.asInteger() == 0x43000538) { + _soundResource1.play(0x21043059); + } else if (param.asInteger() == 0x02B20220) { + _soundResource1.play(0xC5408620); + } else if (param.asInteger() == 0x0A720138) { + _soundResource1.play(0xD4C08010); + } else if (param.asInteger() == 0xB613A180) { + _soundResource1.play(0x44051000); + } + break; + } + return messageResult; +} + +void KmScene1201::stFetchMatch() { + if (!stStartAction(AnimationCallback(&KmScene1201::stFetchMatch))) { + _status2 = 0; + _flagE5 = false; + setDoDeltaX(_attachedSprite->getX() < _x ? 1 : 0); + setFileHash(0x9CAA0218, 0, -1); + SetUpdateHandler(&Klayman::update); + SetSpriteCallback(NULL); + SetMessageHandler(&KmScene1201::hmMatch); + NextState(&KmScene1201::stLightMatch); + } +} + +void KmScene1201::stLightMatch() { + _status2 = 1; + _flagE5 = false; + setDoDeltaX(_attachedSprite->getX() < _x ? 1 : 0); + setFileHash(0x1222A513, 0, -1); + SetUpdateHandler(&Klayman::update); + SetSpriteCallback(NULL); + SetMessageHandler(&KmScene1201::hmMatch); +} + +uint32 KmScene1201::handleMessage40DDF0(int messageNum, const MessageParam ¶m, Entity *sender) { + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x01084280) { + _soundResource1.play(0x405002D8); + if (_attachedSprite) { + sendMessage(_attachedSprite, 0x480B, 0); + } + } else if (param.asInteger() == 0x02421405) { + if (_countdown != 0) { + _countdown--; + stMoveObjectSkipTurn(); + } else { + SetMessageHandler(&Klayman::handleMessage41D480); + } + } + break; + } + return Klayman::handleMessage41D480(messageNum, param, sender); +} + +uint32 KmScene1201::hmTumbleHeadless(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Klayman::handleMessage41D480(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x000F0082) { + _soundResource1.play(0x74E2810F); + } + break; + } + return messageResult; +} + +void KmScene1201::sub40DF00() { + if (!stStartAction(AnimationCallback(&KmScene1201::sub40DF00))) { + _status2 = 2; + _flagE5 = false; + _countdown = 8; + setDoDeltaX(0); + setFileHash(0x0C1CA072, 0, -1); + SetUpdateHandler(&KmScene1201::update40DBE0); + SetSpriteCallback(&AnimatedSprite::updateDeltaXY); + SetMessageHandler(&KmScene1201::handleMessage40DDF0); + } +} + +void KmScene1201::stMoveObjectSkipTurn() { + _flagE5 = false; + setFileHash2(0x0C1CA072, 0x01084280, 0); + SetUpdateHandler(&KmScene1201::update40DBE0); + SetSpriteCallback(&AnimatedSprite::updateDeltaXY); + SetMessageHandler(&KmScene1201::handleMessage40DDF0); +} + +void KmScene1201::stTumbleHeadless() { + if (!stStartActionFromIdle(AnimationCallback(&KmScene1201::stTumbleHeadless))) { + _status2 = 1; + _flagE5 = false; + setDoDeltaX(0); + setFileHash(0x2821C590, 0, -1); + SetUpdateHandler(&Klayman::update); + SetSpriteCallback(&AnimatedSprite::updateDeltaXY); + SetMessageHandler(&KmScene1201::hmTumbleHeadless); + NextState(&Klayman::stTryStandIdle); + sendMessage(_class464, 0x2006, 0); + _soundResource1.play(0x62E0A356); + } +} + +void KmScene1201::sub40E040() { + if (!stStartActionFromIdle(AnimationCallback(&KmScene1201::sub40E040))) { + _status2 = 1; + _flagE5 = false; + setFileHash(0x5420E254, 0, -1); + SetUpdateHandler(&Klayman::update); + SetSpriteCallback(NULL); + SetMessageHandler(&Klayman::handleMessage41D360); + } +} + +KmScene1303::KmScene1303(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y) + : Klayman(vm, parentScene, x, y, 1000, 1000) { + + // Empty +} + +uint32 KmScene1303::xHandleMessage(int messageNum, const MessageParam ¶m) { + switch (messageNum) { + case 0x4804: + GotoState(&KmScene1303::stPeekWall1); + break; + case 0x483B: + GotoState(&KmScene1303::stPeekWallReturn); + break; + case 0x483C: + GotoState(&KmScene1303::stPeekWall2); + break; + } + return 0; +} + +uint32 KmScene1303::hmPeekWallReturn(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = handleMessage41D480(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == calcHash("PopBalloon")) { + sendMessage(_parentScene, 0x2000, 0); + } else if (param.asInteger() == 0x02B20220) { + _soundResource1.play(0xC5408620); + } else if (param.asInteger() == 0x0A720138) { + _soundResource1.play(0xD4C08010); + } else if (param.asInteger() == 0xB613A180) { + _soundResource1.play(0x44051000); + } + break; + } + return messageResult; +} + +void KmScene1303::update4161A0() { + Klayman::update(); + _counter3++; + if (_counter3 >= _counter3Max) + stPeekWall3(); +} + +void KmScene1303::stPeekWall1() { + _status2 = 0; + _flagE5 = true; + setFileHash(0xAC20C012, 8, 37); + SetUpdateHandler(&Klayman::update); + SetSpriteCallback(NULL); + SetMessageHandler(&Klayman::handleMessage41D480); + NextState(&KmScene1303::stPeekWall3); +} + +void KmScene1303::stPeekWall2() { + _status2 = 1; + _flagE5 = false; + setFileHash(0xAC20C012, 43, 49); + SetUpdateHandler(&Klayman::update); + SetSpriteCallback(NULL); + SetMessageHandler(&Klayman::handleMessage41D480); +} + +void KmScene1303::stPeekWall3() { + _counter3 = 0; + _status2 = 0; + _flagE5 = true; + _counter3Max = _vm->_rnd->getRandomNumber(64) + 24; + setFileHash(0xAC20C012, 38, 42); + SetUpdateHandler(&KmScene1303::update4161A0); + SetSpriteCallback(NULL); + SetMessageHandler(&Klayman::handleMessage41D360); + _newHashListIndex = 42; +} + +void KmScene1303::stPeekWallReturn() { + _status2 = 0; + _flagE5 = false; + setFileHash(0x2426932E, 0, -1); + SetUpdateHandler(&Klayman::update); + SetSpriteCallback(NULL); + SetMessageHandler(&KmScene1303::hmPeekWallReturn); +} + +KmScene1304::KmScene1304(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y) + : Klayman(vm, parentScene, x, y, 1000, 1000) { + + // Empty +} + +uint32 KmScene1304::xHandleMessage(int messageNum, const MessageParam ¶m) { + switch (messageNum) { + case 0x4001: + case 0x4800: + sub41C930(param.asPoint().x, false); + break; + case 0x4004: + GotoState(&Klayman::stTryStandIdle); + break; + case 0x4812: + if (param.asInteger() == 2) { + GotoState(&Klayman::stPickUpNeedle); + } else if (param.asInteger() == 1) { + GotoState(&Klayman::sub41FFF0); + } else { + GotoState(&Klayman::stPickUpGeneric); + } + break; + case 0x4817: + setDoDeltaX(param.asInteger()); + sub41C7B0(); + break; + case 0x481B: + if (param.asPoint().y != 0) { + sub41CC40(param.asPoint().y, param.asPoint().x); + } else { + sub41CCE0(param.asPoint().x); + } + break; + case 0x481F: + if (param.asInteger() == 1) { + GotoState(&Klayman::stTurnAwayFromUse); + } else if (param.asInteger() == 0) { + GotoState(&Klayman::stTurnToUseHalf); + } else { + GotoState(&Klayman::stWonderAbout); + } + break; + case 0x483F: + sub41CD00(param.asInteger()); + break; + case 0x4840: + sub41CD70(param.asInteger()); + break; + } + return 0; +} + +KmScene1305::KmScene1305(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y) + : Klayman(vm, parentScene, x, y, 1000, 1000) { + + // Empty +} + +uint32 KmScene1305::xHandleMessage(int messageNum, const MessageParam ¶m) { + switch (messageNum) { + case 0x4001: + case 0x4800: + sub41C930(param.asPoint().x, false); + break; + case 0x4004: + GotoState(&Klayman::stTryStandIdle); + break; + case 0x4804: + GotoState(&KmScene1305::stCrashDown); + break; + case 0x4817: + setDoDeltaX(param.asInteger()); + sub41C7B0(); + break; + } + return 0; +} + +void KmScene1305::stCrashDown() { + _soundResource1.play(0x41648271); + _status2 = 1; + _flagE5 = false; + setFileHash2(0x000BAB02, 0x88003000, 0); + SetUpdateHandler(&Klayman::update); + SetSpriteCallback(NULL); + SetMessageHandler(&Klayman::handleMessage41D480); + NextState(&KmScene1305::cbCrashDownEvent); +} + +void KmScene1305::cbCrashDownEvent() { + setDoDeltaX(2); + stTryStandIdle(); +} + +KmScene1306::KmScene1306(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y) + : Klayman(vm, parentScene, x, y, 1000, 1000) { + + _flag1 = false; +} + +uint32 KmScene1306::xHandleMessage(int messageNum, const MessageParam ¶m) { + uint32 messageResult = 0; + switch (messageNum) { + case 0x2000: + _flag1 = param.asInteger() != 0; + break; + case 0x4001: + case 0x4800: + sub41C930(param.asPoint().x, false); + break; + case 0x4004: + if (_flag1) + GotoState(&Klayman::sub421350); + else + GotoState(&Klayman::stTryStandIdle); + break; + case 0x4812: + if (param.asInteger() == 2) { + GotoState(&Klayman::stPickUpNeedle); + } else if (param.asInteger() == 1) { + GotoState(&Klayman::sub41FFF0); + } else { + GotoState(&Klayman::stPickUpGeneric); + } + break; + case 0x4816: + if (param.asInteger() == 1) { + GotoState(&Klayman::stTurnPressButton); + } else if (param.asInteger() == 2) { + GotoState(&Klayman::stStampFloorButton); + } else { + GotoState(&Klayman::stPressButtonSide); + } + break; + case 0x4817: + setDoDeltaX(param.asInteger()); + sub41C7B0(); + break; + case 0x481A: + GotoState(&Klayman::sub420680); + break; + case 0x481B: + if (param.asPoint().y != 0) { + sub41CC40(param.asPoint().y, param.asPoint().x); + } else { + sub41CCE0(param.asPoint().x); + } + break; + case 0x481D: + if (_flag1) + GotoState(&Klayman::sub4214D0); + else + GotoState(&Klayman::stTurnToUse); + break; + case 0x481E: + if (_flag1) + GotoState(&Klayman::sub421510); + else + GotoState(&Klayman::sub4207F0); + break; + case 0x481F: + if (param.asInteger() == 1) { + GotoState(&Klayman::stWonderAboutAfter); + } else if (param.asInteger() == 0) { + GotoState(&Klayman::stWonderAboutHalf); + } else if (param.asInteger() == 4) { + GotoState(&Klayman::stTurnAwayFromUse); + } else if (param.asInteger() == 3) { + GotoState(&Klayman::stTurnToUseHalf); + } else { + GotoState(&Klayman::stWonderAbout); + } + break; + case 0x482D: + setDoDeltaX(_x > (int16)param.asInteger() ? 1 : 0); + sub41C7B0(); + break; + case 0x482E: + if (param.asInteger() == 1) { + GotoState(&Klayman::sub421030); + } else { + GotoState(&Klayman::sub420FE0); + } + break; + case 0x482F: + if (param.asInteger() == 1) { + GotoState(&Klayman::sub4210C0); + } else { + GotoState(&Klayman::sub421070); + } + break; + case 0x4834: + GotoState(&Klayman::stStepOver); + break; + case 0x4835: + sendMessage(_parentScene, 0x2000, 1); + _flag1 = true; + GotoState(&Klayman::stSitInTeleporter); + break; + case 0x4836: + sendMessage(_parentScene, 0x2000, 0); + _flag1 = false; + GotoState(&Klayman::sub421310); + break; + case 0x483D: + sub417D40(); + break; + case 0x483E: + sub417D80(); + break; + case 0x483F: + sub41CD00(param.asInteger()); + break; + case 0x4840: + sub41CD70(param.asInteger()); + break; + } + return messageResult; +} + +void KmScene1306::sub417D40() { + _status2 = 0; + _flagE5 = false; + setFileHash(0xEE084A04, 0, -1); + SetUpdateHandler(&Klayman::update); + SetSpriteCallback(NULL); + SetMessageHandler(&KmScene1306::handleMessage417CB0); +} + +void KmScene1306::sub417D80() { + _status2 = 0; + _flagE5 = false; + setFileHash(0xB86A4274, 0, -1); + SetUpdateHandler(&Klayman::update); + SetSpriteCallback(NULL); + SetMessageHandler(&KmScene1306::handleMessage417CB0); +} + +uint32 KmScene1306::handleMessage417CB0(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = handleMessage41D480(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x4E0A2C24) { + _soundResource1.play(0x85B10BB8); + } else if (param.asInteger() == 0x4E6A0CA0) { + _soundResource1.play(0xC5B709B0); + } + } + return messageResult; +} + +KmScene1308::KmScene1308(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y) + : Klayman(vm, parentScene, x, y, 1000, 1000) { + + _flag1 = false; +} + +uint32 KmScene1308::xHandleMessage(int messageNum, const MessageParam ¶m) { + switch (messageNum) { + case 0x4001: + case 0x4800: + sub41C930(param.asPoint().x, false); + break; + case 0x4004: + GotoState(&Klayman::stTryStandIdle); + break; + case 0x480A: + if (param.asInteger() == 1) { + GotoState(&Klayman::stMoveObjectSkipTurnFaceObject); + } else { + GotoState(&Klayman::stMoveObjectFaceObject); + } + break; + case 0x480D: + GotoState(&KmScene1001::sub420C50); + break; + case 0x4812: + if (param.asInteger() == 2) { + GotoState(&Klayman::stPickUpNeedle); + } else if (param.asInteger() == 1) { + GotoState(&Klayman::sub41FFF0); + } else { + GotoState(&Klayman::stPickUpGeneric); + } + break; + case 0x4817: + setDoDeltaX(param.asInteger()); + sub41C7B0(); + break; + case 0x481A: + if (param.asInteger() == 1) { + GotoState(&KmScene1308::sub456150); + } else { + GotoState(&Klayman::sub420680); + } + break; + case 0x481B: + if (param.asPoint().y != 0) { + sub41CC40(param.asPoint().y, param.asPoint().x); + } else { + sub41CCE0(param.asPoint().x); + } + break; + case 0x481D: + GotoState(&Klayman::stTurnToUse); + break; + case 0x481E: + GotoState(&Klayman::sub4207F0); + break; + case 0x4827: + GotoState(&Klayman::sub420E20); + break; + case 0x4834: + GotoState(&Klayman::stStepOver); + break; + case 0x483F: + sub41CD00(param.asInteger()); + break; + case 0x4840: + sub41CD70(param.asInteger()); + break; + } + return 0; +} + +uint32 KmScene1308::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Klayman::handleMessage41D480(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (!_flag1 && param.asInteger() == 0x06040580) { + setFileHash3(0xDC409440, 0x46431401, 0); + } else if (_flag1 && param.asInteger() == 0x46431401) { + _flag1 = false; + setFileHash2(0xDC409440, 0x01084280, 0); + } else if (param.asInteger() == 0x062A1510) { + _soundResource1.play(0x41688704); + } else if (param.asInteger() == 0x02B20220) { + _soundResource1.play(0xC5408620); + } else if (param.asInteger() == 0x0A720138) { + _soundResource1.play(0xD4C08010); + } else if (param.asInteger() == 0xB613A180) { + _soundResource1.play(0x44051000); + } else if (param.asInteger() == 0x0E4C8141) { + _soundResource1.play(0xDC4A1280); + } + break; + } + return messageResult; +} + +void KmScene1308::sub456150() { + if (!stStartActionFromIdle(AnimationCallback(&KmScene1308::sub456150))) { + _status2 = 2; + _flag1 = false; + for (uint i = 0; i < 3; i++) { + if (getSubVar(0x0090EA95, i)) { + bool more; + setSubVar(0x08D0AB11, i, 1); + setSubVar(0x0090EA95, i, 0); + do { + more = false; + setSubVar(0xA010B810, i, _vm->_rnd->getRandomNumber(16 - 1)); + for (uint j = 0; j < i && !more; j++) { + if (getSubVar(0x08D0AB11, j) && getSubVar(0xA010B810, j) == getSubVar(0xA010B810, i)) + more = true; + } + if (getSubVar(0xA010B810, i) == getSubVar(0x0C10A000, i)) + more = true; + } while (more); + _flag1 = true; + } + } + if (!_flag1) { + gotoState(NULL); + sub41C7B0(); + } else { + _flagE5 = false; + setFileHash(0xDC409440, 0, -1); + SetUpdateHandler(&Klayman::update); + SetSpriteCallback(&Klayman::spriteUpdate41F250); + SetMessageHandler(&KmScene1308::handleMessage); + _flag1 = false; + } + } +} + +// KmScene1401 + +KmScene1401::KmScene1401(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y) + : Klayman(vm, parentScene, x, y, 1000, 1000) { + + // Empty +} + +uint32 KmScene1401::xHandleMessage(int messageNum, const MessageParam ¶m) { + switch (messageNum) { + case 0x4001: + case 0x4800: + sub41C930(param.asPoint().x, false); + break; + case 0x4004: + GotoState(&Klayman::stTryStandIdle); + break; + case 0x480A: + if (param.asInteger() == 1) { + GotoState(&Klayman::stMoveObjectSkipTurnFaceObject); + } else { + GotoState(&Klayman::stMoveObjectFaceObject); + } + break; + case 0x4816: + if (param.asInteger() == 1) { + GotoState(&Klayman::stTurnPressButton); + } else if (param.asInteger() == 2) { + GotoState(&Klayman::stStampFloorButton); + } else { + GotoState(&Klayman::stPressButtonSide); + } + break; + case 0x4817: + setDoDeltaX(param.asInteger()); + sub41C7B0(); + break; + case 0x481B: + if (param.asPoint().y != 0) { + sub41CC40(param.asPoint().y, param.asPoint().x); + } else { + sub41CCE0(param.asPoint().x); + } + break; + case 0x481F: + if (param.asInteger() == 1) { + GotoState(&Klayman::stTurnAwayFromUse); + } else if (param.asInteger() == 0) { + GotoState(&Klayman::stTurnToUseHalf); + } else { + GotoState(&Klayman::stWonderAbout); + } + break; + case 0x482D: + setDoDeltaX(_x > (int16)param.asInteger() ? 1 : 0); + sub41C7B0(); + break; + case 0x482E: + if (param.asInteger() == 1) { + GotoState(&Klayman::sub421030); + } else { + GotoState(&Klayman::sub420FE0); + } + break; + case 0x482F: + if (param.asInteger() == 1) { + GotoState(&Klayman::sub4210C0); + } else { + GotoState(&Klayman::sub421070); + } + break; + } + return 0; +} + +// KmScene1402 + +KmScene1402::KmScene1402(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y) + : Klayman(vm, parentScene, x, y, 1000, 1000) { + + SetFilterY(&Sprite::defFilterY); +} + +uint32 KmScene1402::xHandleMessage(int messageNum, const MessageParam ¶m) { + switch (messageNum) { + case 0x4001: + case 0x4800: + sub41C930(param.asPoint().x, false); + break; + case 0x4004: + GotoState(&Klayman::stTryStandIdle); + break; + case 0x480A: + if (param.asInteger() == 1) { + GotoState(&Klayman::stMoveObjectSkipTurnFaceObject); + } else { + GotoState(&Klayman::stMoveObjectFaceObject); + } + break; + case 0x4817: + setDoDeltaX(param.asInteger()); + sub41C7B0(); + break; + case 0x481B: + if (param.asPoint().y != 0) { + sub41CC40(param.asPoint().y, param.asPoint().x); + } else { + sub41CCE0(param.asPoint().x); + } + break; + case 0x481D: + GotoState(&Klayman::stTurnToUse); + break; + case 0x481E: + GotoState(&Klayman::sub4207F0); + break; + } + return 0; +} + +// KmScene1403 + +KmScene1403::KmScene1403(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y) + : Klayman(vm, parentScene, x, y, 1000, 1000) { + + setKlaymanTable(klaymanTable4, ARRAYSIZE(klaymanTable4)); +} + +uint32 KmScene1403::xHandleMessage(int messageNum, const MessageParam ¶m) { + switch (messageNum) { + case 0x4001: + case 0x4800: + sub41C930(param.asPoint().x, false); + break; + case 0x4004: + GotoState(&Klayman::stTryStandIdle); + break; + case 0x480A: + if (param.asInteger() == 1) { + GotoState(&Klayman::stMoveObjectSkipTurnFaceObject); + } else { + GotoState(&Klayman::stMoveObjectFaceObject); + } + break; + case 0x480D: + GotoState(&KmScene1001::sub420C50); + break; + case 0x4812: + if (param.asInteger() == 2) { + GotoState(&Klayman::stPickUpNeedle); + } else if (param.asInteger() == 1) { + GotoState(&Klayman::sub41FFF0); + } else { + GotoState(&Klayman::stPickUpGeneric); + } + break; + case 0x4817: + setDoDeltaX(param.asInteger()); + sub41C7B0(); + break; + case 0x481B: + if (param.asPoint().y != 0) { + sub41CC40(param.asPoint().y, param.asPoint().x); + } else { + sub41CCE0(param.asPoint().x); + } + break; + case 0x4827: + GotoState(&Klayman::sub420E20); + break; + case 0x483F: + sub41CD00(param.asInteger()); + break; + case 0x4840: + sub41CD70(param.asInteger()); + break; + } + return 0; +} + +// KmScene1404 + +KmScene1404::KmScene1404(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y) + : Klayman(vm, parentScene, x, y, 1000, 1000) { + + // Empty +} + +uint32 KmScene1404::xHandleMessage(int messageNum, const MessageParam ¶m) { + switch (messageNum) { + case 0x4001: + case 0x4800: + sub41C930(param.asPoint().x, false); + break; + case 0x4004: + GotoState(&Klayman::stTryStandIdle); + break; + case 0x480A: + if (param.asInteger() == 1) { + GotoState(&Klayman::stMoveObjectSkipTurnFaceObject); + } else { + GotoState(&Klayman::stMoveObjectFaceObject); + } + break; + case 0x4812: + if (param.asInteger() == 2) { + GotoState(&Klayman::stPickUpNeedle); + } else if (param.asInteger() == 1) { + GotoState(&Klayman::sub41FFF0); + } else { + GotoState(&Klayman::stPickUpGeneric); + } + break; + case 0x4817: + setDoDeltaX(param.asInteger()); + sub41C7B0(); + break; + case 0x481A: + GotoState(&Klayman::sub420680); + break; + case 0x481B: + if (param.asPoint().y != 0) { + sub41CC40(param.asPoint().y, param.asPoint().x); + } else { + sub41CCE0(param.asPoint().x); + } + break; + case 0x481D: + GotoState(&Klayman::stTurnToUse); + break; + case 0x481E: + GotoState(&Klayman::sub4207F0); + break; + case 0x481F: + if (param.asInteger() == 1) { + GotoState(&Klayman::stWonderAboutAfter); + } else if (param.asInteger() == 0) { + GotoState(&Klayman::stWonderAboutHalf); + } else if (param.asInteger() == 4) { + GotoState(&Klayman::stTurnAwayFromUse); + } else if (param.asInteger() == 3) { + GotoState(&Klayman::stTurnToUseHalf); + } else { + GotoState(&Klayman::stWonderAbout); + } + break; + case 0x482D: + setDoDeltaX(_x > (int16)param.asInteger() ? 1 : 0); + sub41C7B0(); + break; + case 0x483F: + sub41CD00(param.asInteger()); + break; + case 0x4840: + sub41CD70(param.asInteger()); + break; + } + return 0; +} + +KmScene1608::KmScene1608(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y) + : Klayman(vm, parentScene, x, y, 1000, 1000), _flag1(false) { +} + +uint32 KmScene1608::xHandleMessage(int messageNum, const MessageParam ¶m) { + switch (messageNum) { + case 0x2032: + _flag1 = param.asInteger() != 0; + break; + case 0x4001: + case 0x4800: + sub41C930(param.asPoint().x, false); + break; + case 0x4004: + if (_flag1) + GotoState(&Klayman::sub421350); + else + GotoState(&Klayman::stTryStandIdle); + break; + case 0x4812: + if (param.asInteger() == 2) { + GotoState(&Klayman::stPickUpNeedle); + } else if (param.asInteger() == 1) { + GotoState(&Klayman::sub41FFF0); + } else { + GotoState(&Klayman::stPickUpGeneric); + } + break; + case 0x4817: + setDoDeltaX(param.asInteger()); + sub41C7B0(); + break; + case 0x481B: + if (param.asPoint().y != 0) { + sub41CC40(param.asPoint().y, param.asPoint().x); + } else { + sub41CCE0(param.asPoint().x); + } + break; + case 0x481D: + if (_flag1) + GotoState(&Klayman::sub4214D0); + break; + case 0x481E: + if (_flag) + GotoState(&Klayman::sub421510); + break; + case 0x481F: + if (param.asInteger() == 1) { + GotoState(&Klayman::stWonderAboutAfter); + } else if (param.asInteger() == 0) { + GotoState(&Klayman::stWonderAboutHalf); + } else if (param.asInteger() == 4) { + GotoState(&Klayman::stTurnAwayFromUse); + } else if (param.asInteger() == 3) { + GotoState(&Klayman::stTurnToUseHalf); + } else { + GotoState(&Klayman::stWonderAbout); + } + break; + case 0x482D: + setDoDeltaX(_x > (int16)param.asInteger() ? 1 : 0); + sub41C7B0(); + break; + case 0x4834: + GotoState(&Klayman::stStepOver); + break; + case 0x4835: + sendMessage(_parentScene, 0x2032, 1); + _flag1 = true; + GotoState(&Klayman::stSitInTeleporter); + break; + case 0x4836: + sendMessage(_parentScene, 0x2032, 0); + _flag1 = false; + GotoState(&Klayman::sub421310); + break; + case 0x483F: + sub41CD00(param.asInteger()); + break; + case 0x4840: + sub41CD70(param.asInteger()); + break; + } + return 0; +} + +// KmScene1705 + +KmScene1705::KmScene1705(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y) + : Klayman(vm, parentScene, x, y, 1000, 1000), _flag(false) { + + // Empty +} + +uint32 KmScene1705::xHandleMessage(int messageNum, const MessageParam ¶m) { + uint32 messageResult = 0; + switch (messageNum) { + case 0x2000: + _flag = param.asInteger() != 0; + messageResult = 1; + break; + case 0x4001: + case 0x4800: + sub41C930(param.asPoint().x, false); + break; + case 0x4004: + if (_flag) { + GotoState(&Klayman::sub421350); + } else { + GotoState(&Klayman::stTryStandIdle); + } + break; + case 0x4803: + GotoState(&KmScene1705::stFallSkipJump); + break; + case 0x4812: + if (param.asInteger() == 2) { + GotoState(&Klayman::stPickUpNeedle); + } else if (param.asInteger() == 1) { + GotoState(&Klayman::sub41FFF0); + } else { + GotoState(&Klayman::stPickUpGeneric); + } + break; + case 0x4817: + setDoDeltaX(param.asInteger()); + sub41C7B0(); + break; + case 0x481B: + if (param.asPoint().y != 0) { + sub41CC40(param.asPoint().y, param.asPoint().x); + } else { + sub41CCE0(param.asPoint().x); + } + break; + case 0x481D: + if (_flag) { + GotoState(&Klayman::sub4214D0); + } + break; + case 0x481E: + if (_flag) { + GotoState(&Klayman::sub421510); + } + break; + case 0x481F: + if (param.asInteger() == 1) { + GotoState(&Klayman::stWonderAboutAfter); + } else if (param.asInteger() == 0) { + GotoState(&Klayman::stWonderAboutHalf); + } else if (param.asInteger() == 4) { + GotoState(&Klayman::stTurnAwayFromUse); + } else if (param.asInteger() == 3) { + GotoState(&Klayman::stTurnToUseHalf); + } else { + GotoState(&Klayman::stWonderAbout); + } + break; + case 0x4834: + GotoState(&Klayman::stStepOver); + break; + case 0x4835: + sendMessage(_parentScene, 0x2000, 1); + _flag = true; + GotoState(&Klayman::stSitInTeleporter); + break; + case 0x4836: + sendMessage(_parentScene, 0x2000, 0); + _flag = false; + GotoState(&Klayman::sub421310); + break; + case 0x483D: + sub468AD0(); + break; + case 0x483E: + sub468B10(); + break; + } + return messageResult; +} + +uint32 KmScene1705::handleMessage4689A0(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = handleMessage41D480(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x4E0A2C24) { + _soundResource1.play(0x85B10BB8); + } else if (param.asInteger() == 0x4E6A0CA0) { + _soundResource1.play(0xC5B709B0); + } + break; + } + return messageResult; +} + +void KmScene1705::spriteUpdate468A30() { + updateDeltaXY(); + HitRect *hitRect = _vm->_collisionMan->findHitRectAtPos(_x, _y + 10); + if (hitRect->type == 0x5001) { + _y = hitRect->rect.y1; + processDelta(); + sendMessage(this, 0x1019, 0); + } +} + +void KmScene1705::stFallSkipJump() { + _status2 = 2; + _flagE5 = false; + setFileHash2(0xB93AB151, 0x40A100F8, 0); + SetUpdateHandler(&Klayman::update); + SetSpriteCallback(&KmScene1705::spriteUpdate468A30); + SetMessageHandler(&Klayman::handleMessage41D360); + NextState(&Klayman::stLandOnFeet); +} + +void KmScene1705::sub468AD0() { + _status2 = 0; + _flagE5 = false; + setFileHash(0x5E0A4905, 0, -1); + SetUpdateHandler(&Klayman::update); + SetSpriteCallback(NULL); + SetMessageHandler(&KmScene1705::handleMessage4689A0); +} + +void KmScene1705::sub468B10() { + _status2 = 0; + _flagE5 = false; + setFileHash(0xD86E4477, 0, -1); + SetUpdateHandler(&Klayman::update); + SetSpriteCallback(NULL); + SetMessageHandler(&KmScene1705::handleMessage4689A0); +} + +KmScene1901::KmScene1901(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y) + : Klayman(vm, parentScene, x, y, 1000, 1000) { + + // Empty +} + +uint32 KmScene1901::xHandleMessage(int messageNum, const MessageParam ¶m) { + switch (messageNum) { + case 0x4001: + case 0x4800: + sub41C930(param.asPoint().x, false); + break; + case 0x4004: + GotoState(&Klayman::stTryStandIdle); + break; + case 0x4817: + setDoDeltaX(param.asInteger()); + sub41C7B0(); + break; + case 0x481D: + GotoState(&Klayman::stTurnToUse); + break; + case 0x481E: + GotoState(&Klayman::sub4207F0); + break; + case 0x482D: + setDoDeltaX(_x > (int16)param.asInteger() ? 1 : 0); + sub41C7B0(); + break; + case 0x483F: + sub41CD00(param.asInteger()); + break; + case 0x4840: + sub41CD70(param.asInteger()); + break; + } + return 0; +} + +KmScene2001::KmScene2001(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y) + : Klayman(vm, parentScene, x, y, 1000, 1000), _flag(false) { + + // Empty +} + +uint32 KmScene2001::xHandleMessage(int messageNum, const MessageParam ¶m) { + switch (messageNum) { + case 0x2000: + _flag = param.asInteger() != 0; + break; + case 0x4001: + case 0x4800: + sub41C930(param.asPoint().x, false); + break; + case 0x4004: + if (_flag) { + GotoState(&Klayman::sub421350); + } else { + GotoState(&Klayman::stTryStandIdle); + } + break; + case 0x4804: + if (param.asInteger() != 0) { + _x4 = param.asInteger(); + GotoState(&Klayman::stWalking); + } else { + GotoState(&Klayman::stPeekWall); + } + break; + case 0x4817: + setDoDeltaX(param.asInteger()); + sub41C7B0(); + break; + case 0x481D: + if (_flag) { + GotoState(&Klayman::sub4214D0); + } + break; + case 0x481E: + if (_flag) { + GotoState(&Klayman::sub421510); + } + break; + case 0x4834: + GotoState(&Klayman::stStepOver); + break; + case 0x4835: + sendMessage(_parentScene, 0x2000, 1); + _flag = true; + GotoState(&Klayman::stSitInTeleporter); + break; + case 0x4836: + sendMessage(_parentScene, 0x2000, 0); + _flag = false; + GotoState(&Klayman::sub421310); + break; + case 0x483D: + sub440230(); + break; + case 0x483E: + stDoTeleport(); + break; + } + return 0; +} + +uint32 KmScene2001::handleMessage4401A0(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = handleMessage41D480(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x4E0A2C24) { + _soundResource1.play(0x85B10BB8); + } if (param.asInteger() == 0x4E6A0CA0) { + _soundResource1.play(0xC5B709B0); + } + break; + } + return messageResult; +} + +void KmScene2001::sub440230() { + _status2 = 0; + _flagE5 = false; + setFileHash(0xBE68CC54, 0, -1); + SetUpdateHandler(&Klayman::update); + SetSpriteCallback(NULL); + SetMessageHandler(&KmScene2001::handleMessage4401A0); +} + +void KmScene2001::stDoTeleport() { + _status2 = 0; + _flagE5 = false; + setFileHash(0x18AB4ED4, 0, -1); + SetUpdateHandler(&Klayman::update); + SetSpriteCallback(NULL); + SetMessageHandler(&KmScene2001::handleMessage4401A0); +} + +KmScene2101::KmScene2101(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y) + : Klayman(vm, parentScene, x, y, 1000, 1000), _flag1(false) { + + // Empty +} + +uint32 KmScene2101::xHandleMessage(int messageNum, const MessageParam ¶m) { + uint32 messageResult = 0; + switch (messageNum) { + case 0x2000: + _flag1 = param.asInteger() != 0; + messageResult = 1; + break; + case 0x4001: + case 0x4800: + sub41C930(param.asPoint().x, false); + break; + case 0x4004: + if (_flag1) + GotoState(&Klayman::sub421350); + else + GotoState(&Klayman::stTryStandIdle); + break; + case 0x4811: + GotoState(&KmScene2101::sub4862C0); + break; + case 0x4812: + if (param.asInteger() == 2) { + GotoState(&Klayman::stPickUpNeedle); + } else if (param.asInteger() == 1) { + GotoState(&Klayman::sub41FFF0); + } else { + GotoState(&Klayman::stPickUpGeneric); + } + break; + case 0x4816: + if (param.asInteger() == 1) { + GotoState(&Klayman::stTurnPressButton); + } else if (param.asInteger() == 2) { + GotoState(&Klayman::stStampFloorButton); + } else { + GotoState(&Klayman::stPressButtonSide); + } + break; + case 0x4817: + setDoDeltaX(param.asInteger()); + sub41C7B0(); + break; + case 0x481B: + if (param.asPoint().y != 0) { + sub41CC40(param.asPoint().y, param.asPoint().x); + } else { + sub41CCE0(param.asPoint().x); + } + break; + case 0x481D: + if (_flag1) + GotoState(&Klayman::sub4214D0); + break; + case 0x481E: + if (_flag) + GotoState(&Klayman::sub421510); + break; + case 0x4834: + GotoState(&Klayman::stStepOver); + break; + case 0x4835: + sendMessage(_parentScene, 0x2000, 1); + _flag1 = true; + GotoState(&Klayman::stSitInTeleporter); + break; + case 0x4836: + sendMessage(_parentScene, 0x2000, 0); + _flag1 = false; + GotoState(&Klayman::sub421310); + break; + case 0x483D: + sub486320(); + break; + case 0x483E: + sub486360(); + break; + } + return messageResult; +} + +uint32 KmScene2101::handleMessage486160(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = handleMessage41D480(messageNum, param, sender); + int16 speedUpFrameIndex; + switch (messageNum) { + case 0x1008: + speedUpFrameIndex = getFrameIndex(kKlaymanSpeedUpHash); + if (_frameIndex < speedUpFrameIndex) { + setFileHash(0x35AA8059, speedUpFrameIndex, -1); + _y = 438; + } + messageResult = 0; + break; + case 0x100D: + if (param.asInteger() == 0x1A1A0785) { + _soundResource1.play(0x40F0A342); + } else if (param.asInteger() == 0x60428026) { + _soundResource1.play(0x40608A59); + } + break; + } + return messageResult; +} + +uint32 KmScene2101::handleMessage486230(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = handleMessage41D480(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x4E0A2C24) { + _soundResource1.play(0x85B10BB8); + } else if (param.asInteger() == 0x4E6A0CA0) { + _soundResource1.play(0xC5B709B0); + } + break; + } + return messageResult; +} + +void KmScene2101::sub4862C0() { + _status2 = 1; + _flagE5 = false; + setFileHash(0x35AA8059, 0, -1); + SetUpdateHandler(&Klayman::update); + SetSpriteCallback(&AnimatedSprite::updateDeltaXY); + SetMessageHandler(&KmScene2101::handleMessage486160); + _soundResource1.play(0x402E82D4); +} + +void KmScene2101::sub486320() { + _status2 = 0; + _flagE5 = false; + setFileHash(0xFF290E30, 0, -1); + SetUpdateHandler(&Klayman::update); + SetSpriteCallback(NULL); + SetMessageHandler(&KmScene2101::handleMessage486230); +} + +void KmScene2101::sub486360() { + _status2 = 0; + _flagE5 = false; + setFileHash(0x9A28CA1C, 0, -1); + SetUpdateHandler(&Klayman::update); + SetSpriteCallback(NULL); + SetMessageHandler(&KmScene2101::handleMessage486230); +} + +KmScene2201::KmScene2201(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y, NRect *clipRects, int clipRectsCount) + // TODO: NRect *rect1, int16 unk in Klayman ctor + : Klayman(vm, parentScene, x, y, 1000, 1000) { + + _dataResource.load(0x04104242); + _flagF6 = false; +} + +uint32 KmScene2201::xHandleMessage(int messageNum, const MessageParam ¶m) { + switch (messageNum) { + case 0x4001: + case 0x4800: + sub41C930(param.asPoint().x, false); + break; + case 0x4004: + GotoState(&Klayman::stTryStandIdle); + break; + case 0x4812: + GotoState(&Klayman::stPickUpGeneric); + break; + case 0x4816: + if (param.asInteger() == 0) { + GotoState(&Klayman::stPressButtonSide); + } + break; + case 0x4817: + setDoDeltaX(param.asInteger() ? 1 : 0); + sub41C7B0(); + break; + case 0x4818: + sub41C930(_dataResource.getPoint(param.asInteger()).x, false); + break; + case 0x481B: + if (param.asPoint().y != 0) { + sub41CC40(param.asPoint().y, param.asPoint().x); + } else { + sub41CCE0(param.asPoint().x); + } + break; + case 0x481D: + GotoState(&Klayman::stTurnToUse); + break; + case 0x481E: + GotoState(&Klayman::sub4207F0); + break; + case 0x482D: + setDoDeltaX(_x > (int16)param.asInteger() ? 1 : 0); + sub41C7B0(); + break; + case 0x482E: + if (param.asInteger() == 1) { + GotoState(&Klayman::sub421030); + } else { + GotoState(&Klayman::sub420FE0); + } + break; + case 0x482F: + if (param.asInteger() == 1) { + GotoState(&Klayman::sub4210C0); + } else { + GotoState(&Klayman::sub421070); + } + break; + case 0x483F: + sub41CD00(param.asInteger()); + break; + case 0x4840: + sub41CD70(param.asInteger()); + break; + } + return 0; +} + +KmScene2203::KmScene2203(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y) + : Klayman(vm, parentScene, x, y, 1000, 1000) { + // Empty +} + +uint32 KmScene2203::xHandleMessage(int messageNum, const MessageParam ¶m) { + switch (messageNum) { + case 0x4001: + case 0x4800: + sub41C930(param.asPoint().x, false); + break; + case 0x4004: + GotoState(&Klayman::stTryStandIdle); + break; + case 0x4812: + if (param.asInteger() == 2) { + GotoState(&Klayman::stPickUpNeedle); + } else if (param.asInteger() == 1) { + GotoState(&Klayman::sub41FFF0); + } else { + GotoState(&Klayman::stPickUpGeneric); + } + break; + case 0x4816: + if (param.asInteger() == 1) { + GotoState(&Klayman::stTurnPressButton); + } else if (param.asInteger() == 2) { + GotoState(&Klayman::stStampFloorButton); + } else { + GotoState(&Klayman::stPressButtonSide); + } + break; + case 0x4817: + setDoDeltaX(param.asInteger()); + sub41C7B0(); + break; + case 0x4818: + sub41C930(_dataResource.getPoint(param.asInteger()).x, false); + break; + case 0x4819: + GotoState(&Klayman::sub420750); + break; + case 0x481A: + GotoState(&Klayman::sub420680); + break; + case 0x481B: + if (param.asPoint().y != 0) { + sub41CC40(param.asPoint().y, param.asPoint().x); + } else { + sub41CCE0(param.asPoint().x); + } + break; + case 0x481D: + GotoState(&Klayman::stTurnToUse); + break; + case 0x481E: + GotoState(&Klayman::sub4207F0); + break; + case 0x482D: + setDoDeltaX(_x > (int16)param.asInteger() ? 1 : 0); + sub41C7B0(); + break; + case 0x483F: + sub41CD00(param.asInteger()); + break; + case 0x4840: + sub41CD70(param.asInteger()); + break; + } + return 0; +} + +KmScene2205::KmScene2205(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y) + : Klayman(vm, parentScene, x, y, 1000, 1000) { + // Empty +} + +void KmScene2205::xUpdate() { + setGlobalVar(0x18288913, _frameIndex); +} + +uint32 KmScene2205::xHandleMessage(int messageNum, const MessageParam ¶m) { + switch (messageNum) { + case 0x4001: + case 0x4800: + sub41C930(param.asPoint().x, false); + break; + case 0x4004: + GotoState(&Klayman::stTryStandIdle); + break; + case 0x4804: + if (param.asInteger() != 0) { + _x4 = param.asInteger(); + GotoState(&KmScene2205::sub423980); + } else { + GotoState(&Klayman::stPeekWall); + } + break; + case 0x4816: + if (param.asInteger() == 0) { + GotoState(&Klayman::stPressButtonSide); + } + break; + case 0x4817: + setDoDeltaX(param.asInteger()); + sub41C7B0(); + break; + case 0x4818: + sub41C930(_dataResource.getPoint(param.asInteger()).x, false); + break; + case 0x483F: + sub41CD00(param.asInteger()); + break; + case 0x4840: + sub41CD70(param.asInteger()); + break; + } + return 0; +} + +void KmScene2205::sub423980() { + int16 frameIndex = getGlobalVar(0x18288913); + if (frameIndex < 0 || frameIndex > 13) + frameIndex = 0; + _status2 = 0; + _flagE1 = true; + _flagE5 = true; + setFileHash(0x1A249001, frameIndex, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::handleMessage41EB70); + SetSpriteCallback(&Klayman::spriteUpdate41F300); + NextState(&Klayman::sub41FA40); + FinalizeState(&Klayman::stStartWalkingDone); +} + +KmScene2206::KmScene2206(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y) + : Klayman(vm, parentScene, x, y, 1000, 1000) { + + // TODO Sound1ChList_addSoundResource(0x80101800, 0xD3B02847); +} + +KmScene2206::~KmScene2206() { + // TODO Sound1ChList_sub_407AF0(0x80101800); +} + +void KmScene2206::xUpdate() { + setGlobalVar(0x18288913, _frameIndex); +} + +uint32 KmScene2206::xHandleMessage(int messageNum, const MessageParam ¶m) { + switch (messageNum) { + case 0x4001: + case 0x4800: + sub41C930(param.asPoint().x, false); + break; + case 0x4004: + GotoState(&Klayman::stTryStandIdle); + break; + case 0x4803: + GotoState(&KmScene2206::sub482490); + break; + case 0x4804: + if (param.asInteger() != 0) { + _x4 = param.asInteger(); + GotoState(&KmScene2206::sub482530); + } else { + GotoState(&Klayman::stPeekWall); + } + break; + case 0x4812: + if (param.asInteger() == 1) { + GotoState(&Klayman::sub41FFF0); + } else { + GotoState(&Klayman::stPickUpGeneric); + } + break; + case 0x4816: + if (param.asInteger() == 1) { + GotoState(&Klayman::stTurnPressButton); + } else if (param.asInteger() == 2) { + GotoState(&Klayman::stStampFloorButton); + } else { + GotoState(&Klayman::stPressButtonSide); + } + break; + case 0x4817: + setDoDeltaX(param.asInteger()); + sub41C7B0(); + break; + case 0x481B: + if (param.asPoint().y != 0) { + sub41CC40(param.asPoint().y, param.asPoint().x); + } else { + sub41CCE0(param.asPoint().x); + } + break; + case 0x481F: + if (param.asInteger() == 0) { + GotoState(&Klayman::stWonderAboutHalf); + } else if (param.asInteger() == 1) { + GotoState(&Klayman::stWonderAboutAfter); + } else if (param.asInteger() == 3) { + GotoState(&Klayman::stTurnToUseHalf); + } else if (param.asInteger() == 4) { + GotoState(&Klayman::stTurnAwayFromUse); + } else { + GotoState(&Klayman::stWonderAbout); + } + break; + case 0x482D: + setDoDeltaX(_x > (int16)param.asInteger() ? 1 : 0); + sub41C7B0(); + break; + case 0x482E: + if (param.asInteger() == 1) { + GotoState(&Klayman::sub421030); + } else { + GotoState(&Klayman::sub420FE0); + } + break; + case 0x482F: + if (param.asInteger() == 1) { + GotoState(&Klayman::sub4210C0); + } else { + GotoState(&Klayman::sub421070); + } + break; + case 0x4837: + sub41CE70(); + break; + case 0x483F: + sub41CD00(param.asInteger()); + break; + case 0x4840: + sub41CD70(param.asInteger()); + break; + } + return 0; +} + +void KmScene2206::spriteUpdate482450() { + _yDelta++; + _y += _yDelta; + if (_y > 600) { + sendMessage(this, 0x1019, 0); + } +} + +void KmScene2206::sub482490() { + if (!stStartActionFromIdle(AnimationCallback(&KmScene2206::sub482490))) { + _status2 = 1; + sendMessage(_parentScene, 0x4803, 0); + _flagE5 = false; + _yDelta = 0; + setFileHash(0x5420E254, 0, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::handleMessage41D360); + SetSpriteCallback(&KmScene2206::spriteUpdate482450); + // TODO Sound1ChList_playLooping(0xD3B02847); + } +} + +void KmScene2206::sub482530() { + int16 frameIndex = getGlobalVar(0x18288913) + 1; + if (frameIndex < 0 || frameIndex > 13) + frameIndex = 0; + _status2 = 0; + _flagE1 = true; + _flagE5 = true; + setFileHash(0x1A249001, frameIndex, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::handleMessage41EB70); + SetSpriteCallback(&Klayman::spriteUpdate41F300); + NextState(&Klayman::sub41FA40); + FinalizeState(&Klayman::stStartWalkingDone); +} + +KmScene2207::KmScene2207(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y) + : Klayman(vm, parentScene, x, y, 1000, 1000) { + // Empty +} + +uint32 KmScene2207::xHandleMessage(int messageNum, const MessageParam ¶m) { + switch (messageNum) { + case 0x2001: + GotoState(&KmScene2207::sub442460); + break; + case 0x2005: + spriteUpdate442430(); + GotoState(&KmScene2207::stTryStandIdle); + break; + case 0x4001: + case 0x4800: + sub41C930(param.asPoint().x, false); + break; + case 0x4004: + GotoState(&Klayman::stTryStandIdle); + break; + case 0x480D: + GotoState(&KmScene2207::sub4424B0); + break; + case 0x4812: + GotoState(&Klayman::stPickUpGeneric); + break; + case 0x4816: + if (param.asInteger() == 1) { + GotoState(&Klayman::stTurnPressButton); + } else if (param.asInteger() == 2) { + GotoState(&Klayman::stStampFloorButton); + } else { + GotoState(&Klayman::stPressButtonSide); + } + break; + case 0x4817: + setDoDeltaX(param.asInteger()); + sub41C7B0(); + break; + case 0x481B: + if (param.asPoint().y != 0) { + sub41CC40(param.asPoint().y, param.asPoint().x); + } else { + sub41CCE0(param.asPoint().x); + } + break; + case 0x4827: + GotoState(&Klayman::sub420E20); + break; + case 0x482D: + setDoDeltaX(_x > (int16)param.asInteger() ? 1 : 0); + sub41C7B0(); + break; + case 0x483F: + sub41CD00(param.asInteger()); + break; + case 0x4840: + sub41CD70(param.asInteger()); + break; + } + return 0; +} + +void KmScene2207::spriteUpdate442430() { + _x = _attachedSprite->getX() - 20; + _y = _attachedSprite->getY() + 46; + processDelta(); +} + +void KmScene2207::sub442460() { + if (!stStartActionFromIdle(AnimationCallback(&KmScene2207::sub442460))) { + _status2 = 1; + _flagE5 = true; + setFileHash(0x5420E254, 0, -1); + SetUpdateHandler(&Klayman::update); + SetSpriteCallback(&KmScene2207::spriteUpdate442430); + SetMessageHandler(&Klayman::handleMessage41D360); + } +} + +void KmScene2207::sub4424B0() { + if (!stStartAction(AnimationCallback(&KmScene2207::sub4424B0))) { + _status2 = 0; + if (_flagF7) { + stReleaseLeverUp(); + } else { + _flagE5 = false; + setFileHash(0x0C303040, 0, -1); + SetUpdateHandler(&Klayman::update); + SetSpriteCallback(&KmScene2207::spriteUpdate41F230); + SetMessageHandler(&Klayman::handleMessage41E210); + NextState(&KmScene2207::sub442520); + } + } +} + +void KmScene2207::sub442520() { + setFileHash(0x0D318140, 0, -1); + sendMessage(_attachedSprite, 0x480F, 0); + NextState(&KmScene2207::sub442560); +} + +void KmScene2207::sub442560() { + setFileHash(0x1564A2C0, 0, -1); + SetUpdateHandler(&Klayman::update); + SetSpriteCallback(&Klayman::spriteUpdate41F230); + NextState(&KmScene2207::sub4425A0); + _flagE5 = true; + _flagF7 = true; +} + +void KmScene2207::sub4425A0() { + setFileHash(0x4464A440, 0, -1); + SetUpdateHandler(&Klayman::update); + SetSpriteCallback(&Klayman::spriteUpdate41F230); + SetMessageHandler(&Klayman::handleMessage41D360); + _flagE5 = true; + _flagF7 = true; +} + +KmScene2242::KmScene2242(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y) + : Klayman(vm, parentScene, x, y, 1000, 1000) { + // Empty +} + +void KmScene2242::xUpdate() { + setGlobalVar(0x18288913, _frameIndex); +} + +uint32 KmScene2242::xHandleMessage(int messageNum, const MessageParam ¶m) { + switch (messageNum) { + case 0x4001: + case 0x4800: + sub41C930(param.asPoint().x, false); + break; + case 0x4004: + GotoState(&Klayman::stTryStandIdle); + break; + case 0x4804: + if (param.asInteger() != 0) { + _x4 = param.asInteger(); + GotoState(&KmScene2242::sub444D20); + } else { + GotoState(&Klayman::stPeekWall); + } + break; + case 0x4812: + if (param.asInteger() == 2) { + GotoState(&Klayman::stPickUpNeedle); + } else if (param.asInteger() == 1) { + GotoState(&Klayman::sub41FFF0); + } else { + GotoState(&Klayman::stPickUpGeneric); + } + break; + case 0x4817: + setDoDeltaX(param.asInteger()); + sub41C7B0(); + break; + case 0x481B: + if (param.asPoint().y != 0) { + sub41CC40(param.asPoint().y, param.asPoint().x); + } else { + sub41CCE0(param.asPoint().x); + } + break; + case 0x481F: + if (param.asInteger() == 0) { + GotoState(&Klayman::stWonderAboutHalf); + } else if (param.asInteger() == 1) { + GotoState(&Klayman::stWonderAboutAfter); + } else if (param.asInteger() == 3) { + GotoState(&Klayman::stTurnToUseHalf); + } else if (param.asInteger() == 4) { + GotoState(&Klayman::stTurnAwayFromUse); + } else { + GotoState(&Klayman::stWonderAbout); + } + break; + case 0x482D: + setDoDeltaX(_x > (int16)param.asInteger() ? 1 : 0); + sub41C7B0(); + break; + case 0x4837: + sub41CE70(); + break; + } + return 0; +} + +void KmScene2242::sub444D20() { + int16 frameIndex = (int16)getGlobalVar(0x18288913); + if (frameIndex < 0 || frameIndex > 13) + frameIndex = 0; + _status2 = 0; + _flagE1 = true; + _flagE5 = true; + setFileHash(0x1A249001, frameIndex, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::handleMessage41EB70); + SetSpriteCallback(&Klayman::spriteUpdate41F300); + NextState(&Klayman::sub41FA40); + FinalizeState(&Klayman::stStartWalkingDone); +} + +KmHallOfRecords::KmHallOfRecords(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y) + : Klayman(vm, parentScene, x, y, 1000, 1000) { + // Empty +} + +void KmHallOfRecords::xUpdate() { + setGlobalVar(0x18288913, _frameIndex); +} + +uint32 KmHallOfRecords::xHandleMessage(int messageNum, const MessageParam ¶m) { + switch (messageNum) { + case 0x4001: + case 0x4800: + sub41C930(param.asPoint().x, false); + break; + case 0x4004: + GotoState(&Klayman::stTryStandIdle); + break; + case 0x4804: + if (param.asInteger() != 0) { + _x4 = param.asInteger(); + GotoState(&KmHallOfRecords::sub43B130); + } else { + GotoState(&Klayman::stPeekWall); + } + break; + case 0x4817: + setDoDeltaX(param.asInteger()); + sub41C7B0(); + break; + case 0x481F: + if (param.asInteger() == 0) { + GotoState(&Klayman::stWonderAboutHalf); + } else if (param.asInteger() == 1) { + GotoState(&Klayman::stWonderAboutAfter); + } else if (param.asInteger() == 3) { + GotoState(&Klayman::stTurnToUseHalf); + } else if (param.asInteger() == 4) { + GotoState(&Klayman::stTurnAwayFromUse); + } else { + GotoState(&Klayman::stWonderAbout); + } + break; + case 0x482D: + setDoDeltaX(_x > (int16)param.asInteger() ? 1 : 0); + sub41C7B0(); + break; + case 0x4837: + sub41CE70(); + break; + } + return 0; +} + +void KmHallOfRecords::sub43B130() { + int16 frameIndex = (int16)getGlobalVar(0x18288913); + if (frameIndex < 0 || frameIndex > 13) + frameIndex = 0; + _status2 = 0; + _flagE1 = true; + _flagE5 = true; + setFileHash(0x1A249001, frameIndex, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::handleMessage41EB70); + SetSpriteCallback(&Klayman::spriteUpdate41F300); + NextState(&Klayman::sub41FA40); + FinalizeState(&Klayman::stStartWalkingDone); +} + +KmScene2247::KmScene2247(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y) + : Klayman(vm, parentScene, x, y, 1000, 1000) { + // Empty +} + +void KmScene2247::xUpdate() { + setGlobalVar(0x18288913, _frameIndex); +} + +uint32 KmScene2247::xHandleMessage(int messageNum, const MessageParam ¶m) { + switch (messageNum) { + case 0x4001: + case 0x4800: + sub41C930(param.asPoint().x, false); + break; + case 0x4004: + GotoState(&Klayman::stTryStandIdle); + break; + case 0x4804: + if (param.asInteger() != 0) { + _x4 = param.asInteger(); + GotoState(&KmScene2247::sub453520); + } else { + GotoState(&Klayman::stPeekWall); + } + break; + case 0x4817: + setDoDeltaX(param.asInteger()); + sub41C7B0(); + break; + case 0x481F: + if (param.asInteger() == 0) { + GotoState(&Klayman::stWonderAboutHalf); + } else if (param.asInteger() == 1) { + GotoState(&Klayman::stWonderAboutAfter); + } else if (param.asInteger() == 3) { + GotoState(&Klayman::stTurnToUseHalf); + } else if (param.asInteger() == 4) { + GotoState(&Klayman::stTurnAwayFromUse); + } else { + GotoState(&Klayman::stWonderAbout); + } + break; + case 0x482D: + setDoDeltaX(_x > (int16)param.asInteger() ? 1 : 0); + sub41C7B0(); + break; + case 0x4837: + sub41CE70(); + break; + } + return 0; +} + +void KmScene2247::sub453520() { + int16 frameIndex = (int16)getGlobalVar(0x18288913); + if (frameIndex < 0 || frameIndex > 13) + frameIndex = 0; + _status2 = 0; + _flagE1 = true; + _flagE5 = true; + setFileHash(0x1A249001, frameIndex, -1); + SetUpdateHandler(&Klayman::update); + SetMessageHandler(&Klayman::handleMessage41EB70); + SetSpriteCallback(&Klayman::spriteUpdate41F300); + NextState(&Klayman::sub41FA40); + FinalizeState(&Klayman::stStartWalkingDone); +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/klayman.h b/engines/neverhood/klayman.h new file mode 100644 index 0000000000..645ed74226 --- /dev/null +++ b/engines/neverhood/klayman.h @@ -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 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef NEVERHOOD_KLAYMAN_H +#define NEVERHOOD_KLAYMAN_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 + +class Klayman; + +const uint32 kKlaymanSpeedUpHash = 0x004A2148; + +struct KlaymanTableItem { + int value; + void (Klayman::*callback)(); +}; + +class Klayman : public AnimatedSprite { +public: + Klayman(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y, int surfacePriority = 1000, int objectPriority = 1000, NRectArray *clipRects = NULL); + + void update(); + + void stDoIdlePickEar(); + void sub41FDA0(); + void sub41FDF0(); + void stDoIdleChest(); + void sub41FEB0(); + void stTryStandIdle(); + void stWakeUp(); + void stSleeping(); + void stPickUpGeneric(); + void stTurnPressButton(); + void stStampFloorButton(); + void stPressButtonSide(); + void stLargeStep(); + void stWonderAboutHalf(); + void stWonderAboutAfter(); + void stTurnToUseHalf(); + void stTurnAwayFromUse(); + void stWonderAbout(); + void stPeekWall(); + void sub420210(); + void sub4201C0(); + void sub420340(); + void sub420250(); + void sub420290(); + void sub420380(); + void sub4203C0(); + void sub420300(); + void sub420970(); + void sub4209D0(); + void sub420BC0(); + void sub420AD0(); + void sub421030(); + void sub420FE0(); + void sub4210C0(); + void sub421070(); + void stLandOnFeet(); + void sub420ED0(); + void sub420750(); + void stTurnToUse(); + void sub4207F0(); + void sub420F20(); + void sub421350(); + void stIdleSitBlink(); + void stIdleSitBlinkSecond(); + void stPickUpNeedle(); + void sub41FFF0(); + void sub4214D0(); + void sub421510(); + void stStepOver(); + void stSitInTeleporter(); + void sub421310(); + void stMoveObjectSkipTurnFaceObject(); + void sub420660(); + void stMoveObjectSkipTurn(); + void stMoveObjectFaceObject(); + void sub420C50(); + void stPullLeverDown(); + void stHoldLeverDown(); + void stReleaseLeverUp(); + void sub420E20(); + void sub420E90(); + void sub420EB0(); + void sub420680(); + void stWalking(); + void sub41FA40(); + void stStartWalkingDone(); + + void sub41CE70(); + + void spriteUpdate41F250(); + void spriteUpdate41F5F0(); + void suLargeStep(); + void spriteUpdate41F230(); + void spriteUpdate41F5A0(); + void spriteUpdate41F300(); + void spriteUpdate41F320(); + + uint32 handleMessage41D360(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 handleMessage41D480(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 handleMessage41EB70(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 handleMessage41E210(int messageNum, const MessageParam ¶m, Entity *sender); + + void setKlaymanTable(const KlaymanTableItem *table, int tableCount); + void setKlaymanTable1(); + void setKlaymanTable2(); + void setKlaymanTable3(); + + void setSoundFlag(bool value) { _soundFlag = value; } + +protected: + Entity *_parentScene; + Sprite *_attachedSprite; + int _statusE0; + bool _flagE1; + bool _flagE2; + bool _flagE3; + bool _flagE4; + bool _flagE5; + int16 _x4, _y4; + int16 _counter, _counterMax; + int16 _counter3, _counter3Max; + int16 _counter1; + int16 _counter2; + bool _flagF6; + bool _flagF7; + bool _flagF8; + int _status2; + bool _flagFA; + SoundResource _soundResource1; + SoundResource _soundResource2; + int _status3; + const KlaymanTableItem *_table; + int _tableCount; + int _tableMaxValue; + uint32 _field114; + /* + 00000118 field118 dw ? + */ + bool _soundFlag; + int _resourceHandle; + virtual void xUpdate(); + virtual uint32 xHandleMessage(int messageNum, const MessageParam ¶m); + + void stIdlePickEar(); + void evIdlePickEarDone(); + uint32 hmIdlePickEar(int messageNum, const MessageParam ¶m, Entity *sender); + + void sub41FDB0(); + uint32 handleMessage41E980(int messageNum, const MessageParam ¶m, Entity *sender); + + void sub41FE00(); + void sub41FE50(); + uint32 handleMessage41E9E0(int messageNum, const MessageParam ¶m, Entity *sender); + + void stIdleChest(); + uint32 hmIdleChest(int messageNum, const MessageParam ¶m, Entity *sender); + + void sub41FEC0(); + uint32 handleMessage41EFE0(int messageNum, const MessageParam ¶m, Entity *sender); + + void sub41D320(uint32 fileHash, AnimationCb callback); + void update41D2B0(); + + bool stStartActionFromIdle(AnimationCb callback); + void sub41C7B0(); + void sub41C770(); + void sub41C790(); + + void update41D0F0(); + + void stStand(); + void stStandAround(); + + uint32 handleMessage41F140(int messageNum, const MessageParam ¶m, Entity *sender); + + void sub41C930(int16 x, bool flag); + + uint32 hmSleeping(int messageNum, const MessageParam ¶m, Entity *sender); + + bool stStartAction(AnimationCb callback3); + + void stSneak(); + void stSneakDone(); + uint32 handleMessage41DD80(int messageNum, const MessageParam ¶m, Entity *sender); + void sub41CD70(int16 x); + void stStartWalking(); + uint32 handleMessage41EC70(int messageNum, const MessageParam ¶m, Entity *sender); + + uint32 hmPickUpGeneric(int messageNum, const MessageParam ¶m, Entity *sender); + + uint32 hmPressButton(int messageNum, const MessageParam ¶m, Entity *sender); + + void sub41CD00(int16 x); + void sub41CC40(int16 x1, int16 x2); + void sub41CAC0(int16 x); + void sub41CCE0(int16 x); + void stLargeStepDone(); + + uint32 hmLargeStep(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 handleMessage41EEF0(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 hmPeekWall(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 handleMessage41D790(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 handleMessage41D880(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 handleMessage41DAA0(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 handleMessage41DFD0(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 handleMessage41E0D0(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 handleMessage41E490(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 handleMessage41E290(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 handleMessage41E2F0(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 handleMessage41D640(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 handleMessage41EAB0(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 handleMessage41D970(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 handleMessage41DD20(int messageNum, const MessageParam ¶m, Entity *sender); + + void update41D1C0(); + + uint32 handleMessage41DB90(int messageNum, const MessageParam ¶m, Entity *sender); + +}; + +class KmScene1001 : public Klayman { +public: + KmScene1001(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y); +protected: + uint32 xHandleMessage(int messageNum, const MessageParam ¶m); + void sub44FA50(); + uint32 handleMessage44FA00(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class KmScene1002 : public Klayman { +public: + KmScene1002(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y, Sprite *class599, Sprite *ssLadderArch); +protected: + Sprite *_class599; + Sprite *_ssLadderArch; + Sprite *_otherSprite; + int _status; + void xUpdate(); + uint32 xHandleMessage(int messageNum, const MessageParam ¶m); + void update4497D0(); + uint32 handleMessage449800(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 handleMessage4498E0(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 hmPressDoorButton(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 hmMoveVenusFlyTrap(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 handleMessage449BA0(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 handleMessage449C90(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 handleMessage449D60(int messageNum, const MessageParam ¶m, Entity *sender); + void suFallDown(); + void sub449E20(); + void sub449E90(); + void sub449EF0(); + void sub449F70(); + void stSpitOutFall(); + void sub44A0D0(); + void sub44A150(); + void sub44A230(); + void stJumpAndFall(); + void stDropFromRing(); + void stPressDoorButton(); + void stHitByBoxingGlove(); + void stHitByBoxingGloveDone(); + void stMoveVenusFlyTrap(); + void stContinueMovingVenusFlyTrap(); + void stMoveVenusFlyTrapDone(); +}; + +class KmScene1004 : public Klayman { +public: + KmScene1004(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y); +protected: + uint32 xHandleMessage(int messageNum, const MessageParam ¶m); + uint32 hmReadNote(int messageNum, const MessageParam ¶m, Entity *sender); + void stReadNote(); +}; + +class KmScene1109 : public Klayman { +public: + KmScene1109(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y); +protected: + bool _flag1; + uint32 xHandleMessage(int messageNum, const MessageParam ¶m); + uint32 handleMessage461EA0(int messageNum, const MessageParam ¶m, Entity *sender); + void sub461F30(); + void sub461F70(); +}; + +class KmScene1201 : public Klayman { +public: + KmScene1201(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y, Entity *class464); +protected: + Entity *_class464; + int _countdown; + uint32 xHandleMessage(int messageNum, const MessageParam ¶m); + void update40DBE0(); + uint32 hmMatch(int messageNum, const MessageParam ¶m, Entity *sender); + void stFetchMatch(); + void stLightMatch(); + uint32 handleMessage40DDF0(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 hmTumbleHeadless(int messageNum, const MessageParam ¶m, Entity *sender); + void sub40DF00(); + void stMoveObjectSkipTurn(); + void stTumbleHeadless(); + void sub40E040(); +}; + +class KmScene1303 : public Klayman { +public: + KmScene1303(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y); +protected: + uint32 xHandleMessage(int messageNum, const MessageParam ¶m); + uint32 hmPeekWallReturn(int messageNum, const MessageParam ¶m, Entity *sender); + void update4161A0(); + void stPeekWall1(); + void stPeekWall2(); + void stPeekWall3(); + void stPeekWallReturn(); +}; + +class KmScene1304 : public Klayman { +public: + KmScene1304(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y); +protected: + uint32 xHandleMessage(int messageNum, const MessageParam ¶m); +}; + +class KmScene1305 : public Klayman { +public: + KmScene1305(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y); +protected: + uint32 xHandleMessage(int messageNum, const MessageParam ¶m); + void stCrashDown(); + void cbCrashDownEvent(); +}; + +class KmScene1306 : public Klayman { +public: + KmScene1306(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y); +protected: + bool _flag1; + uint32 xHandleMessage(int messageNum, const MessageParam ¶m); + uint32 handleMessage417CB0(int messageNum, const MessageParam ¶m, Entity *sender); + void sub417D40(); + void sub417D80(); +}; + +class KmScene1308 : public Klayman { +public: + KmScene1308(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y); +protected: + bool _flag1; + uint32 xHandleMessage(int messageNum, const MessageParam ¶m); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void sub456150(); +}; + +class KmScene1401 : public Klayman { +public: + KmScene1401(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y); +protected: + uint32 xHandleMessage(int messageNum, const MessageParam ¶m); +}; + +class KmScene1402 : public Klayman { +public: + KmScene1402(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y); +protected: + uint32 xHandleMessage(int messageNum, const MessageParam ¶m); +}; + +class KmScene1403 : public Klayman { +public: + KmScene1403(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y); +protected: + uint32 xHandleMessage(int messageNum, const MessageParam ¶m); +}; + +class KmScene1404 : public Klayman { +public: + KmScene1404(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y); +protected: + uint32 xHandleMessage(int messageNum, const MessageParam ¶m); +}; + +class KmScene1608 : public Klayman { +public: + KmScene1608(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y); +protected: + bool _flag1; + uint32 xHandleMessage(int messageNum, const MessageParam ¶m); +}; + +class KmScene1705 : public Klayman { +public: + KmScene1705(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y); +protected: + bool _flag; + uint32 xHandleMessage(int messageNum, const MessageParam ¶m); + uint32 handleMessage4689A0(int messageNum, const MessageParam ¶m, Entity *sender); + void spriteUpdate468A30(); + void stFallSkipJump(); + void sub468AD0(); + void sub468B10(); +}; + +class KmScene1901 : public Klayman { +public: + KmScene1901(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y); +protected: + uint32 xHandleMessage(int messageNum, const MessageParam ¶m); +}; + +class KmScene2001 : public Klayman { +public: + KmScene2001(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y); +protected: + bool _flag; + uint32 xHandleMessage(int messageNum, const MessageParam ¶m); + uint32 handleMessage4401A0(int messageNum, const MessageParam ¶m, Entity *sender); + void sub440230(); + void stDoTeleport(); +}; + +class KmScene2101 : public Klayman { +public: + KmScene2101(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y); +protected: + bool _flag1; + uint32 xHandleMessage(int messageNum, const MessageParam ¶m); + uint32 handleMessage486160(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 handleMessage486230(int messageNum, const MessageParam ¶m, Entity *sender); + void sub4862C0(); + void sub486320(); + void sub486360(); +}; + +class KmScene2201 : public Klayman { +public: + KmScene2201(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y, NRect *clipRects, int clipRectsCount); +protected: + uint32 xHandleMessage(int messageNum, const MessageParam ¶m); +}; + +class KmScene2203 : public Klayman { +public: + KmScene2203(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y); +protected: + uint32 xHandleMessage(int messageNum, const MessageParam ¶m); +}; + +class KmScene2205 : public Klayman { +public: + KmScene2205(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y); + void sub423980(); +protected: + void xUpdate(); + uint32 xHandleMessage(int messageNum, const MessageParam ¶m); +}; + +class KmScene2206 : public Klayman { +public: + KmScene2206(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y); + ~KmScene2206(); +protected: + int16 _yDelta; + void xUpdate(); + uint32 xHandleMessage(int messageNum, const MessageParam ¶m); + void spriteUpdate482450(); + void sub482490(); + void sub482530(); +}; + +class KmScene2207 : public Klayman { +public: + KmScene2207(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y); +protected: + uint32 xHandleMessage(int messageNum, const MessageParam ¶m); + void spriteUpdate442430(); + void sub442460(); + void sub4424B0(); + void sub442520(); + void sub442560(); + void sub4425A0(); +}; + +class KmScene2242 : public Klayman { +public: + KmScene2242(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y); +protected: + void xUpdate(); + uint32 xHandleMessage(int messageNum, const MessageParam ¶m); + void sub444D20(); +}; + +class KmHallOfRecords : public Klayman { +public: + KmHallOfRecords(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y); +protected: + void xUpdate(); + uint32 xHandleMessage(int messageNum, const MessageParam ¶m); + void sub43B130(); +}; + +class KmScene2247 : public Klayman { +public: + KmScene2247(NeverhoodEngine *vm, Entity *parentScene, int16 x, int16 y); +protected: + void xUpdate(); + uint32 xHandleMessage(int messageNum, const MessageParam ¶m); + void sub453520(); +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_KLAYMAN_H */ diff --git a/engines/neverhood/module.cpp b/engines/neverhood/module.cpp new file mode 100644 index 0000000000..5be9a6321f --- /dev/null +++ b/engines/neverhood/module.cpp @@ -0,0 +1,117 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "neverhood/module.h" +#include "neverhood/navigationscene.h" +#include "neverhood/smackerscene.h" + +namespace Neverhood { + +Module::Module(NeverhoodEngine *vm, Module *parentModule) + : Entity(vm, 0), _parentModule(parentModule), _childObject(NULL), + _done(false), _sceneType(kSceneTypeNormal) { + + SetMessageHandler(&Module::handleMessage); + +} + +Module::~Module() { + delete _childObject; +} + +void Module::draw() { + if (_childObject) + _childObject->draw(); +} + +uint32 Module::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + switch (messageNum) { + case 0x0008: + if (_parentModule) + sendMessage(_parentModule, 8, 0); + return 0; + case 0x1009: + _moduleResult = param.asInteger(); + _done = true; + return 0; + case 0x100A: + case 0x1023: + case 0x1024: + // Unused resource preloading messages + return 0; + default: + if (_childObject && sender == _parentModule) + return sender->sendMessage(_childObject, messageNum, param); + } + return 0; +} + +NavigationScene *Module::navigationScene() { + // Not so nice + 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 flag1, bool canAbort) { + SmackerScene *smackerScene; + _sceneType = kSceneTypeSmacker; + smackerScene = new SmackerScene(_vm, this, doubleSurface, flag1, canAbort); + smackerScene->setFileHash(fileHash); + smackerScene->nextVideo(); + _childObject = smackerScene; +} + +void Module::createSmackerScene(const uint32 *fileHashList, bool doubleSurface, bool flag1, bool canAbort) { + SmackerScene *smackerScene; + _sceneType = kSceneTypeSmacker; + smackerScene = new SmackerScene(_vm, this, doubleSurface, flag1, canAbort); + smackerScene->setFileHashList(fileHashList); + smackerScene->nextVideo(); + _childObject = smackerScene; +} + +bool Module::updateChild() { + if (_childObject) { + _childObject->handleUpdate(); + if (_done) { + _done = false; + // Save the last area type if it's a NavigationScene for further processing + if (_sceneType == kSceneTypeNavigation) + _navigationAreaType = navigationScene()->getNavigationAreaType(); + delete _childObject; + _childObject = NULL; + _sceneType = kSceneTypeNormal; + return false; + } + } + return true; +} + +void Module::leaveModule(uint32 result) { + sendMessage(_parentModule, 0x1009, result); +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/module.h b/engines/neverhood/module.h new file mode 100644 index 0000000000..57a42c623b --- /dev/null +++ b/engines/neverhood/module.h @@ -0,0 +1,70 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +// TODO: I couldn't come up with a better name than 'Module' so far + +#ifndef NEVERHOOD_MODULE_H +#define NEVERHOOD_MODULE_H + +#include "neverhood/neverhood.h" +#include "neverhood/background.h" +#include "neverhood/collisionman.h" +#include "neverhood/entity.h" +#include "neverhood/graphics.h" +#include "neverhood/mouse.h" +#include "neverhood/palette.h" +#include "neverhood/screen.h" + +namespace Neverhood { + +class NavigationScene; + +enum SceneType { + kSceneTypeNormal, + kSceneTypeSmacker, + kSceneTypeNavigation +}; + +class Module : public Entity { +public: + Module(NeverhoodEngine *vm, Module *parentModule); + virtual ~Module(); + virtual void draw(); +protected: + Module *_parentModule; + Entity *_childObject; + bool _done; + uint32 _moduleResult; + SceneType _sceneType; + int _navigationAreaType; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + NavigationScene *navigationScene(); + void createNavigationScene(uint32 navigationListId, int navigationIndex, const byte *itemsTypes = NULL); + void createSmackerScene(uint32 fileHash, bool doubleSurface, bool flag1, bool canAbort); + void createSmackerScene(const uint32 *fileHashList, bool doubleSurface, bool flag1, bool canAbort); + bool updateChild(); + void leaveModule(uint32 result); +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_MODULE_H */ diff --git a/engines/neverhood/module.mk b/engines/neverhood/module.mk new file mode 100644 index 0000000000..adf58b1225 --- /dev/null +++ b/engines/neverhood/module.mk @@ -0,0 +1,50 @@ +MODULE := engines/neverhood + +MODULE_OBJS = \ + background.o \ + blbarchive.o \ + collisionman.o \ + detection.o \ + diskplayerscene.o \ + gamemodule.o \ + gamevars.o \ + graphics.o \ + klayman.o \ + module.o \ + module1000.o \ + module1100.o \ + module1200.o \ + module1300.o \ + module1400.o \ + module1500.o \ + module1600.o \ + module1700.o \ + module1800.o \ + module1900.o \ + module2000.o \ + module2100.o \ + module2200.o \ + module2300.o \ + module2600.o \ + module2700.o \ + module3000.o \ + mouse.o \ + navigationscene.o \ + neverhood.o \ + palette.o \ + resource.o \ + resourceman.o \ + scene.o \ + screen.o \ + smackerscene.o \ + smackerplayer.o \ + sprite.o \ + staticdata.o + +# This module can be built as a plugin +ifdef BUILD_PLUGINS +PLUGIN := 1 +endif + +# Include common rules +include $(srcdir)/rules.mk diff --git a/engines/neverhood/module1000.cpp b/engines/neverhood/module1000.cpp new file mode 100644 index 0000000000..b60c3182d0 --- /dev/null +++ b/engines/neverhood/module1000.cpp @@ -0,0 +1,1898 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "neverhood/module1000.h" + +namespace Neverhood { + +Module1000::Module1000(NeverhoodEngine *vm, Module *parentModule, int which) + : Module(vm, parentModule) { + + debug("Create Module1000(%d)", which); + + _musicFileHash = getGlobalVar(0xD0A14D10) ? 0x81106480 : 0x00103144; + + // TODO Music18hList_add(0x03294419, 0x061880C6); + // TODO Music18hList_add(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() { + // TODO Music18hList_deleteGroup(0x03294419); +} + +void Module1000::createScene(int sceneNum, int which) { + debug("Module1000::createScene(%d, %d)", sceneNum, which); + _vm->gameState().sceneNum = sceneNum; + switch (_vm->gameState().sceneNum) { + case 0: + // TODO Music18hList_play(0x061880C6, 0, 0, 1); + _childObject = new Scene1001(_vm, this, which); + break; + case 1: + // TODO Music18hList_play(0x061880C6, 0, 0, 1); + _childObject = new Scene1002(_vm, this, which); + break; + case 2: + // TODO Music18hList_play(0x061880C6, 0, 0); + _childObject = new Class152(_vm, this, 0xC084110C, 0x41108C00); + break; + case 3: + // TODO Music18hList_stop(0x061880C6, 0, 2); + _childObject = new Scene1004(_vm, this, which); + break; + case 4: + // TODO Music18hList_stop(0x061880C6, 0, 0); + // TODO Music18hList_play(_musicFileHash, 0, 0, 1); + _childObject = new Scene1005(_vm, this, which); + break; + } + SetUpdateHandler(&Module1000::updateScene); + _childObject->handleUpdate(); +} + +void Module1000::updateScene() { + if (!updateChild()) { + switch (_vm->gameState().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) + 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: + // TODO Music18hList_stop(_musicFileHash, 0, 1); + createScene(3, 1); + break; + } + } +} + +// Scene1001 + +AsScene1001Door::AsScene1001Door(NeverhoodEngine *vm) + : AnimatedSprite(vm, 1100), _soundResource1(vm), _soundResource2(vm) { + + createSurface(800, 137, 242); + _x = 726; + _y = 440; + callback1(); + _soundResource2.load(0xED403E03); + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene1001Door::handleMessage); +} + +uint32 AsScene1001Door::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2000: + handleMessage2000h(); + break; + case 0x3002: + removeCallbacks(); + break; + } + return 0; +} + +void AsScene1001Door::handleMessage2000h() { + switch (getGlobalVar(0x52371C95)) { + case 0: + case 1: + _soundResource1.play(0x65482F03); + setFileHash(0x624C0498, 1, 3); + NextState(&AsScene1001Door::callback1); + break; + case 2: + _soundResource2.play(); + setFileHash(0x624C0498, 6, 6); + NextState(&AsScene1001Door::callback2); + break; + default: + // Nothing + break; + } + incGlobalVar(0x52371C95, 1); +} + +void AsScene1001Door::callback1() { + switch (getGlobalVar(0x52371C95)) { + case 1: + setFileHash(0x624C0498, 4, -1); + _newHashListIndex = 4; + break; + case 2: + setFileHash(0x624C0498, 1, -1); + _newHashListIndex = 1; + break; + case 3: + stopAnimation(); + setVisible(false); + break; + default: + setFileHash(0x624C0498, 0, -1); + _newHashListIndex = 0; + break; + } +} + +void AsScene1001Door::callback2() { + setGlobalVar(0xD217189D, 1); + setFileHash(0x624C0498, 6, 6); + NextState(&AsScene1001Door::callback3); + _x = 30; +} + +void AsScene1001Door::callback3() { + _soundResource1.play(); + stopAnimation(); + setVisible(false); +} + +AsScene1001Hammer::AsScene1001Hammer(NeverhoodEngine *vm, Sprite *asDoor) + : AnimatedSprite(vm, 1100), _soundResource(vm), _asDoor(asDoor) { + + _x = 547; + _y = 206; + createSurface(900, 177, 192); + setFileHash(0x022C90D4, -1, -1); + _newHashListIndex = -2; + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene1001Hammer::handleMessage); +} + +uint32 AsScene1001Hammer::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x00352100) { + sendMessage(_asDoor, 0x2000, 0); + } else if (param.asInteger() == 0x0A1A0109) { + _soundResource.play(0x66410886); + } + break; + case 0x2000: + setFileHash(0x022C90D4, 1, -1); + _soundResource.play(0xE741020A); + _newHashListIndex = -2; + break; + } + return 0; +} + +AsScene1001Window::AsScene1001Window(NeverhoodEngine *vm) + : AnimatedSprite(vm, 1200), _soundResource(vm) { + + _x = 320; + _y = 240; + createSurface(100, 66, 129); + setFileHash(0xC68C2299, 0, -1); + _newHashListIndex = 0; + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene1001Window::handleMessage); +} + +uint32 AsScene1001Window::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x0E0A1410) { + _soundResource.play(0x60803F10); + } + break; + case 0x2001: + setFileHash(0xC68C2299, 0, -1); + break; + case 0x3002: + SetMessageHandler(NULL); + setGlobalVar(0x03C698DA, 1); + setVisible(false); + break; + } + return 0; +} + +AsScene1001Lever::AsScene1001Lever(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y, int deltaXType) + : AnimatedSprite(vm, 1100), _soundResource(vm), _parentScene(parentScene) { + + createSurface(1010, 71, 73); + setDoDeltaX(deltaXType); + setFileHash(0x04A98C36, 0, -1); + _newHashListIndex = 0; + _x = x; + _y = y; + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene1001Lever::handleMessage); +} + +uint32 AsScene1001Lever::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x00C0C444) { + sendMessage(_parentScene, 0x480F, 0); + } else if (param.asInteger() == 0xC41A02C0) { + _soundResource.play(0x40581882); + } + break; + case 0x1011: + sendMessage(_parentScene, 0x4826, 0); + messageResult = 1; + break; + case 0x3002: + setFileHash(0x04A98C36, 0, -1); + _newHashListIndex = 0; + break; + case 0x480F: + setFileHash(0x04A98C36, 0, -1); + break; + case 0x482A: + sendMessage(_parentScene, 0x1022, 990); + break; + case 0x482B: + sendMessage(_parentScene, 0x1022, 1010); + break; + } + return messageResult; +} + +SsCommonButtonSprite::SsCommonButtonSprite(NeverhoodEngine *vm, Scene *parentScene, uint32 fileHash, int surfacePriority, uint32 soundFileHash) + : StaticSprite(vm, fileHash, surfacePriority), _parentScene(parentScene), _soundResource(vm), _countdown(0) { + + _priority = 1100; + _soundFileHash = soundFileHash ? soundFileHash : 0x44141000; + setVisible(false); + SetUpdateHandler(&SsCommonButtonSprite::update); + SetMessageHandler(&SsCommonButtonSprite::handleMessage); +} + +void SsCommonButtonSprite::update() { + if (_countdown != 0 && (--_countdown) == 0) { + setVisible(false); + } +} + +uint32 SsCommonButtonSprite::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x480B: + sendMessage(_parentScene, 0x480B, 0); + setVisible(true); + _countdown = 8; + _soundResource.play(_soundFileHash); + break; + } + return messageResult; +} + +Scene1001::Scene1001(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule, true), _fieldE4(-1), _fieldE6(-1) { + + _name = "Scene1001"; + + Sprite *tempSprite; + + SetMessageHandler(&Scene1001::handleMessage); + + setHitRects(0x004B4860); + _surfaceFlag = false; + setBackground(0x4086520E); + setPalette(0x4086520E); + insertMouse433(0x6520A400); + + if (which < 0) { + setRectList(0x004B49F0); + insertKlayman<KmScene1001>(200, 433); + setMessageList(0x004B4888); + } else if (which == 1) { + setRectList(0x004B49F0); + insertKlayman<KmScene1001>(640, 433); + setMessageList(0x004B4898); + } else if (which == 2) { + setRectList(0x004B49F0); + if (getGlobalVar(0xC0418A02)) { + insertKlayman<KmScene1001>(390, 433); + _klayman->setDoDeltaX(1); + } else { + insertKlayman<KmScene1001>(300, 433); + } + setMessageList(0x004B4970); + } else { + setRectList(0x004B4A00); + insertKlayman<KmScene1001>(200, 433); + setMessageList(0x004B4890); + } + + tempSprite = insertStaticSprite(0x2080A3A8, 1300); + + _klayman->setClipRect(0, 0, tempSprite->getDrawRect().x2(), 480); + + if (getGlobalVar(0xD217189D) == 0) { + _asDoor = insertSprite<AsScene1001Door>(); + _asDoor->setClipRect(0, 0, tempSprite->getDrawRect().x2(), 480); + } else { + _asDoor = NULL; + } + + _asLever = insertSprite<AsScene1001Lever>(this, 150, 433, 1); + + insertStaticSprite(0x809861A6, 950); + insertStaticSprite(0x89C03848, 1100); + + _ssButton = insertSprite<SsCommonButtonSprite>(this, 0x15288120, 100, 0); + + if (getGlobalVar(0x03C698DA) == 0) { + tempSprite = insertStaticSprite(0x8C066150, 200); + _asWindow = insertSprite<AsScene1001Window>(); + _asWindow->setClipRect(tempSprite->getDrawRect()); + } else { + _asWindow = NULL; + } + + _asHammer = insertSprite<AsScene1001Hammer>(_asDoor); + +} + +Scene1001::~Scene1001() { + setGlobalVar(0xC0418A02, _klayman->isDoDeltaX()); +} + +uint32 Scene1001::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + debug("Scene1001::handleMessage(%04X)", messageNum); + uint32 messageResult = 0; + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x0001: + if (param.asPoint().x == 0 && getGlobalVar(0xA4014072)) { + leaveScene(0); + } + break; + case 0x000D: + if (param.asInteger() == 0x188B2105) { + leaveScene(0); + messageResult = 1; + } + break; + case 0x100D: + if (param.asInteger() == 0x00342624) { + sendEntityMessage(_klayman, 0x1014, _asLever); + setMessageList2(0x004B4910); + messageResult = 1; + } else if (param.asInteger() == 0x21E64A00) { + if (getGlobalVar(0xD217189D)) { + setMessageList(0x004B48A8); + } else { + setMessageList(0x004B48C8); + } + messageResult = 1; + } else if (param.asInteger() == 0x040424D0) { + sendEntityMessage(_klayman, 0x1014, _ssButton); + } else if (param.asInteger() == 0x80006358) { + if (getGlobalVar(0x03C698DA)) { + setMessageList(0x004B4938); + } else { + setMessageList(0x004B4960); + } + } + break; + case 0x2002: + setRectList(0x004B49F0); + break; + case 0x480B: + if (_asWindow) { + sendMessage(_asWindow, 0x2001, 0); + } + break; + case 0x480F: + if (_asHammer) { + sendMessage(_asHammer, 0x2000, 0); + } + break; + } + return messageResult; +} + +// Scene1002 + +SsScene1002LadderArch::SsScene1002LadderArch(NeverhoodEngine *vm, Scene *parentScene) + : StaticSprite(vm, 0x152C1313, 1015), _parentScene(parentScene) { + + SetMessageHandler(&SsScene1002LadderArch::handleMessage); +} + +uint32 SsScene1002LadderArch::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x482A: + sendMessage(_parentScene, 0x1022, 995); + break; + case 0x482B: + sendMessage(_parentScene, 0x1022, 1015); + break; + } + return messageResult; +} + +Class599::Class599(NeverhoodEngine *vm, Scene *parentScene) + : StaticSprite(vm, 0x316C4BB4, 1015), _parentScene(parentScene) { + + SetMessageHandler(&Class599::handleMessage); +} + +uint32 Class599::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x482A: + sendMessage(_parentScene, 0x1022, 995); + break; + case 0x482B: + sendMessage(_parentScene, 0x1022, 1015); + break; + } + return messageResult; +} + +AsScene1002Ring::AsScene1002Ring(NeverhoodEngine *vm, Scene *parentScene, bool flag1, int16 x, int16 y, int16 clipY1, bool flag2) + : AnimatedSprite(vm, 1100), _soundResource(vm), _parentScene(parentScene), _flag1(flag1) { + + SetUpdateHandler(&AsScene1002Ring::update); + + if (flag1) { + createSurface(990, 68, 314); + if (flag2) { + setFileHash(0x04103090, 0, -1); + SetMessageHandler(&AsScene1002Ring::handleMessage447930); + } else { + setFileHash(0xA85C4011, _vm->_rnd->getRandomNumber(15), -1); + SetMessageHandler(&AsScene1002Ring::handleMessage4475E0); + } + } else { + createSurface(990, 68, 138); + setFileHash(0xA85C4011, _vm->_rnd->getRandomNumber(15), -1); + SetMessageHandler(&AsScene1002Ring::handleMessage4475E0); + } + + setClipRect(0, clipY1, 640, 480); + + _x = x; + _y = y; + + setDoDeltaX(_vm->_rnd->getRandomNumber(1)); + +} + +void AsScene1002Ring::update() { + AnimatedSprite::updateAnim(); + AnimatedSprite::updatePosition(); +} + +uint32 AsScene1002Ring::handleMessage4475E0(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x4806: + setDoDeltaX(((Sprite*)sender)->isDoDeltaX() ? 1 : 0); + sendMessage(_parentScene, 0x4806, 0); + SetMessageHandler(&AsScene1002Ring::handleMessage447760); + if (_flag1) { + setFileHash(0x87502558, 0, -1); + } else { + setFileHash(0x80DD4010, 0, -1); + } + break; + case 0x480F: + setDoDeltaX(((Sprite*)sender)->isDoDeltaX() ? 1 : 0); + sendMessage(_parentScene, 0x480F, 0); + SetMessageHandler(&AsScene1002Ring::handleMessage447890); + setFileHash(0x861A2020, 0, -1); + break; + case 0x482A: + sendMessage(_parentScene, 0x1022, 990); + break; + case 0x482B: + sendMessage(_parentScene, 0x1022, 1010); + break; + } + return messageResult; +} + +uint32 AsScene1002Ring::handleMessage447760(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + if (_flag1) { + setFileHash(0x78D0A812, 0, -1); + SetMessageHandler(&AsScene1002Ring::handleMessage447930); + } else { + setFileHash(0xB85D2A10, 0, -1); + SetMessageHandler(&AsScene1002Ring::handleMessage447930); + } + break; + case 0x4807: + sendMessage(_parentScene, 0x4807, 0); + setDoDeltaX(_vm->_rnd->getRandomNumber(1)); + setFileHash(0x8258A030, 0, -1); + SetMessageHandler(&AsScene1002Ring::handleMessage447A00); + break; + case 0x482A: + sendMessage(_parentScene, 0x1022, 990); + break; + case 0x482B: + sendMessage(_parentScene, 0x1022, 1010); + break; + } + return messageResult; +} + +uint32 AsScene1002Ring::handleMessage447890(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + setFileHash(0x04103090, 0, -1); + SetMessageHandler(&AsScene1002Ring::handleMessage447930); + break; + case 0x482A: + sendMessage(_parentScene, 0x1022, 990); + break; + case 0x482B: + sendMessage(_parentScene, 0x1022, 1010); + break; + } + return messageResult; +} + +uint32 AsScene1002Ring::handleMessage447930(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x4807: + sendMessage(_parentScene, 0x4807, 0); + setDoDeltaX(_vm->_rnd->getRandomNumber(1)); + setFileHash(0x8258A030, 0, -1); + SetMessageHandler(&AsScene1002Ring::handleMessage447A00); + break; + case 0x482A: + sendMessage(_parentScene, 0x1022, 990); + break; + case 0x482B: + sendMessage(_parentScene, 0x1022, 1010); + break; + } + return messageResult; +} + +uint32 AsScene1002Ring::handleMessage447A00(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = handleMessage4475E0(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x05410F72) { + _soundResource.play(0x21EE40A9); + } + break; + case 0x3002: + setFileHash(0xA85C4011, 0, -1); + break; + case 0x482A: + sendMessage(_parentScene, 0x1022, 990); + break; + case 0x482B: + sendMessage(_parentScene, 0x1022, 1010); + break; + } + return messageResult; +} + +AsScene1002Door::AsScene1002Door(NeverhoodEngine *vm, NRect &clipRect) + : StaticSprite(vm, 1200) { + + _spriteResource.load2(0x1052370F); + createSurface(800, _spriteResource.getDimensions().width, _spriteResource.getDimensions().height); + setClipRect(clipRect); + + _x = 526; + + if (getGlobalVar(0x8306F218)) { + _y = 49; + } else { + _y = 239; + } + + _surface->getDrawRect().x = 0; + _surface->getDrawRect().y = 0; + _surface->getDrawRect().width = _spriteResource.getDimensions().width; + _surface->getDrawRect().height = _spriteResource.getDimensions().height; + + _needRefresh = true; + + SetUpdateHandler(&AsScene1002Door::update); + SetMessageHandler(&AsScene1002Door::handleMessage); + SetSpriteCallback(NULL); + StaticSprite::update(); + +} + +void AsScene1002Door::update() { + handleSpriteUpdate(); + StaticSprite::update(); +} + +uint32 AsScene1002Door::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x4808: + setGlobalVar(0x8306F218, 1); + SetSpriteCallback(&AsScene1002Door::suOpenDoor); + break; + case 0x4809: + setGlobalVar(0x8306F218, 0); + SetSpriteCallback(&AsScene1002Door::suCloseDoor); + break; + } + return messageResult; +} + +void AsScene1002Door::suOpenDoor() { + if (_y > 49) { + _y -= 8; + if (_y < 49) { + SetSpriteCallback(NULL); + _y = 49; + } + _needRefresh = true; + } +} + +void AsScene1002Door::suCloseDoor() { + if (_y < 239) { + _y += 8; + if (_y > 239) { + SetSpriteCallback(NULL); + _y = 239; + } + _needRefresh = true; + } +} + +Class505::Class505(NeverhoodEngine *vm) + : AnimatedSprite(vm, 1400) { + + createSurface(1025, 88, 165); + setVisible(false); + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&Class505::handleMessage); +} + +uint32 Class505::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2004: + _x = ((Sprite*)sender)->getX() - 98; + _y = ((Sprite*)sender)->getY() - 111; + setFileHash(0x0422255A, 0, -1); + setVisible(true); + break; + case 0x3002: + stopAnimation(); + setVisible(false); + break; + } + return messageResult; +} + +AsScene1002DoorSpy::AsScene1002DoorSpy(NeverhoodEngine *vm, NRect &clipRect, Scene *parentScene, Sprite *asDoor, Sprite *class505) + : AnimatedSprite(vm, 1300), _rect(clipRect), _parentScene(parentScene), _asDoor(asDoor), _class505(class505), + _soundResource(vm) { + + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene1002DoorSpy::handleMessage4489D0); + SetSpriteCallback(&AsScene1002DoorSpy::spriteUpdate448AA0); + createSurface(800, 136, 147); + setClipRect(clipRect); + spriteUpdate448AA0(); + _soundResource.load(0xC0C40298); + setFileHash(0x586C1D48, 0, 0); +} + +uint32 AsScene1002DoorSpy::handleMessage4489D0(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0xA61CA1C2) { + sendMessage(_class505, 0x2004, 0); + } else if (param.asInteger() == 0x14CE0620) { + _soundResource.play(); + } + break; + case 0x2003: + sub448B10(); + break; + } + return messageResult; +} + +uint32 AsScene1002DoorSpy::handleMessage448A60(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = handleMessage4489D0(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + removeCallbacks(); + break; + } + return messageResult; +} + +void AsScene1002DoorSpy::spriteUpdate448AA0() { + _x = _asDoor->getX() + 34; + _y = _asDoor->getY() + 175; +} + +void AsScene1002DoorSpy::sub448AC0() { + setClipRect(_rect); + _parentScene->setSurfacePriority(getSurface(), 800); + setFileHash(0x586C1D48, 0, 0); + SetMessageHandler(&AsScene1002DoorSpy::handleMessage4489D0); +} + +void AsScene1002DoorSpy::sub448B10() { + setClipRect(0, 0, 640, 480); + _parentScene->setSurfacePriority(getSurface(), 1200); + setFileHash(0x586C1D48, 1, -1); + SetMessageHandler(&AsScene1002DoorSpy::handleMessage448A60); + NextState(&AsScene1002DoorSpy::sub448AC0); +} + +Class426::Class426(NeverhoodEngine *vm, Scene *parentScene, uint32 fileHash1, uint32 fileHash2, int surfacePriority, uint32 soundFileHash) + : StaticSprite(vm, 1100), _parentScene(parentScene), _soundResource(vm), _status(0) { + + _soundFileHash = soundFileHash != 0 ? soundFileHash : 0x44141000; + + _fileHashes[0] = fileHash1; + _fileHashes[1] = fileHash2; + + _spriteResource.load2(fileHash1); + createSurface(surfacePriority, 40, 40); + + _surface->getDrawRect().x = 0; + _surface->getDrawRect().y = 0; + _surface->getDrawRect().width = _spriteResource.getDimensions().width; + _surface->getDrawRect().height = _spriteResource.getDimensions().height; + _x = _spriteResource.getPosition().x; + _y = _spriteResource.getPosition().y; + + setVisible(false); + _needRefresh = true; + + SetUpdateHandler(&Class426::update); + SetMessageHandler(&Class426::handleMessage); + +} + +void Class426::setFileHashes(uint32 fileHash1, uint32 fileHash2) { + _fileHashes[0] = fileHash1; + _fileHashes[1] = fileHash2; + if (_status == 2) { + _spriteResource.load2(fileHash2); + _surface->getDrawRect().x = 0; + _surface->getDrawRect().y = 0; + _surface->getDrawRect().width = _spriteResource.getDimensions().width; + _surface->getDrawRect().height = _spriteResource.getDimensions().height; + _x = _spriteResource.getPosition().x; + _y = _spriteResource.getPosition().y; + _needRefresh = true; + StaticSprite::update(); + } else { + _spriteResource.load2(fileHash1); + _surface->getDrawRect().x = 0; + _surface->getDrawRect().y = 0; + _surface->getDrawRect().width = _spriteResource.getDimensions().width; + _surface->getDrawRect().height = _spriteResource.getDimensions().height; + _x = _spriteResource.getPosition().x; + _y = _spriteResource.getPosition().y; + _needRefresh = true; + StaticSprite::update(); + } +} + +void Class426::update() { + if (_countdown != 0 && (--_countdown) == 0) { + if (_status == 1) { + _status = 2; + _spriteResource.load2(_fileHashes[1]); + _surface->getDrawRect().x = 0; + _surface->getDrawRect().y = 0; + _surface->getDrawRect().width = _spriteResource.getDimensions().width; + _surface->getDrawRect().height = _spriteResource.getDimensions().height; + _x = _spriteResource.getPosition().x; + _y = _spriteResource.getPosition().y; + _needRefresh = true; + StaticSprite::update(); + _countdown = 4; + } else if (_status == 2) { + _status = 3; + _spriteResource.load2(_fileHashes[0]); + _surface->getDrawRect().x = 0; + _surface->getDrawRect().y = 0; + _surface->getDrawRect().width = _spriteResource.getDimensions().width; + _surface->getDrawRect().height = _spriteResource.getDimensions().height; + _x = _spriteResource.getPosition().x; + _y = _spriteResource.getPosition().y; + _needRefresh = true; + StaticSprite::update(); + _countdown = 4; + } else if (_status == 3) { + _status = 0; + setVisible(false); + } + } +} + +uint32 Class426::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x480B: + sendMessage(_parentScene, 0x480B, 0); + _status = 1; + _countdown = 4; + setVisible(true); + _soundResource.play(_soundFileHash); + break; + } + return messageResult; +} + +AsScene1002VenusFlyTrap::AsScene1002VenusFlyTrap(NeverhoodEngine *vm, Scene *parentScene, Sprite *klayman, bool flag) + : AnimatedSprite(vm, 1100), _soundResource(vm), _parentScene(parentScene), _klayman(klayman), + _flag(flag), _countdown(0) { + + createSurface(995, 175, 195); + + SetUpdateHandler(&AsScene1002VenusFlyTrap::update); + SetMessageHandler(&AsScene1002VenusFlyTrap::handleMessage448000); + SetSpriteCallback(&AnimatedSprite::updateDeltaXY); + + if (!_flag) { + if (getGlobalVar(0x8306F218)) { + setDoDeltaX(1); + _x = 366; + _y = 435; + sub4485F0(); + } else { + _x = 174 + getGlobalVar(0x1B144052) * 32; + _y = 435; + sub448660(); + } + } else { + _x = 186 + getGlobalVar(0x86341E88) * 32; + _y = 364; + if (getGlobalVar(0x13206309) || getGlobalVar(0x80101B1E)) { + sub4485F0(); + } else { + sub448660(); + } + } + + _flags = 4; +} + +void AsScene1002VenusFlyTrap::update() { + if (_countdown != 0 && (--_countdown == 0)) { + removeCallbacks(); + } + AnimatedSprite::update(); +} + +void AsScene1002VenusFlyTrap::update447FB0() { + if (_countdown == 0 && _klayman->getX() - 20 > _x) { + setDoDeltaX(1); + } else if (_klayman->getX() + 20 < _x) { + setDoDeltaX(0); + } + update(); +} + +uint32 AsScene1002VenusFlyTrap::handleMessage448000(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x000890C4) { + _soundResource.play(0xC21190D8); + } else if (param.asInteger() == 0x522200A0) { + _soundResource.play(0x931080C8); + } + break; + case 0x1011: + if (_flag) { + if (_x >= 154 && _x <= 346) { + sendMessage(_parentScene, 0x2000, 0); + messageResult = 1; + } + } else { + if (_x >= 174 && _x <= 430) { + sendMessage(_parentScene, 0x2000, 0); + messageResult = 1; + } + } + break; + case 0x480B: + setDoDeltaX(param.asInteger() != 0 ? 1 : 0); + if (!_flag) { + if (getGlobalVar(0x8306F218)) { + sub448560(); + } else { + sub448530(); + } + } else { + if (getGlobalVar(0x13206309) || getGlobalVar(0x80101B1E)) { + sub448560(); + } else { + sub448530(); + } + } + break; + case 0x480C: + if (_flag) { + if (_x >= 154 && _x <= 346) + messageResult = 1; + else + messageResult = 0; + } else { + if (_x >= 174 && _x <= 430) + messageResult = 1; + else + messageResult = 0; + } + break; + case 0x480E: + if (param.asInteger() == 1) { + sub4485B0(); + } + break; + case 0x4810: + sub448780(); + break; + case 0x482A: + sendMessage(_parentScene, 0x1022, 995); + break; + case 0x482B: + sendMessage(_parentScene, 0x1022, 1015); + break; + } + return messageResult; +} + +uint32 AsScene1002VenusFlyTrap::handleMessage4482E0(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = handleMessage448000(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + removeCallbacks(); + break; + } + return messageResult; +} + +uint32 AsScene1002VenusFlyTrap::handleMessage448320(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x000890C4) { + _soundResource.play(0xC21190D8); + } else if (param.asInteger() == 0x41881801) { + if (_flag) { + if (_x > 330) { + sendMessage(_klayman, 0x4811, 2); + } else if (_x > 265) { + sendMessage(_klayman, 0x4811, 0); + } else { + sendMessage(_klayman, 0x4811, 0); + } + } else { + sendMessage(_klayman, 0x4811, 0); + } + } else if (param.asInteger() == 0x522200A0) { + _soundResource.play(0x931080C8); + } + break; + case 0x3002: + removeCallbacks(); + break; + case 0x482A: + sendMessage(_parentScene, 0x1022, 995); + break; + case 0x482B: + sendMessage(_parentScene, 0x1022, 1015); + break; + } + return messageResult; +} + +void AsScene1002VenusFlyTrap::sub4484F0() { + setDoDeltaX(2); + setFileHash(0xC4080034, 0, -1); + SetUpdateHandler(&AsScene1002VenusFlyTrap::update); + SetMessageHandler(&AsScene1002VenusFlyTrap::handleMessage448320); + NextState(&AsScene1002VenusFlyTrap::sub448660); +} + +void AsScene1002VenusFlyTrap::sub448530() { + setFileHash(0xC4080034, 0, -1); + SetUpdateHandler(&AsScene1002VenusFlyTrap::update); + SetMessageHandler(&AsScene1002VenusFlyTrap::handleMessage4482E0); + NextState(&AsScene1002VenusFlyTrap::sub448660); +} + +void AsScene1002VenusFlyTrap::sub448560() { + sendMessage(_parentScene, 0x4807, 0); + setFileHash(0x82292851, 0, -1); + SetUpdateHandler(&AsScene1002VenusFlyTrap::update); + SetMessageHandler(&AsScene1002VenusFlyTrap::handleMessage4482E0); + NextState(&AsScene1002VenusFlyTrap::sub448660); +} + +void AsScene1002VenusFlyTrap::sub4485B0() { + setDoDeltaX(1); + setFileHash(0x86A82A11, 0, -1); + SetUpdateHandler(&AsScene1002VenusFlyTrap::update); + SetMessageHandler(&AsScene1002VenusFlyTrap::handleMessage4482E0); + NextState(&AsScene1002VenusFlyTrap::sub4485F0); +} + +void AsScene1002VenusFlyTrap::sub4485F0() { + setFileHash(0xB5A86034, 0, -1); + SetUpdateHandler(&AsScene1002VenusFlyTrap::update); + SetMessageHandler(&AsScene1002VenusFlyTrap::handleMessage448000); +} + +void AsScene1002VenusFlyTrap::sub448620() { + setFileHash(0x31303094, 0, -1); + SetUpdateHandler(&AsScene1002VenusFlyTrap::update); + SetMessageHandler(NULL); + NextState(&AsScene1002VenusFlyTrap::sub448720); + _countdown = 24; +} + +void AsScene1002VenusFlyTrap::sub448660() { + setFileHash(0xC8204250, 0, -1); + SetUpdateHandler(&AsScene1002VenusFlyTrap::update447FB0); + SetMessageHandler(&AsScene1002VenusFlyTrap::handleMessage448000); + if (_flag) { + if (_x >= 154 && _x <= 346) { + setGlobalVar(0x86341E88, (_x - 186) / 32); + } else { + NextState(&AsScene1002VenusFlyTrap::sub4484F0); + _countdown = 12; + } + } else { + if (_x >= 174 && _x <= 430) { + setGlobalVar(0x1B144052, (_x - 174) / 32); + } else { + NextState(&AsScene1002VenusFlyTrap::sub4484F0); + _countdown = 12; + } + } +} + +void AsScene1002VenusFlyTrap::sub448720() { + setFileHash(0x152920C4, 0, -1); + SetUpdateHandler(&AsScene1002VenusFlyTrap::update); + SetMessageHandler(&AsScene1002VenusFlyTrap::handleMessage448320); + NextState(&AsScene1002VenusFlyTrap::sub448750); +} + +void AsScene1002VenusFlyTrap::sub448750() { + setFileHash(0x84001117, 0, -1); + SetUpdateHandler(&AsScene1002VenusFlyTrap::update); + SetMessageHandler(&AsScene1002VenusFlyTrap::handleMessage448320); + NextState(&AsScene1002VenusFlyTrap::sub448660); +} + +void AsScene1002VenusFlyTrap::sub448780() { + if (_x - 15 < _klayman->getX() && _x + 15 > _klayman->getX()) { + if (_flag) { + setDoDeltaX(_x > 265 && _x < 330 ? 1 : 0); + } else { + setDoDeltaX(_x > 320 ? 1 : 0); + } + sendMessage(_klayman, 0x2001, 0); + setFileHash(0x8C2C80D4, 0, -1); + SetUpdateHandler(&AsScene1002VenusFlyTrap::update); + SetMessageHandler(&AsScene1002VenusFlyTrap::handleMessage448320); + NextState(&AsScene1002VenusFlyTrap::sub448620); + } +} + +Class506::Class506(NeverhoodEngine *vm) + : AnimatedSprite(vm, 1200), _countdown(0) { + + createSurface(850, 186, 212); + _x = 320; + _y = 240; + if (getGlobalVar(0x8306F218)) { + setFileHash(0x004A4495, -1, -1); + _newHashListIndex = -2; + } else { + setVisible(false); + } + SetUpdateHandler(&Class506::update); + SetMessageHandler(&Class506::handleMessage4491B0); +} + +void Class506::update() { + if (_countdown != 0 && (--_countdown == 0)) { + if (_flag) { + sub449280(); + } else { + sub449250(); + } + } + AnimatedSprite::update(); +} + +uint32 Class506::handleMessage4491B0(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageResult) { + case 0x4808: + _flag = false; + _countdown = 2; + break; + case 0x4809: + _flag = true; + _countdown = 2; + break; + } + return messageResult; +} + +uint32 Class506::handleMessage449210(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = handleMessage4491B0(messageNum, param, sender); + switch (messageResult) { + case 0x3002: + removeCallbacks(); + break; + } + return messageResult; +} + +void Class506::sub449250() { + setFileHash(0x004A4495, 0, -1); + SetMessageHandler(&Class506::handleMessage4491B0); + _newHashListIndex = -2; + setVisible(true); +} + +void Class506::sub449280() { + setFileHash(0x004A4495, -1, -1); + _playBackwards = true; + SetMessageHandler(&Class506::handleMessage449210); + NextState(&Class506::sub4492C0); + setVisible(true); +} + +void Class506::sub4492C0() { + setVisible(false); + stopAnimation(); +} + +Class478::Class478(NeverhoodEngine *vm, Klayman *klayman) + : AnimatedSprite(vm, 1200), _klayman(klayman) { + + createSurface(1200, 40, 163); + SetUpdateHandler(&Class478::update); + SetMessageHandler(&Sprite::handleMessage); + setVisible(false); +} + +void Class478::update() { + if (_klayman->getCurrAnimFileHash() == 0x3A292504) { + setFileHash(0xBA280522, _frameIndex, -1); + _newHashListIndex = _klayman->getFrameIndex(); + setVisible(true); + _x = _klayman->getX(); + _y = _klayman->getY(); + setDoDeltaX(_klayman->isDoDeltaX() ? 1 : 0); + } else if (_klayman->getCurrAnimFileHash() == 0x122D1505) { + setFileHash(0x1319150C, _frameIndex, -1); + _newHashListIndex = _klayman->getFrameIndex(); + setVisible(true); + _x = _klayman->getX(); + _y = _klayman->getY(); + setDoDeltaX(_klayman->isDoDeltaX() ? 1 : 0); + } else { + setVisible(false); + } + AnimatedSprite::update(); +} + +Class479::Class479(NeverhoodEngine *vm, Scene *parentScene, Klayman *klayman) + : AnimatedSprite(vm, 1200), _parentScene(parentScene), _klayman(klayman), + _flag1(false) { + + SetUpdateHandler(&Class479::update); + SetMessageHandler(&Class479::handleMessage); + createSurface(1000, 33, 41); + setVisible(false); +} + +void Class479::update() { + if (_klayman->getCurrAnimFileHash() == 0xAC20C012 && _klayman->getFrameIndex() < 50) { + setFileHash(0x9820C913, _klayman->getFrameIndex(), -1); + _newHashListIndex = _klayman->getFrameIndex(); + setVisible(true); + _x = _klayman->getX(); + _y = _klayman->getY(); + setDoDeltaX(_klayman->isDoDeltaX() ? 1 : 0); + } else { + setVisible(false); + } + AnimatedSprite::update(); +} + +uint32 Class479::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x4AB28209) { + sendMessage(_parentScene, 0x1022, 1200); + _flag1 = true; + _savedClipRect = _surface->getClipRect(); + setClipRect(0, 0, 640, 480); + } else if (param.asInteger() == 0x88001184) { + sendMessage(_parentScene, 0x1022, 1000); + if (_flag1) { + setClipRect(_savedClipRect); + } + } + break; + } + return messageResult; +} + +Scene1002::Scene1002(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule, true), _soundResource1(vm), _soundResource2(vm), _soundResource3(vm), + _flag1B4(false), _flag1BE(false) { + + NRect tempClipRect; + Sprite *tempSprite; + + SetUpdateHandler(&Scene1002::update); + SetMessageHandler(&Scene1002::handleMessage); + + setHitRects(0x004B4138); + + _surfaceFlag = true; + + setBackground(0x12C23307); + setPalette(0x12C23307); + + _flag = false; + + insertStaticSprite(0x06149428, 1100); + insertStaticSprite(0x312C8774, 1100); + + _ssLadderArch = insertSprite<SsScene1002LadderArch>(this); + _ssLadderArchPart1 = insertStaticSprite(0x060000A0, 1200); + _ssLadderArchPart2 = insertStaticSprite(0xB2A423B0, 1100); + _ssLadderArchPart3 = insertStaticSprite(0x316E0772, 1100); + + _class599 = insertSprite<Class599>(this); + + if (which < 0) { + if (_vm->_gameState.field2 == 0) { + insertKlayman<KmScene1002>(90, 226, _class599, _ssLadderArch); + _class478 = insertSprite<Class478>(_klayman); + setMessageList(0x004B4270); + _klayman->setClipRect(31, 0, _ssLadderArchPart2->getDrawRect().x2(), _ssLadderArchPart3->getDrawRect().y2()); + _class478->getSurface()->getClipRect() = _klayman->getSurface()->getClipRect(); + _klayman->setRepl(64, 0); + } else { + insertKlayman<KmScene1002>(379, 435, _class599, _ssLadderArch); + _class478 = insertSprite<Class478>(_klayman); + setMessageList(0x004B4270); + _klayman->setClipRect(_ssLadderArch->getDrawRect().x, 0, _ssLadderArchPart2->getDrawRect().x2(), _ssLadderArchPart1->getDrawRect().y2()); + _class478->setClipRect(_klayman->getClipRect()); + } + } else if (which == 1) { + insertKlayman<KmScene1002>(650, 435, _class599, _ssLadderArch); + _class478 = insertSprite<Class478>(_klayman); + setMessageList(0x004B4478); + _klayman->setClipRect(_ssLadderArch->getDrawRect().x, 0, _ssLadderArchPart2->getDrawRect().x2(), _ssLadderArchPart1->getDrawRect().y2()); + _class478->setClipRect(_klayman->getClipRect()); + _vm->_gameState.field2 = 1; + } else if (which == 2) { + insertKlayman<KmScene1002>(68, 645, _class599, _ssLadderArch); + _class478 = insertSprite<Class478>(_klayman); + setMessageList(0x004B4298); + _klayman->setClipRect(_ssLadderArch->getDrawRect().x, 0, _ssLadderArchPart2->getDrawRect().x2(), _ssLadderArchPart1->getDrawRect().y2()); + _class478->setClipRect(_klayman->getClipRect()); + _vm->_gameState.field2 = 1; + sendMessage(_klayman, 0x4820, 0); + } else { + insertKlayman<KmScene1002>(90, 226, _class599, _ssLadderArch); + _class478 = insertSprite<Class478>(_klayman); + setMessageList(0x004B4470); + _klayman->setClipRect(31, 0, _ssLadderArchPart2->getDrawRect().x2(), _ssLadderArchPart3->getDrawRect().y2()); + _class478->setClipRect(_klayman->getClipRect()); + _class479 = insertSprite<Class479>(this, _klayman); + _class479->setClipRect(_klayman->getClipRect()); + _klayman->setRepl(64, 0); + _vm->_gameState.field2 = 0; + } + + insertMouse433(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, _class599->getDrawRect().y, false); + _asRing2 = insertSprite<AsScene1002Ring>(this, false, 297, 189, _class599->getDrawRect().y, false); + _asRing3 = insertSprite<AsScene1002Ring>(this, true, 370, 201, _class599->getDrawRect().y, getGlobalVar(0x8306F218) != 0); + _asRing4 = insertSprite<AsScene1002Ring>(this, false, 334, 191, _class599->getDrawRect().y, false); + _asRing5 = insertSprite<AsScene1002Ring>(this, false, 425, 184, _class599->getDrawRect().y, false); + + _asDoor = insertSprite<AsScene1002Door>(tempClipRect); + tempSprite = insertSprite<Class505>(); + _asDoorSpy = insertSprite<AsScene1002DoorSpy>(tempClipRect, this, _asDoor, tempSprite); + _class426 = insertSprite<Class426>(this, 0x00412692, 0x140B60BE, 800, 0); + _asVenusFlyTrap = insertSprite<AsScene1002VenusFlyTrap>(this, _klayman, false); + _vm->_collisionMan->addSprite(_asVenusFlyTrap); + + sendEntityMessage(_klayman, 0x2007, _asVenusFlyTrap); + + _class506 = insertSprite<Class506>(); + + setRectList(0x004B43A0); + + _soundResource2.load(0x60755842); + _soundResource3.load(0x616D5821); + +} + +Scene1002::~Scene1002() { +} + +void Scene1002::update() { + Scene::update(); + if (!_flag1B4 && _klayman->getY() > 230) { + _klayman->setClipRect(_ssLadderArch->getDrawRect().x, 0, _ssLadderArchPart2->getDrawRect().x2(), _ssLadderArchPart1->getDrawRect().y2()); + _class478->setClipRect(_klayman->getClipRect()); + deleteSprite(&_ssLadderArchPart3); + _klayman->clearRepl(); + _flag1B4 = true; + _vm->_gameState.field2 = 1; + } + + if (_flag1BE && _klayman->getY() > 422) { + sendMessage(_parentModule, 0x1024, 1); + _flag1BE = false; + } + +} + +uint32 Scene1002::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + debug("Scene1002::handleMessage(%04X)", messageNum); + uint32 messageResult = 0; + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x0001: + // Debug stuff (original) + if (param.asPoint().x == 0 && getGlobalVar(0xA4014072)) { + setGlobalVar(0x8306F218, 1); + setGlobalVar(0x1B144052, 3); + leaveScene(1); + } + break; + case 0x000D: + // Debug stuff (original) + if (param.asInteger() == 0x48848178) { + setGlobalVar(0x8306F218, 1); + setGlobalVar(0x1B144052, 3); + leaveScene(1); + } + messageResult = 1; + break; + case 0x100D: + if (param.asInteger() == 0xE6EE60E1) { + if (getGlobalVar(0x8306F218)) { + setMessageList(0x004B4428); + } else { + setMessageList(0x004B4448); + } + messageResult = 1; + } else if (param.asInteger() == 0x4A845A00) { + sendEntityMessage(_klayman, 0x1014, _asRing1); + } else if (param.asInteger() == 0x43807801) { + sendEntityMessage(_klayman, 0x1014, _asRing2); + } else if (param.asInteger() == 0x46C26A01) { + if (getGlobalVar(0x8306F218)) { + setMessageList(0x004B44B8); + } else { + sendEntityMessage(_klayman, 0x1014, _asRing3); + if (_asVenusFlyTrap->getX() - 10 < 366 && _asVenusFlyTrap->getX() + 10 > 366) { + setGlobalVar(0x2B514304, 1); + setMessageList(0x004B44A8); + } else { + setMessageList(0x004B44A0); + } + } + messageResult = 1; + } else if (param.asInteger() == 0x468C7B11) { + sendEntityMessage(_klayman, 0x1014, _asRing4); + } else if (param.asInteger() == 0x42845B19) { + sendEntityMessage(_klayman, 0x1014, _asRing5); + } else if (param.asInteger() == 0xC0A07458) { + sendEntityMessage(_klayman, 0x1014, _class426); + } + break; + case 0x1024: + sendMessage(_parentModule, 0x1024, param.asInteger()); + break; + case 0x2000: + if (_flag) { + setMessageList2(0x004B43D0); + } else { + if (_klayman->getY() > 420) { + sendEntityMessage(_klayman, 0x1014, _asVenusFlyTrap); + setMessageList2(0x004B4480); + } else if (_klayman->getY() > 227) { + setMessageList2(0x004B41E0); + } else { + setMessageList2(0x004B4148); + } + } + break; + case 0x2002: + _messageList = NULL; + break; + case 0x2005: + _flag = true; + setRectList(0x004B4418); + break; + case 0x2006: + _flag = false; + setRectList(0x004B43A0); + break; + case 0x4806: + sendMessage(_parentModule, 0x1024, 2); + _flag1BE = true; + if (sender == _asRing1) { + setGlobalVar(0x4DE80AC0, 0); + _soundResource1.play(0x665198C0); + } else if (sender == _asRing2) { + setGlobalVar(0x4DE80AC0, 0); + _soundResource1.play(0xE2D389C0); + } else if (sender == _asRing3) { + setGlobalVar(0x4DE80AC0, 0); + _soundResource2.play(); + sendMessage(_asDoor, 0x4808, 0); + sendMessage(_class506, 0x4808, 0); + } else if (sender == _asRing4) { + setGlobalVar(0x4DE80AC0, 0); + _soundResource1.play(0xE0558848); + } else if (sender == _asRing5) { + setGlobalVar(0x4DE80AC0, 1); + _soundResource1.play(0x44014282); + } + break; + case 0x4807: + if (sender == _asRing3) { + _soundResource3.play(); + sendMessage(_asDoor, 0x4809, 0); + sendMessage(_class506, 0x4809, 0); + } else if (sender == _asVenusFlyTrap) { + if (getGlobalVar(0x8306F218)) { + sendMessage(_asRing3, 0x4807, 0); + } + } + break; + case 0x480B: + sendEntityMessage(_klayman, 0x1014, _asDoorSpy); + break; + case 0x480F: + setGlobalVar(0x4DE80AC0, 0); + _soundResource2.play(); + sendMessage(_asDoor, 0x4808, 0); + sendMessage(_class506, 0x4808, 0); + break; + } + return messageResult; +} + +// Class152 + +Class152::Class152(NeverhoodEngine *vm, Module *parentModule, uint32 backgroundFileHash, uint32 cursorFileHash) + : Scene(vm, parentModule, true), _fieldD0(-1), _fieldD2(-1) { + + _surfaceFlag = false; + + SetMessageHandler(&Class152::handleMessage); + + setBackground(backgroundFileHash); + setPalette(backgroundFileHash); + insertMouse435(cursorFileHash, 20, 620); +} + +uint32 Class152::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x0001: + if (param.asPoint().x <= 20 || param.asPoint().x >= 620) { + leaveScene(0); + } + break; + } + return 0; +} + +// Scene1004 + +AsScene1004TrashCan::AsScene1004TrashCan(NeverhoodEngine *vm) + : AnimatedSprite(vm, 1100), _soundResource(vm) { + + _x = 330; + _y = 327; + createSurface(800, 56, 50); + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene1004TrashCan::handleMessage); + setVisible(false); +} + +uint32 AsScene1004TrashCan::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x225A8587) { + _soundResource.play(0x109AFC4C); + } + break; + case 0x2002: + setFileHash(0xEB312C11, 0, -1); + setVisible(true); + break; + case 0x3002: + stopAnimation(); + setVisible(false); + break; + } + return 0; +} + +Scene1004::Scene1004(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule, true), _paletteAreaStatus(-1) { + + Sprite *tempSprite; + + _surfaceFlag = true; + + SetUpdateHandler(&Scene1004::update); + SetMessageHandler(&Scene1004::handleMessage); + + setBackground(0x50C03005); + + if (getGlobalVar(0x0D0A14D10)) { + setPalette(0xA30BA329); + _palette->addBasePalette(0xA30BA329, 0, 256, 0); + } else { + setPalette(0x50C03005); + _palette->addBasePalette(0x50C03005, 0, 256, 0); + } + addEntity(_palette); + + insertMouse433(0x03001504); + + if (which < 0) { + setRectList(0x004B7C70); + insertKlayman<KmScene1004>(330, 327); + setMessageList(0x004B7C18); + } else if (which == 1) { + setRectList(0x004B7C70); + insertKlayman<KmScene1004>(330, 327); + setMessageList(0x004B7C08); + } else { + loadDataResource(0x01900A04); + insertKlayman<KmScene1004>(_dataResource.getPoint(0x80052A29).x, 27); + setMessageList(0x004B7BF0); + } + + updatePaletteArea(); + + _class478 = insertSprite<Class478>(_klayman); + + insertStaticSprite(0x800034A0, 1100); + insertStaticSprite(0x64402020, 1100); + insertStaticSprite(0x3060222E, 1300); + tempSprite = insertStaticSprite(0x0E002004, 1300); + + _klayman->setClipRect(0, tempSprite->getDrawRect().y, 640, 480); + _class478->setClipRect(_klayman->getClipRect()); + + _asTrashCan = insertSprite<AsScene1004TrashCan>(); + +} + +void Scene1004::update() { + Scene::update(); + updatePaletteArea(); +} + +uint32 Scene1004::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = 0; + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x926500A1) { + setMessageList(0x004B7C20); + messageResult = 1; + } + break; + case 0x2000: + loadDataResource(0x01900A04); + break; + case 0x2001: + setRectList(0x004B7C70); + break; + case 0x2002: + sendMessage(_asTrashCan, 0x2002, 0); + break; + } + return messageResult; +} + +void Scene1004::updatePaletteArea() { + if (_klayman->getY() < 150) { + if (_paletteAreaStatus != 0) { + _paletteAreaStatus = 0; + _palette->addBasePalette(0x406B0D10, 0, 64, 0); + _palette->startFadeToPalette(12); + } + } else { + if (_paletteAreaStatus != 1) { + _paletteAreaStatus = 1; + _palette->addBasePalette(0x24332243, 0, 64, 0); + _palette->startFadeToPalette(12); + } + } +} + +// Scene1005 + +Scene1005::Scene1005(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule, true) { + + SetMessageHandler(&Scene1005::handleMessage); + + _surfaceFlag = true; + + if (getGlobalVar(0xD0A14D10)) { + setBackground(0x2800E011); + setPalette(0x2800E011); + insertStaticSprite(0x492D5AD7, 100); + insertMouse435(0x0E015288, 20, 620); + } else { + setBackground(0x8870A546); + setPalette(0x8870A546); + insertStaticSprite(0x40D1E0A9, 100); + insertStaticSprite(0x149C00A6, 100); + insertMouse435(0x0A54288F, 20, 620); + } + + drawTextToBackground(); + +} + +uint32 Scene1005::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x0001: + if (param.asPoint().x <= 20 || param.asPoint().x >= 620) { + leaveScene(0); + } + break; + } + return 0; +} + +void Scene1005::drawTextToBackground() { + TextResource textResource(_vm); + const char *textStart, *textEnd; + int16 y = 36; + uint32 textIndex = getTextIndex(); + FontSurface *fontSurface = createFontSurface(); + textResource.load(0x80283101); + textStart = textResource.getString(textIndex, textEnd); + while (textStart < textEnd) { + fontSurface->drawString(_background->getSurface(), 188, y, (const byte*)textStart); + y += 36; + textStart += strlen(textStart) + 1; + } + delete fontSurface; +} + +FontSurface *Scene1005::createFontSurface() { + 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")); + fontSurface = new FontSurface(_vm, tracking, numRows, firstChar, charWidth, charHeight); + if (getGlobalVar(0xD0A14D10)) { + fontSprite.load2(0x283CE401); + } else { + fontSprite.load2(0xC6604282); + } + fontSurface->drawSpriteResourceEx(fontSprite, false, false, 0, 0); + return fontSurface; +} + +uint32 Scene1005::getTextIndex() { + uint32 textIndex; + textIndex = getTextIndex1(); + if (getGlobalVar(0xD0A14D10)) { + textIndex = getTextIndex2(); + } + if (getGlobalVar(0x8440001F) && getGlobalVar(0x01830201) == textIndex) { + textIndex = getTextIndex3(); + } else { + setGlobalVar(0x8440001F, 1); + setGlobalVar(0x01830201, textIndex); + } + return textIndex; +} + +uint32 Scene1005::getTextIndex1() { + uint32 textIndex; + if (getGlobalVar(0x98109F12)) { + if (!getGlobalVar(0x2090590C)) + textIndex = 18; + else if (!getGlobalVar(0x610210B7)) + textIndex = 19; + else if (getGlobalVar(0x0C0288F4)) { + if (!getGlobalVar(0xD0A14D10)) + textIndex = 23; + else if (!getSubVar(0x0090EA95, 0) && !getSubVar(0x08D0AB11, 0)) + textIndex = 24; + else if (!getGlobalVar(0xC0780812)) + textIndex = 26; + else if (!getSubVar(0x0090EA95, 1) && !getSubVar(0x08D0AB11, 1)) + textIndex = 27; + else if (!getGlobalVar(0xC0780812)) + textIndex = 28; + else + textIndex = 29; + } else if (!getGlobalVar(0xE7498218)) + textIndex = 20; + else if (!getGlobalVar(0x081890D14)) + textIndex = 21; + else + textIndex = 22; + } else if (getGlobalVar(0x00040153)) { + if (!getGlobalVar(0x10938830)) + textIndex = 12; + else if (!getGlobalVar(0x2050861A)) + textIndex = 13; + else if (!getGlobalVar(0x4DE80AC0)) + textIndex = 50; + else if (!getGlobalVar(0x89C669AA)) + textIndex = 14; + else if (!getGlobalVar(0x1C1B8A9A)) + textIndex = 15; + else if (!getGlobalVar(0xCB45DE03)) + textIndex = 16; + else + textIndex = 17; + } else if (!getGlobalVar(0x2B514304)) { + textIndex = 0; + } else if (getGlobalVar(0x0A18CA33)) { + if (!getGlobalVar(0x404290D5)) + textIndex = 4; + else if (!getGlobalVar(0x45080C38)) + textIndex = 5; + else if (!getSubVar(0x14800353, 0x40119852)) + textIndex = 6; + else if (!getGlobalVar(0x4E0BE910)) + textIndex = 7; + else if (!getGlobalVar(0x86615030)) + textIndex = 8; + else if (!getSubVar(0x14800353, 0x304008D2)) + textIndex = 9; + else if (!getSubVar(0x14800353, 0x01180951)) + textIndex = 10; + else + textIndex = 11; + } else if (!getGlobalVar(0x0A310817)) { + textIndex = 1; + } else if (getGlobalVar(0x000CF819)) { + textIndex = 3; + } else { + textIndex = 2; + } + return textIndex; +} + +uint32 Scene1005::getTextIndex2() { + uint32 textIndex = getGlobalVar(0x29408F00); + if (textIndex + 1 >= 10) { + setGlobalVar(0x29408F00, 0); + textIndex = 0; + } else { + setGlobalVar(0x29408F00, textIndex + 1); + } + return textIndex + 40; +} + +uint32 Scene1005::getTextIndex3() { + uint32 textIndex = getGlobalVar(0x8A140C21); + if (textIndex + 1 >= 10) { + setGlobalVar(0x8A140C21, 0); + textIndex = 0; + } else { + setGlobalVar(0x8A140C21, textIndex + 1); + } + return textIndex + 30; +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/module1000.h b/engines/neverhood/module1000.h new file mode 100644 index 0000000000..47b1a91229 --- /dev/null +++ b/engines/neverhood/module1000.h @@ -0,0 +1,336 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef NEVERHOOD_MODULE1000_H +#define NEVERHOOD_MODULE1000_H + +#include "neverhood/neverhood.h" +#include "neverhood/module.h" +#include "neverhood/scene.h" + +namespace Neverhood { + +// Module1000 + +class Module1000 : public Module { +public: + Module1000(NeverhoodEngine *vm, Module *parentModule, int which); + virtual ~Module1000(); +protected: + uint32 _musicFileHash; + void createScene(int sceneNum, int which); + void updateScene(); +}; + +// Scene1001 + +class AsScene1001Door : public AnimatedSprite { +public: + AsScene1001Door(NeverhoodEngine *vm); +protected: + SoundResource _soundResource1; + SoundResource _soundResource2; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void handleMessage2000h(); + void callback1(); + void callback2(); + void callback3(); +}; + +class AsScene1001Hammer : public AnimatedSprite { +public: + AsScene1001Hammer(NeverhoodEngine *vm, Sprite *asDoor); +protected: + Sprite *_asDoor; + SoundResource _soundResource; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class AsScene1001Window : public AnimatedSprite { +public: + AsScene1001Window(NeverhoodEngine *vm); +protected: + SoundResource _soundResource; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class AsScene1001Lever : public AnimatedSprite { +public: + AsScene1001Lever(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y, int deltaXType); +protected: + Scene *_parentScene; + SoundResource _soundResource; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class SsCommonButtonSprite : public StaticSprite { +public: + SsCommonButtonSprite(NeverhoodEngine *vm, Scene *parentScene, uint32 fileHash, int surfacePriority, uint32 soundFileHash); +protected: + Scene *_parentScene; + SoundResource _soundResource; + uint32 _soundFileHash; + int16 _countdown; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene1001 : public Scene { +public: + Scene1001(NeverhoodEngine *vm, Module *parentModule, int which); + virtual ~Scene1001(); +protected: + Sprite *_asHammer; + Sprite *_asDoor; + Sprite *_asWindow; + Sprite *_asLever; + Sprite *_ssButton; + int16 _fieldE4; + int16 _fieldE6; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +// TODO: Move this to some common file since it's used several times + +class Class152 : public Scene { +public: + Class152(NeverhoodEngine *vm, Module *parentModule, uint32 backgroundFileHash, uint32 cursorFileHash); +protected: + // TODO: Are these used? + int16 _fieldD0; + int16 _fieldD2; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +// Scene1002 + +class SsScene1002LadderArch : public StaticSprite { +public: + SsScene1002LadderArch(NeverhoodEngine *vm, Scene *parentScene); +protected: + Scene *_parentScene; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Class599 : public StaticSprite { +public: + Class599(NeverhoodEngine *vm, Scene *parentScene); +protected: + Scene *_parentScene; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class AsScene1002Ring : public AnimatedSprite { +public: + AsScene1002Ring(NeverhoodEngine *vm, Scene *parentScene, bool flag1, int16 x, int16 y, int16 clipY1, bool flag2); +protected: + Scene *_parentScene; + bool _flag1; + SoundResource _soundResource; + void update(); + uint32 handleMessage4475E0(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 handleMessage447760(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 handleMessage447890(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 handleMessage447930(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 handleMessage447A00(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class AsScene1002Door : public StaticSprite { +public: + AsScene1002Door(NeverhoodEngine *vm, NRect &clipRect); +protected: + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void suOpenDoor(); + void suCloseDoor(); +}; + +class Class505 : public AnimatedSprite { +public: + Class505(NeverhoodEngine *vm); +protected: + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class AsScene1002DoorSpy : public AnimatedSprite { +public: + AsScene1002DoorSpy(NeverhoodEngine *vm, NRect &clipRect, Scene *parentScene, Sprite *asDoor, Sprite *class505); +protected: + Scene *_parentScene; + Sprite *_asDoor; + Sprite *_class505; + SoundResource _soundResource; + NRect _rect; + uint32 handleMessage4489D0(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 handleMessage448A60(int messageNum, const MessageParam ¶m, Entity *sender); + void spriteUpdate448AA0(); + void sub448AC0(); + void sub448B10(); +}; + +class Class426 : public StaticSprite { +public: + Class426(NeverhoodEngine *vm, Scene *parentScene, uint32 fileHash1, uint32 fileHash2, int surfacePriority, uint32 soundFileHash); + void setFileHashes(uint32 fileHash1, uint32 fileHash2); +protected: + Scene *_parentScene; + int _countdown; + uint32 _fileHashes[2]; + int _status; + SoundResource _soundResource; + uint32 _soundFileHash; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class AsScene1002VenusFlyTrap : public AnimatedSprite { +public: + AsScene1002VenusFlyTrap(NeverhoodEngine *vm, Scene *parentScene, Sprite *klayman, bool flag); +protected: + Scene *_parentScene; + Sprite *_klayman; + int _countdown; + SoundResource _soundResource; + bool _flag; + void update(); + void update447FB0(); + uint32 handleMessage448000(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 handleMessage4482E0(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 handleMessage448320(int messageNum, const MessageParam ¶m, Entity *sender); + void sub4484F0(); + void sub448530(); + void sub448560(); + void sub4485B0(); + void sub4485F0(); + void sub448620(); + void sub448660(); + void sub448720(); + void sub448750(); + void sub448780(); +}; + +class Class506 : public AnimatedSprite { +public: + Class506(NeverhoodEngine *vm); +protected: + int _countdown; + bool _flag; + void update(); + uint32 handleMessage4491B0(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 handleMessage449210(int messageNum, const MessageParam ¶m, Entity *sender); + void sub449250(); + void sub449280(); + void sub4492C0(); +}; + +class Class478 : public AnimatedSprite { +public: + Class478(NeverhoodEngine *vm, Klayman *klayman); +protected: + Klayman *_klayman; + void update(); +}; + +class Class479 : public AnimatedSprite { +public: + Class479(NeverhoodEngine *vm, Scene *parentScene, Klayman *klayman); +protected: + Scene *_parentScene; + Klayman *_klayman; + bool _flag1; + NRect _savedClipRect; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene1002 : public Scene { +public: + Scene1002(NeverhoodEngine *vm, Module *parentModule, int which); + virtual ~Scene1002(); +protected: + Sprite *_asRing1; + Sprite *_asRing2; + Sprite *_asRing3; + Sprite *_asRing4; + Sprite *_asRing5; + Sprite *_asDoor; + Sprite *_asDoorSpy; + Sprite *_asVenusFlyTrap; + Sprite *_ssLadderArch; + Sprite *_ssLadderArchPart1; + Sprite *_ssLadderArchPart2; + Sprite *_ssLadderArchPart3; + Sprite *_class599; + Sprite *_class478; + Sprite *_class479; + Sprite *_class506; + Sprite *_class426; + SoundResource _soundResource1; + SoundResource _soundResource2; + SoundResource _soundResource3; + bool _flag1B4; + bool _flag1BE; + bool _flag; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +// Scene1004 + +class AsScene1004TrashCan : public AnimatedSprite { +public: + AsScene1004TrashCan(NeverhoodEngine *vm); +protected: + SoundResource _soundResource; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene1004 : public Scene { +public: + Scene1004(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + Sprite *_class478; + Sprite *_asTrashCan; + int _paletteAreaStatus; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void updatePaletteArea(); +}; + +// Scene1005 + +class Scene1005 : public Scene { +public: + Scene1005(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void drawTextToBackground(); + FontSurface *createFontSurface(); + uint32 getTextIndex(); + uint32 getTextIndex1(); + uint32 getTextIndex2(); + uint32 getTextIndex3(); +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_MODULE1000_H */ diff --git a/engines/neverhood/module1100.cpp b/engines/neverhood/module1100.cpp new file mode 100644 index 0000000000..52c9d92a33 --- /dev/null +++ b/engines/neverhood/module1100.cpp @@ -0,0 +1,719 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "neverhood/module1100.h" +#include "neverhood/gamemodule.h" + +namespace Neverhood { + +Module1100::Module1100(NeverhoodEngine *vm, Module *parentModule, int which) + : Module(vm, parentModule) { + + if (which < 0) { + createScene(_vm->gameState().sceneNum, -1); + } else if (which == 1) { + createScene(9, 1); + } else { + createScene(9, 3); + } + + // TODO Sound1ChList_addSoundResources(0x2C818, dword_4B85B0, true); + // TODO Sound1ChList_setSoundValuesMulti(dword_4B85B0, true, 50, 600, 20, 250); + // TODO Sound1ChList_setSoundValues(0x74E01054, false, 100, 200, 10, 20); + // TODO Sound1ChList_setVolume(0x74E01054, 60); + // TODO Sound1ChList_sub_407C70(0x2C818, 0x41861371, 0x43A2507F); + +} + +Module1100::~Module1100() { + // TODO Sound1ChList_sub_407A50(0x2C818); +} + +void Module1100::createScene(int sceneNum, int which) { + static const uint32 kSmackerFileHashList06[] = {0x10880805, 0x1088081D, 0}; + static const uint32 kSmackerFileHashList07[] = {0x00290321, 0x01881000, 0}; + debug("Module1100::createScene(%d, %d)", sceneNum, which); + _vm->gameState().sceneNum = sceneNum; + switch (_vm->gameState().sceneNum) { + case 0: + _countdown = 65; + createNavigationScene(0x004B8430, which); + break; + case 1: + _countdown = 50; + createNavigationScene(0x004B8460, which); + break; + case 2: + if (getGlobalVar(0x610210B7)) { + createNavigationScene(0x004B84F0, which); + } else { + createNavigationScene(0x004B8490, which); + } + break; + case 3: + if (getGlobalVar(0x610210B7)) { + createNavigationScene(0x004B8580, which); + } else { + createNavigationScene(0x004B8550, which); + } + break; + case 4: + _childObject = new Scene1105(_vm, this, which); + break; + case 5: + if (getGlobalVar(0x610210B7)) + createSmackerScene(0x04180001, true, false, false); + else + createSmackerScene(0x04180007, true, false, false); + break; + case 6: + // TODO Sound1ChList_sub_407AF0(0x2C818); + createSmackerScene(kSmackerFileHashList06, true, true, false); + break; + case 7: + // TODO Sound1ChList_setSoundValues(0x74E01054, false, 0, 0, 0, 0); + createSmackerScene(kSmackerFileHashList07, true, true, false); + break; + case 8: + _childObject = new Scene1109(_vm, this, which); + break; + case 1002: + _countdown = 40; + // TODO Sound1ChList_sub_4080B0(true); + createSmackerScene(0x00012211, true, true, false); + break; + } + SetUpdateHandler(&Module1100::updateScene); + _childObject->handleUpdate(); +} + +void Module1100::updateScene() { + if (!updateChild()) { + switch (_vm->gameState().sceneNum) { + case 0: + _countdown = 0; + // TODO Sound1ChList_sub_407C70(0x2C818, 0x48498E46, 0x50399F64); + // TODO Sound1ChList_setVolume(0x48498E46, 65); + // TODO Sound1ChList_setVolume(0x50399F64, 65); + if (_moduleResult == 0) { + createScene(1, 0); + } else if (_moduleResult == 1) { + createScene(8, 0); + } + break; + case 1: + // TODO Sound1ChList_sub_407C70(0x2C818, 0x41861371, 0x43A2507F); + if (getGlobalVar(0x0C0288F4)) { + 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: + // TODO Sound1ChList_setSoundValues(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: + if (getGlobalVar(0x610210B7)) { + createScene(3, 0); + } else { + createScene(4, 0); + } + break; + case 6: + leaveModule(1); + break; + case 7: + createScene(2, 2); + break; + case 8: + if (_moduleResult == 0) { + createScene(0, 0); + } else if (_moduleResult == 1) { + leaveModule(0); + } + break; + case 1002: + _countdown = 0; + // TODO Sound1ChList_sub_407C70(0x2C818, 0x48498E46, 0x50399F64, 0); + createScene(1, 1); + break; + } + } else { + switch (_vm->gameState().sceneNum) { + case 0: +#if 0 // TODO + if (navigationScene()->soundFlag1 && _countdown != 0 && (--_countdown == 0)) { + Sound1ChList_sub_407C70(0x2C818, 0x48498E46, 0x50399F64); + Sound1ChList_setVolume(0x48498E46, 65); + Sound1ChList_setVolume(0x50399F64, 65); + } +#endif + break; + case 1: +#if 0 // TODO + if (navigationScene()->soundFlag1 && _countdown != 0 && (--_countdown == 0)) { + Sound1ChList_sub_407C70(0x2C818, 0x41861371, 0x43A2507F); + } +#endif + break; + case 2: + // TODO Sound1ChList_setSoundValues(0x74E01054, !navigationScene()->soundFlag1, 0, 0, 0, 0); + break; + case 5: + case 6: + case 7: + case 1002: + if (_countdown != 0 && (--_countdown == 0)) { + // TODO Sound1ChList_sub_407C70(0x2C818, 0x48498E46, 0x50399F64); + // TODO Sound1ChList_setVolume(0x48498E46, 65); + // TODO Sound1ChList_setVolume(0x50399F64, 65); + } + break; + } + } +} + +static const uint32 kScene1105FileHashes[] = { + 0x00028006, + 0x0100A425, + 0x63090415, + 0x082100C4, + 0x0068C607, + 0x00018344, + 0x442090E4, + 0x0400E004, + 0x5020A054, + 0xB14A891E +}; + +static const uint32 kScene1105BackgroundFileHashes[] = { + 0x20018662, + 0x20014202, + 0x20012202, + 0x20010002 // CHECKME: This used ?? +}; + +static const uint32 kSsScene1105SymbolDieFileHashes[] = { + 0, + 0x90898414, + 0x91098414, + 0x92098414, + 0x94098414, + 0x98098414, + 0x80098414, + 0xB0098414, + 0xD0098414, + 0x10098414 +}; + +SsScene1105Button::SsScene1105Button(NeverhoodEngine *vm, Scene *parentScene, uint32 fileHash, NRect &rect) + : StaticSprite(vm, fileHash, 200), _soundResource(vm), _parentScene(parentScene), + _countdown(0) { + + _rect = rect; + SetMessageHandler(&SsScene1105Button::handleMessage); + SetUpdateHandler(&SsScene1105Button::update); + setVisible(false); +} + +void SsScene1105Button::update() { + if (_countdown != 0 && (--_countdown == 0)) { + sendMessage(_parentScene, 0x4807, 0); + setVisible(false); + } +} + +uint32 SsScene1105Button::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + if (_countdown == 0) { + sendMessage(_parentScene, 0x4826, 0); + messageResult = 1; + } + break; + case 0x480B: + _countdown = 8; + setVisible(true); + _soundResource.play(0x44141000); + break; + } + return messageResult; +} + +SsScene1105Symbol::SsScene1105Symbol(NeverhoodEngine *vm, uint32 fileHash, int16 x, int16 y) + : StaticSprite(vm, fileHash, 200) { + + _x = x; + _y = y; + _drawRect.x = -(_spriteResource.getDimensions().width / 2); + _drawRect.y = -(_spriteResource.getDimensions().height / 2); + StaticSprite::update(); +} + +void SsScene1105Symbol::hide() { + setVisible(false); + _needRefresh = true; + StaticSprite::update(); +} + +SsScene1105SymbolDie::SsScene1105SymbolDie(NeverhoodEngine *vm, uint index, int16 x, int16 y) + : StaticSprite(vm, 1100), _index(index) { + + SetMessageHandler(&SsScene1105SymbolDie::handleMessage); + _x = x; + _y = y; + createSurface(200, 50, 50); + loadSymbolSprite(); +} + +uint32 SsScene1105SymbolDie::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2000: + loadSymbolSprite(); + break; + } + return messageResult; +} + +void SsScene1105SymbolDie::loadSymbolSprite() { + load(kSsScene1105SymbolDieFileHashes[getSubVar(0x61084036, _index)], true, false); + _drawRect.x = -(_spriteResource.getDimensions().width / 2); + _drawRect.y = -(_spriteResource.getDimensions().height / 2); + StaticSprite::update(); +} + +void SsScene1105SymbolDie::hide() { + setVisible(false); + _needRefresh = true; + StaticSprite::update(); +} + +AsScene1105TeddyBear::AsScene1105TeddyBear(NeverhoodEngine *vm, Scene *parentScene) + : AnimatedSprite(vm, 1100), _soundResource1(vm), _soundResource2(vm), + _parentScene(parentScene) { + + // TODO createSurface3(100, dword_4AF4C0); + createSurface(100, 640, 480); //TODO: Remeove once the line above is done + _x = 320; + _y = 240; + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene1105TeddyBear::handleMessage); + setFileHash(0x65084002, 0, -1); + _newHashListIndex = 0; + setVisible(false); + _needRefresh = true; + updatePosition(); + _soundResource1.load(0xCE840261); + _soundResource2.load(0xCCA41A62); +} + +uint32 AsScene1105TeddyBear::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2002: + if (getGlobalVar(0x610210B7)) { + setFileHash(0x6B0C0432, 0, -1); + _soundResource1.play(); + } else { + setFileHash(0x65084002, 0, -1); + _soundResource2.play(); + } + break; + case 0x3002: + sendMessage(_parentScene, 0x2003, 0); + stopAnimation(); + break; + } + return messageResult; +} + +void AsScene1105TeddyBear::show() { + setVisible(true); + _needRefresh = true; + updatePosition(); +} + +void AsScene1105TeddyBear::hide() { + setVisible(false); + _needRefresh = true; + updatePosition(); +} + +SsScene1105OpenButton::SsScene1105OpenButton(NeverhoodEngine *vm, Scene *parentScene) + : StaticSprite(vm, 900), _soundResource(vm), _parentScene(parentScene), + _countdown(0), _flag1(false) { + + _spriteResource.load2(0x8228A46C); + createSurface(400, _spriteResource.getDimensions().width, _spriteResource.getDimensions().height); + _x = _spriteResource.getPosition().x; + _y = _spriteResource.getPosition().y; + _drawRect.x = 0; + _drawRect.y = 0; + _drawRect.width = _spriteResource.getDimensions().width; + _drawRect.height = _spriteResource.getDimensions().height; + _deltaRect = _drawRect; + _needRefresh = true; + processDelta(); + setVisible(false); + _soundResource.load(0x44045140); + SetUpdateHandler(&SsScene1105OpenButton::update); + SetMessageHandler(&SsScene1105OpenButton::handleMessage); +} + +void SsScene1105OpenButton::update() { + StaticSprite::update(); + if (_countdown != 0 && (--_countdown == 0)) { + setVisible(false); + sendMessage(_parentScene, 0x2001, 0); + } +} + +uint32 SsScene1105OpenButton::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = 0; + Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + if (_countdown == 0 && !_flag1) { + _soundResource.play(); + setVisible(true); + _flag1 = true; + _countdown = 4; + } + messageResult = 1; + break; + } + return messageResult; +} + +Scene1105::Scene1105(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule, true), _soundResource1(vm), _soundResource2(vm), + _soundResource3(vm), _countdown(0), _flag1(false), _flag2(false), _flag3(false), + _flag4(false), _flag5(false), _backgroundIndex(0) { + + Sprite *ssOpenButton; + + _vm->gameModule()->initScene1405Vars(); + + _surfaceFlag = true; + SetUpdateHandler(&Scene1105::update); + SetMessageHandler(&Scene1105::handleMessage); + + setBackground(0x20010002); + setPalette(0x20010002); + + _asTeddyBear = insertSprite<AsScene1105TeddyBear>(this); + ssOpenButton = insertSprite<SsScene1105OpenButton>(this); + _vm->_collisionMan->addSprite(ssOpenButton); + insertMouse435(0x10006208, 20, 620); + + _soundResource1.load(0x48442057); + _soundResource2.load(0xC025014F); + _soundResource3.load(0x68E25540); + +} + +uint32 Scene1105::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = 0; + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x0001: + // TODO Debug stuff + if (param.asPoint().x <= 20 || param.asPoint().x >= 620) { + if (!_flag2 && _backgroundIndex == 0) { + if (_flag1) { + _flag1 = false; + _backgroundIndex = 15; + SetUpdateHandler(&Scene1105::upClosePanel); + } else + _flag1 = true; + _flag5 = false; + } + } + break; + // TODO Debug stuff + case 0x2001: + showMouse(false); + _backgroundIndex = 24; + SetUpdateHandler(&Scene1105::upOpenPanel); + break; + case 0x2003: + _backgroundIndex = 24; + _flag5 = true; + SetUpdateHandler(&Scene1105::upClosePanel); + break; + case 0x4807: + if (sender == _ssActionButton) { + if (getSubVar(0x7500993A, 0) == getSubVar(0x61084036, 0) && + getSubVar(0x7500993A, 1) == getSubVar(0x61084036, 1) && + getSubVar(0x7500993A, 2) == getSubVar(0x61084036, 2)) { + setGlobalVar(0x610210B7, 1); + _soundResource3.play(); + _flag3 = true; + } else { + sendMessage(_asTeddyBear, 0x2002, 0); + } + showMouse(false); + _flag2 = true; + } + break; + case 0x4826: + if (_flag1) { + if (sender == _ssActionButton) { + sendMessage(_ssActionButton, 0x480B, 0); + _flag1 = false; + } else if (!getGlobalVar(0x610210B7)) { + if (sender == _ssSymbol1UpButton) { + if (getSubVar(0x61084036, 0) < 9) { + incSubVar(0x61084036, 0, +1); + sendMessage(_ssSymbol1UpButton, 0x480B, 0); + sendMessage(_ssSymbolDice[0], 0x2000, 0); + } + } else if (sender == _ssSymbol1DownButton) { + if (getSubVar(0x61084036, 0) > 1) { + incSubVar(0x61084036, 0, -1); + sendMessage(_ssSymbol1DownButton, 0x480B, 0); + sendMessage(_ssSymbolDice[0], 0x2000, 0); + } + } else if (sender == _ssSymbol2UpButton) { + if (getSubVar(0x61084036, 1) < 9) { + incSubVar(0x61084036, 1, +1); + sendMessage(_ssSymbol2UpButton, 0x480B, 0); + sendMessage(_ssSymbolDice[1], 0x2000, 0); + } + } else if (sender == _ssSymbol2DownButton) { + if (getSubVar(0x61084036, 1) > 1) { + incSubVar(0x61084036, 1, -1); + sendMessage(_ssSymbol2DownButton, 0x480B, 0); + sendMessage(_ssSymbolDice[1], 0x2000, 0); + } + } else if (sender == _ssSymbol3UpButton) { + if (getSubVar(0x61084036, 2) < 9) { + incSubVar(0x61084036, 2, +1); + sendMessage(_ssSymbol3UpButton, 0x480B, 0); + sendMessage(_ssSymbolDice[2], 0x2000, 0); + } + } else if (sender == _ssSymbol3DownButton) { + if (getSubVar(0x61084036, 2) > 1) { + incSubVar(0x61084036, 2, -1); + sendMessage(_ssSymbol3DownButton, 0x480B, 0); + sendMessage(_ssSymbolDice[2], 0x2000, 0); + } + } + } + } + break; + } + return messageResult; +} + +void Scene1105::createObjects() { + _ssSymbols[0] = insertSprite<SsScene1105Symbol>(kScene1105FileHashes[getSubVar(0x13100631, 0)], 161, 304); + _ssSymbols[1] = insertSprite<SsScene1105Symbol>(kScene1105FileHashes[getSubVar(0x13100631, 1)], 294, 304); + _ssSymbols[2] = insertSprite<SsScene1105Symbol>(kScene1105FileHashes[getSubVar(0x13100631, 2)], 440, 304); + + _ssSymbolDice[0] = insertSprite<SsScene1105SymbolDie>(0, 206, 304); + _ssSymbolDice[1] = insertSprite<SsScene1105SymbolDie>(1, 339, 304); + _ssSymbolDice[2] = insertSprite<SsScene1105SymbolDie>(2, 485, 304); + + _ssSymbol1UpButton = insertSprite<SsScene1105Button>(this, 0x08002860, NRect(146, 362, 192, 403)); + _vm->_collisionMan->addSprite(_ssSymbol1UpButton); + _ssSymbol1DownButton = insertSprite<SsScene1105Button>(this, 0x42012460, NRect(147, 404, 191, 442)); + _vm->_collisionMan->addSprite(_ssSymbol1DownButton); + _ssSymbol2UpButton = insertSprite<SsScene1105Button>(this, 0x100030A0, NRect(308, 361, 355, 402)); + _vm->_collisionMan->addSprite(_ssSymbol2UpButton); + _ssSymbol2DownButton = insertSprite<SsScene1105Button>(this, 0x840228A0, NRect(306, 406, 352, 445)); + _vm->_collisionMan->addSprite(_ssSymbol2DownButton); + _ssSymbol3UpButton = insertSprite<SsScene1105Button>(this, 0x20000120, NRect(476, 358, 509, 394)); + _vm->_collisionMan->addSprite(_ssSymbol3UpButton); + _ssSymbol3DownButton = insertSprite<SsScene1105Button>(this, 0x08043121, NRect(463, 401, 508, 438)); + _vm->_collisionMan->addSprite(_ssSymbol3DownButton); + _ssActionButton = insertSprite<SsScene1105Button>(this, 0x8248AD35, NRect(280, 170, 354, 245)); + _vm->_collisionMan->addSprite(_ssActionButton); + + _flag1 = true; + + _asTeddyBear->show(); + + // TODO: Find a nicer way + deleteSprite((Sprite**)&_mouseCursor); + insertMouse435(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) { + _soundResource1.play(); + } + 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) { + _soundResource2.play(); + _asTeddyBear->hide(); + } + changeBackground(backgroundFileHash); + _palette->addPalette(backgroundFileHash, 0, 256, 0); + } + if (_backgroundIndex == 0) { + SetUpdateHandler(&Scene1105::update); + _flag4 = true; + } + } +} + +void Scene1105::update() { + + // DEBUG: Show the correct code + debug("(%d, %d) (%d, %d) (%d, %d)", + getSubVar(0x7500993A, 0), getSubVar(0x61084036, 0), + getSubVar(0x7500993A, 1), getSubVar(0x61084036, 1), + getSubVar(0x7500993A, 2), getSubVar(0x61084036, 2)); + + Scene::update(); + if (_countdown != 0 && (--_countdown == 0)) { + createObjects(); + } + if (_flag4 && !_soundResource2.isPlaying()) { + leaveScene(_flag5); + } + if (_flag3 && !_soundResource3.isPlaying()) { + sendMessage(_asTeddyBear, 0x2002, 0); + _flag3 = false; + } +} + +Scene1109::Scene1109(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule, which) { + + _surfaceFlag = true; + SetMessageHandler(&Scene1109::handleMessage); + + setBackground(0x8449E02F); + setPalette(0x8449E02F); + insertMouse433(0x9E02B84C); + + _sprite1 = insertStaticSprite(0x600CEF01, 1100); + + if (which < 0) { + insertKlayman<KmScene1109>(140, 436); + setMessageList(0x004B6260); + sendMessage(this, 0x2000, 0); + } else if (which == 1) { + insertKlayman<KmScene1109>(450, 436); + sendMessage(_klayman, 0x2000, 1); + setMessageList(0x004B6268, false); + sendMessage(this, 0x2000, 1); + } else if (which == 2) { + insertKlayman<KmScene1109>(450, 436); + sendMessage(_klayman, 0x2000, 1); + setMessageList(0x004B6318, false); + sendMessage(this, 0x2000, 1); + } else if (which == 3) { + insertKlayman<KmScene1109>(450, 436); + sendMessage(_klayman, 0x2000, 1); + setMessageList(0x004B6278, false); + sendMessage(this, 0x2000, 1); + } else { + insertKlayman<KmScene1109>(0, 436); + setMessageList(0x004B6258); + sendMessage(this, 0x2000, 0); + } + + _klayman->setClipRect(0, 0, _sprite1->getDrawRect().x2(), 480); + +} + +uint32 Scene1109::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2000: + if (param.asInteger()) { + setRectList(0x004B63A8); + _klayman->setKlaymanTable3(); + } else { + setRectList(0x004B6398); + _klayman->setKlaymanTable1(); + } + break; + } + return 0; +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/module1100.h b/engines/neverhood/module1100.h new file mode 100644 index 0000000000..c46c1dfbca --- /dev/null +++ b/engines/neverhood/module1100.h @@ -0,0 +1,136 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef NEVERHOOD_MODULE1100_H +#define NEVERHOOD_MODULE1100_H + +#include "neverhood/neverhood.h" +#include "neverhood/module.h" +#include "neverhood/scene.h" + +namespace Neverhood { + +// Module1100 + +class Module1100 : public Module { +public: + Module1100(NeverhoodEngine *vm, Module *parentModule, int which); + virtual ~Module1100(); +protected: + int _countdown; + void createScene(int sceneNum, int which); + void updateScene(); +}; + +class SsScene1105Button : public StaticSprite { +public: + SsScene1105Button(NeverhoodEngine *vm, Scene *parentScene, uint32 fileHash, NRect &rect); +protected: + Scene *_parentScene; + SoundResource _soundResource; + int _countdown; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class SsScene1105Symbol : public StaticSprite { +public: + SsScene1105Symbol(NeverhoodEngine *vm, uint32 fileHash, int16 x, int16 y); + void hide(); +}; + +class SsScene1105SymbolDie : public StaticSprite { +public: + SsScene1105SymbolDie(NeverhoodEngine *vm, uint index, int16 x, int16 y); + void hide(); +protected: + uint _index; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void loadSymbolSprite(); +}; + +class AsScene1105TeddyBear : public AnimatedSprite { +public: + AsScene1105TeddyBear(NeverhoodEngine *vm, Scene *parentScene); + void show(); + void hide(); +protected: + Scene *_parentScene; + SoundResource _soundResource1; + SoundResource _soundResource2; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class SsScene1105OpenButton : public StaticSprite { +public: + SsScene1105OpenButton(NeverhoodEngine *vm, Scene *parentScene); +protected: + Scene *_parentScene; + SoundResource _soundResource; + int _countdown; + bool _flag1; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene1105 : public Scene { +public: + Scene1105(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + SoundResource _soundResource1; + SoundResource _soundResource2; + SoundResource _soundResource3; + int _countdown; + int _backgroundIndex; + bool _flag1; + bool _flag2; + bool _flag3; + bool _flag4; + bool _flag5; + AsScene1105TeddyBear *_asTeddyBear; + SsScene1105Symbol *_ssSymbols[3]; + SsScene1105SymbolDie *_ssSymbolDice[3]; + Sprite *_ssSymbol1UpButton; + Sprite *_ssSymbol1DownButton; + Sprite *_ssSymbol2UpButton; + Sprite *_ssSymbol2DownButton; + Sprite *_ssSymbol3UpButton; + Sprite *_ssSymbol3DownButton; + Sprite *_ssActionButton; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void createObjects(); + void upOpenPanel(); + void upClosePanel(); + void update(); +}; + +class Scene1109 : public Scene { +public: + Scene1109(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + Sprite *_sprite1; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_MODULE1100_H */ diff --git a/engines/neverhood/module1200.cpp b/engines/neverhood/module1200.cpp new file mode 100644 index 0000000000..25abf95643 --- /dev/null +++ b/engines/neverhood/module1200.cpp @@ -0,0 +1,1236 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "neverhood/module1200.h" + +namespace Neverhood { + +Module1200::Module1200(NeverhoodEngine *vm, Module *parentModule, int which) + : Module(vm, parentModule) { + + SetMessageHandler(&Module1200::handleMessage); + + debug("Module1200: which = %d", which); + + if (which < 0) { + createScene(_vm->gameState().sceneNum, -1); + } else if (which == 1) { + createScene(0, 2); + } else { + createScene(0, 0); + } + + // TODO Music18hList_add(0x00478311, 0x62222CAE); + // TODO Music18hList_play(0x62222CAE, 0, 0, 1); +} + +Module1200::~Module1200() { + // TODO Music18hList_deleteGroup(0x00478311); +} + +void Module1200::createScene(int sceneNum, int which) { + debug("Module1200::createScene(%d, %d)", sceneNum, which); + _vm->gameState().sceneNum = sceneNum; + switch (_vm->gameState().sceneNum) { + case 0: + _childObject = new Scene1201(_vm, this, which); + break; + case 1: + _childObject = new Scene1202(_vm, this, which); + break; + case 2: + // TODO Music18hList_stop(0x62222CAE, 0, 0); + createSmackerScene(0x31890001, true, true, false); + setGlobalVar(0x2A02C07B, 1); + break; + } + SetUpdateHandler(&Module1200::updateScene); + _childObject->handleUpdate(); +} + +void Module1200::updateScene() { + if (!updateChild()) { + switch (_vm->gameState().sceneNum) { + case 0: + if (_moduleResult == 1) { + createScene(1, 0); + } else if (_moduleResult == 2) { + if (getGlobalVar(0x0A18CA33) && !getGlobalVar(0x2A02C07B)) { + createScene(2, -1); + } else { + leaveModule(1); + } + } else { + leaveModule(0); + } + break; + case 1: + createScene(0, 1); + break; + case 2: + // TODO Music18hList_play(0x62222CAE, 0, 0, 1); + createScene(0, 3); + break; + } + } +} + +// Scene1201 + +static const uint32 kScene1201InitArray[] = { + 1, 0, 2, 4, 5, 3, 6, 7, 8, 10, 9, 11, 13, 14, 12, 16, 17, 15 +}; + +static const NPoint kScene1201PointArray[] = { + {218, 193}, + {410, 225}, + {368, 277}, + {194, 227}, + {366, 174}, + {458, 224}, + {242, 228}, + {512, 228}, + {458, 277}, + {217, 233}, + {458, 173}, + {410, 276}, + {203, 280}, + {371, 226}, + {508, 279}, + {230, 273}, + {410, 171}, + {493, 174} +}; + +static const uint32 kScene1201TntFileHashList1[] = { + 0x2098212D, + 0x1600437E, + 0x1600437E, + 0x00A840E3, + 0x1A1830F6, + 0x1A1830F6, + 0x00212062, + 0x384010B6, + 0x384010B6, + 0x07A01080, + 0xD80C2837, + 0xD80C2837, + 0x03A22092, + 0xD8802CB6, + 0xD8802CB6, + 0x03A93831, + 0xDA460476, + 0xDA460476 +}; + +static const uint32 kScene1201TntFileHashList2[] = { + 0x3040C676, + 0x10914448, + 0x10914448, + 0x3448A066, + 0x1288C049, + 0x1288C049, + 0x78C0E026, + 0x3098D05A, + 0x3098D05A, + 0x304890E6, + 0x1284E048, + 0x1284E048, + 0xB140A1E6, + 0x5088A068, + 0x5088A068, + 0x74C4C866, + 0x3192C059, + 0x3192C059 +}; + +SsScene1201Tnt::SsScene1201Tnt(NeverhoodEngine *vm, uint32 elemIndex, uint32 pointIndex, int16 clipY2) + : StaticSprite(vm, 900), _field7A(-1) { + + int16 x = kScene1201PointArray[pointIndex].x; + int16 y = kScene1201PointArray[pointIndex].y; + if (x < 300) { + _spriteResource.load2(kScene1201TntFileHashList1[elemIndex]); + _x = _spriteResource.getPosition().x; + _y = _spriteResource.getPosition().y; + _drawRect.x = 0; + _drawRect.y = 0; + _drawRect.width = _spriteResource.getDimensions().width; + _drawRect.height = _spriteResource.getDimensions().height; + } else { + _spriteResource.load2(kScene1201TntFileHashList2[elemIndex]); + _x = x; + _y = y; + _drawRect.x = -(_spriteResource.getDimensions().width / 2); + _drawRect.y = -_spriteResource.getDimensions().height; + _drawRect.width = _spriteResource.getDimensions().width; + _drawRect.height = _spriteResource.getDimensions().height; + + } + createSurface(50, _spriteResource.getDimensions().width, _spriteResource.getDimensions().height); + setClipRect(0, 0, 640, clipY2); + _needRefresh = true; + StaticSprite::update(); +} + +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(0x02038314, _nameHash) && !getSubVar(0x02720344, _nameHash)) { + SetMessageHandler(&AsScene1201Tape::handleMessage); + } else { + setVisible(false); + SetMessageHandler(NULL); + } +} + +uint32 AsScene1201Tape::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + sendMessage(_parentScene, 0x4826, 0); + messageResult = 1; + break; + case 0x4806: + setSubVar(0x02038314, _nameHash, 1); + setVisible(false); + SetMessageHandler(NULL); + break; + } + return messageResult; +} + +Class466::Class466(NeverhoodEngine *vm, bool flag) + : AnimatedSprite(vm, 1200), _soundResource(vm) { + + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&Class466::handleMessage); + createSurface(10, 34, 149); + _x = 202; + _y = -32; + if (flag) { + sub40D380(); + } else { + sub40D340(); + } +} + +uint32 Class466::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x02060018) { + _soundResource.play(0x47900E06); + } + break; + case 0x2006: + sub40D360(); + break; + } + return messageResult; +} + +void Class466::sub40D340() { + setFileHash(0x928F0C10, 0, -1); + _newHashListIndex = 0; +} + +void Class466::sub40D360() { + setFileHash(0x928F0C10, 1, -1); + _newHashListIndex = -2; +} + +void Class466::sub40D380() { + setFileHash(0x928F0C10, 15, -1); + _newHashListIndex = -2; +} + +AsScene1201RightDoor::AsScene1201RightDoor(NeverhoodEngine *vm, Sprite *klayman, bool flag) + : AnimatedSprite(vm, 1100), _soundResource(vm), _klayman(klayman), _countdown(0) { + + createSurface1(0xD088AC30, 100); + _x = 320; + _y = 240; + SetUpdateHandler(&AsScene1201RightDoor::update); + SetMessageHandler(&AsScene1201RightDoor::handleMessage); + _newHashListIndex = -2; + if (flag) { + setFileHash(0xD088AC30, -1, -1); + _newHashListIndex = -2; + _countdown = 25; + } else { + stopAnimation(); + setVisible(false); + } +} + +void AsScene1201RightDoor::update() { + if (_countdown != 0 && (--_countdown == 0)) { + sub40D830(); + } + AnimatedSprite::update(); +} + +uint32 AsScene1201RightDoor::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + removeCallbacks(); + break; + case 0x4829: + sub40D7E0(); + break; + } + return messageResult; +} + +void AsScene1201RightDoor::sub40D7E0() { + setFileHash(0xD088AC30, 0, -1); + _newHashListIndex = -2; + setVisible(true); + _soundResource.play(calcHash("fxDoorOpen20")); +} + +void AsScene1201RightDoor::sub40D830() { + setFileHash(0xD088AC30, -1, -1); + _playBackwards = true; + setVisible(true); + _soundResource.play(calcHash("fxDoorClose20")); + NextState(&AsScene1201RightDoor::sub40D880); +} + +void AsScene1201RightDoor::sub40D880() { + stopAnimation(); + setVisible(false); +} + +Class464::Class464(NeverhoodEngine *vm) + : AnimatedSprite(vm, 1200) { + + createSurface(1200, 69, 98); + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&Class464::handleMessage); + SetSpriteCallback(&AnimatedSprite::updateDeltaXY); + setVisible(false); +} + +uint32 Class464::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2006: + _x = 436; + _y = 339; + setFileHash(0xA060C599, 0, -1); + setVisible(true); + break; + case 0x3002: + stopAnimation(); + setVisible(false); + removeCallbacks(); + break; + } + return messageResult; +} + +AsScene1201TntMan::AsScene1201TntMan(NeverhoodEngine *vm, Scene *parentScene, Sprite *class466, bool flag) + : AnimatedSprite(vm, 1100), _soundResource(vm), _parentScene(parentScene), _class466(class466), + _flag(false) { + + flag = false; + + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene1201TntMan::handleMessage); + createSurface(990, 106, 181); + _x = 201; + if (flag) { + _y = 297; + sub40CD60(); + } else { + _y = 334; + sub40CD30(); + } +} + +AsScene1201TntMan::~AsScene1201TntMan() { + // TODO Sound1ChList_sub_407AF0(0x01D00560); +} + +uint32 AsScene1201TntMan::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x092870C0) { + sendMessage(_class466, 0x2006, 0); + } else if (param.asInteger() == 0x11CA0144) { + _soundResource.play(0x51800A04); + } + break; + case 0x1011: + sendMessage(_parentScene, 0x2002, 0); + messageResult = 1; + case 0x480B: + if (!_flag) { + _sprite = (Sprite*)sender; + sub40CD90(); + } + break; + } + return messageResult; + +} + +uint32 AsScene1201TntMan::handleMessage40CCD0(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = AsScene1201TntMan::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + removeCallbacks(); + break; + } + return messageResult; +} + +void AsScene1201TntMan::spriteUpdate40CD10() { + _x = _sprite->getX() + 100; +} + +void AsScene1201TntMan::sub40CD30() { + setFileHash(0x654913D0, 0, -1); + SetMessageHandler(&AsScene1201TntMan::handleMessage); + SetSpriteCallback(NULL); +} + +void AsScene1201TntMan::sub40CD60() { + setFileHash(0x356803D0, 0, -1); + SetMessageHandler(&AsScene1201TntMan::handleMessage40CCD0); + SetSpriteCallback(&AnimatedSprite::updateDeltaXY); + NextState(&AsScene1201TntMan::sub40CD30); +} + +void AsScene1201TntMan::sub40CD90() { + // TODO Sound1ChList_addSoundResource(0x01D00560, 0x4B044624, true); + // TODO Sound1ChList_playLooping(0x4B044624); + _flag = true; + setFileHash(0x85084190, 0, -1); + SetMessageHandler(&AsScene1201TntMan::handleMessage); + SetSpriteCallback(&AsScene1201TntMan::spriteUpdate40CD10); + _newHashListIndex = -2; +} + +Class465::Class465(NeverhoodEngine *vm, Sprite *asTntMan) + : AnimatedSprite(vm, 1200), _asTntMan(asTntMan) { + + createSurface1(0x828C0411, 995); + SetUpdateHandler(&Class465::update); + SetMessageHandler(&Sprite::handleMessage); + SetSpriteCallback(&Class465::spriteUpdate40D150); + setFileHash(0x828C0411, 0, -1); + setVisible(false); +} + +Class465::~Class465() { + // TODO Sound1ChList_sub_407AF0(0x041080A4); +} + +void Class465::update() { + AnimatedSprite::update(); + if (getGlobalVar(0x20A0C516)) { + setVisible(true); + SetUpdateHandler(&AnimatedSprite::update); + // TODO Sound1ChList_addSoundResource(0x041080A4, 0x460A1050, true); + // TODO Sound1ChList_playLooping(0x460A1050); + } +} + +void Class465::spriteUpdate40D150() { + _x = _asTntMan->getX() - 18; + _y = _asTntMan->getY() - 158; +} + +AsScene1201Match::AsScene1201Match(NeverhoodEngine *vm, Scene *parentScene) + : AnimatedSprite(vm, 1100), _soundResource(vm), _parentScene(parentScene) { + + createSurface(1100, 57, 60); + SetUpdateHandler(&AsScene1201Match::update); + SetMessageHandler(&AsScene1201Match::handleMessage40C2D0); + SetSpriteCallback(&AnimatedSprite::updateDeltaXY); + + switch (getGlobalVar(0x0112090A)) { + case 0: + _x = 521; + _y = 112; + _status = 0; + sub40C4C0(); + break; + case 1: + _x = 521; + _y = 112; + _status = 2; + sub40C470(); + _soundResource.load(0xD00230CD); + break; + case 2: + setDoDeltaX(1); + _x = 403; + _y = 337; + _status = 0; + sub40C4F0(); + break; + } +} + +void AsScene1201Match::update() { + if (_countdown != 0 && (--_countdown == 0)) { + removeCallbacks(); + } + updateAnim(); + handleSpriteUpdate(); + updatePosition(); +} + +uint32 AsScene1201Match::handleMessage40C2D0(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x86668011) { + _soundResource.play(); + } + break; + } + return messageResult; +} + +uint32 AsScene1201Match::handleMessage40C320(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = handleMessage40C2D0(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + removeCallbacks(); + break; + } + return messageResult; +} + +uint32 AsScene1201Match::handleMessage40C360(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = handleMessage40C2D0(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + sendMessage(_parentScene, 0x2001, 0); + messageResult = 1; + break; + case 0x4806: + setVisible(false); + setGlobalVar(0x0112090A, 3); + break; + } + return messageResult; +} + +void AsScene1201Match::sub40C3E0() { + setFileHash(0x00842374, 0, -1); + SetMessageHandler(&AsScene1201Match::handleMessage40C320); + if (_status == 0) { + NextState(&AsScene1201Match::sub40C420); + } else { + NextState(&AsScene1201Match::sub40C470); + } +} + +void AsScene1201Match::sub40C420() { + setGlobalVar(0x0112090A, 2); + _x -= 199; + _y += 119; + setFileHash(0x018D0240, 0, -1); + SetMessageHandler(&AsScene1201Match::handleMessage40C320); + NextState(&AsScene1201Match::sub40C4F0); +} + +void AsScene1201Match::sub40C470() { + setFileHash(0x00842374, 0, -1); + SetMessageHandler(&AsScene1201Match::handleMessage40C2D0); + _newHashListIndex = 0; + if (_status != 0) { + _countdown = 36; + _status--; + NextState(&AsScene1201Match::sub40C3E0); + } +} + +void AsScene1201Match::sub40C4C0() { + setFileHash(0x00842374, 0, -1); + SetMessageHandler(&AsScene1201Match::handleMessage40C360); + _newHashListIndex = 0; +} + +void AsScene1201Match::sub40C4F0() { + setDoDeltaX(1); + _x = 403; + _y = 337; + setFileHash(0x00842374, 0, -1); + SetMessageHandler(&AsScene1201Match::handleMessage40C360); + _newHashListIndex = 0; +} + +AsScene1201Creature::AsScene1201Creature(NeverhoodEngine *vm, Scene *parentScene, Sprite *klayman) + : AnimatedSprite(vm, 900), _soundResource(vm), _parentScene(parentScene), _klayman(klayman), + _flag(false) { + + createSurface(1100, 203, 199); + SetUpdateHandler(&AsScene1201Creature::update); + SetMessageHandler(&AsScene1201Creature::handleMessage40C710); + _x = 540; + _y = 320; + sub40C8E0(); + _countdown3 = 2; +} + +void AsScene1201Creature::update() { + bool oldFlag = _flag; + _flag = _x >= 385; + if (_flag != oldFlag) + sub40C8E0(); + if (_countdown1 != 0 && (--_countdown1 == 0)) { + removeCallbacks(); + } + updateAnim(); + handleSpriteUpdate(); + updatePosition(); +} + +uint32 AsScene1201Creature::handleMessage40C710(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x02060018) { + _soundResource.play(0xCD298116); + } + break; + case 0x2004: + GotoState(&AsScene1201Creature::sub40C960); + break; + case 0x2006: + GotoState(&AsScene1201Creature::sub40C9B0); + break; + } + return messageResult; +} + +uint32 AsScene1201Creature::handleMessage40C7B0(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = handleMessage40C710(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x02421405) { + if (_countdown2 != 0 && (--_countdown2 == 0)) { + sub40C990(); + } + } + break; + case 0x3002: + removeCallbacks(); + break; + } + return messageResult; +} + +uint32 AsScene1201Creature::handleMessage40C830(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x02060018) { + _soundResource.play(0xCD298116); + sendMessage(_parentScene, 0x4814, 0); + sendMessage(_klayman, 0x4814, 0); + } + break; + case 0x3002: + removeCallbacks(); + break; + } + return messageResult; +} + +void AsScene1201Creature::sub40C8E0() { + _countdown3--; + if (_countdown3 == 0) + _countdown3 = 3; + setFileHash(0x08081513, 0, -1); + SetMessageHandler(&AsScene1201Creature::handleMessage40C710); + NextState(&AsScene1201Creature::sub40C930); + _countdown1 = 36; +} + +void AsScene1201Creature::sub40C930() { + if (!_flag) { + setFileHash(0xCA287133, 0, -1); + SetMessageHandler(&AsScene1201Creature::handleMessage40C7B0); + NextState(&AsScene1201Creature::sub40C8E0); + } +} + +void AsScene1201Creature::sub40C960() { + setFileHash(0x08081513, 0, -1); + SetMessageHandler(&AsScene1201Creature::handleMessage40C710); + NextState(&AsScene1201Creature::sub40C9E0); + _countdown1 = 48; +} + +void AsScene1201Creature::sub40C990() { + setFileHash2(0x0B6E13FB, 0x01084280, 0); +} + +void AsScene1201Creature::sub40C9B0() { + setFileHash(0xCA287133, 0, -1); + SetMessageHandler(&AsScene1201Creature::handleMessage40C830); + NextState(&AsScene1201Creature::sub40C8E0); + _countdown1 = 0; +} + +void AsScene1201Creature::sub40C9E0() { + setFileHash(0x5A201453, 0, -1); + SetMessageHandler(&AsScene1201Creature::handleMessage40C710); + _countdown1 = 0; +} + +AsScene1201LeftDoor::AsScene1201LeftDoor(NeverhoodEngine *vm, Sprite *klayman) + : AnimatedSprite(vm, 1100), _soundResource(vm), _klayman(klayman) { + + _x = 320; + _y = 240; + createSurface(800, 55, 199); + if (_klayman->getX() < 100) { + setFileHash(0x508A111B, 0, -1); + _newHashListIndex = -2; + _soundResource.play(calcHash("fxDoorOpen03")); + } else { + setFileHash(0x508A111B, -1, -1); + _newHashListIndex = -2; + } + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene1201LeftDoor::handleMessage); +} + +uint32 AsScene1201LeftDoor::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x4809: + sub40D590(); + break; + } + return messageResult; +} + +void AsScene1201LeftDoor::sub40D590() { + setFileHash(0x508A111B, -1, -1); + _playBackwards = true; + _newHashListIndex = 0; +} + +Scene1201::Scene1201(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule, true), _flag(false), _asMatch(NULL), _asTntMan(NULL), + _asCreature(NULL), _class466(NULL), _asLeftDoor(NULL), _asRightDoor(NULL), _asTape(NULL) { + + int16 topY1, topY2, topY3, topY4; + int16 x1, x2; + Sprite *tempSprite, *class464; + + SetUpdateHandler(&Scene1201::update); + SetMessageHandler(&Scene1201::handleMessage); + + setHitRects(0x004AEBD0); + + _surfaceFlag = true; + + if (!getSubVar(0x40050052, 0xE8058B52)) { + setSubVar(0x40050052, 0xE8058B52, 1); + for (uint32 index = 0; index < 18; index++) { + setSubVar(0x10055D14, index, kScene1201InitArray[index]); + } + } + + insertMouse433(0x9A2C0409); + + _asTape = insertSprite<AsScene1201Tape>(this, 3, 1100, 243, 340, 0x9148A011); + _vm->_collisionMan->addSprite(_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; + + _class466 = insertSprite<Class466>(getGlobalVar(0x000CF819) && which != 1); + _class466->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; + + class464 = insertSprite<Class464>(); + + debug("Scene1201: which = %d", which); + + if (which < 0) { + insertKlayman<KmScene1201>(364, 333, class464); + setMessageList(0x004AEC08); + } else if (which == 3) { + insertKlayman<KmScene1201>(400, 329, class464); + setMessageList(0x004AEC08); + } else if (which == 2) { + if (getGlobalVar(0x0A310817) && !getGlobalVar(0x0A18CA33)) { + insertKlayman<KmScene1201>(374, 333, class464); + setMessageList(0x004AEC08); + } else { + insertKlayman<KmScene1201>(640, 329, class464); + setMessageList(0x004AEC20); + } + } else if (which == 1) { + if (getGlobalVar(0xC0418A02)) { + insertKlayman<KmScene1201>(364, 333, class464); + _klayman->setDoDeltaX(1); + } else { + insertKlayman<KmScene1201>(246, 333, class464); + } + setMessageList(0x004AEC30); + } else { + insertKlayman<KmScene1201>(0, 336, class464); + setMessageList(0x004AEC10); + } + + _klayman->setClipRect(x1, 0, x2, 480); + _klayman->setRepl(64, 0); + + if (getGlobalVar(0x0A310817) && !getGlobalVar(0x0A18CA33)) { + setBackground(0x4019A2C4); + setPalette(0x4019A2C4); + _asRightDoor = NULL; + } else { + setBackground(0x40206EC5); + setPalette(0x40206EC5); + _asRightDoor = insertSprite<AsScene1201RightDoor>(_klayman, which == 2); + } + + if (getGlobalVar(0x000CF819)) { + insertStaticSprite(0x10002ED8, 500); + if (!getGlobalVar(0x0A18CA33)) { + _asTntMan = insertSprite<AsScene1201TntMan>(this, _class466, which == 1); + _asTntMan->setClipRect(x1, 0, x2, 480); + _asTntMan->setRepl(64, 0); + _vm->_collisionMan->addSprite(_asTntMan); + tempSprite = insertSprite<Class465>(_asTntMan); + tempSprite->setClipRect(x1, 0, x2, 480); + } + + uint32 tntIndex = 1; + while (tntIndex < 18) { + uint32 elemIndex = getSubVar(0x10055D14, 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(0x10055D14, tntIndex), clipY2); + elemIndex = getSubVar(0x10055D14, 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(0x10055D14, tntIndex + 1), clipY2); + tntIndex += 3; + } + + if (getGlobalVar(0x0A310817) && !getGlobalVar(0x0A18CA33)) { + setRectList(0x004AEE58); + } else { + setRectList(0x004AEDC8); + } + + } else { + + insertStaticSprite(0x8E8A1981, 900); + + uint32 tntIndex = 0; + while (tntIndex < 18) { + uint32 elemIndex = getSubVar(0x10055D14, 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(0x10055D14, tntIndex), clipY2); + tntIndex++; + } + + if (getGlobalVar(0x0A310817) && !getGlobalVar(0x0A18CA33)) { + setRectList(0x004AEE18); + } else { + setRectList(0x004AED88); + } + + } + + tempSprite = insertStaticSprite(0x63D400BC, 900); + + _asLeftDoor = insertSprite<AsScene1201LeftDoor>(_klayman); + _asLeftDoor->setClipRect(x1, tempSprite->getDrawRect().y, tempSprite->getDrawRect().x2(), 480); + + if (getGlobalVar(0x0A310817) && ! getGlobalVar(0x0112090A)) { + setGlobalVar(0x0112090A, 1); + } + + _asMatch = NULL; + + if (getGlobalVar(0x0112090A) < 3) { + _asMatch = insertSprite<AsScene1201Match>(this); + _vm->_collisionMan->addSprite(_asMatch); + } + + if (getGlobalVar(0x0A310817) && !getGlobalVar(0x0A18CA33)) { + _asCreature = insertSprite<AsScene1201Creature>(this, _klayman); + _asCreature->setClipRect(x1, 0, x2, 480); + } + +} + +Scene1201::~Scene1201() { +} + +void Scene1201::update() { + Scene::update(); + if (_asMatch && getGlobalVar(0x0112090A)) { + deleteSprite(&_asMatch); + } +} + +uint32 Scene1201::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x07053000) { + _flag = true; + sendMessage(_asCreature, 0x2004, 0); + } else if (param.asInteger() == 0x140E5744) { + sendMessage(_asCreature, 0x2005, 0); + } else if (param.asInteger() == 0x40253C40) { + _messageListFlag = false; + sendMessage(_asCreature, 0x2006, 0); + } else if (param.asInteger() == 0x090EB048) { + if (_klayman->getX() < 572) { + setMessageList2(0x004AEC90); + } else { + setMessageList2(0x004AEC20); + } + } + break; + case 0x2001: + if (!getGlobalVar(0x0112090A)) { + setMessageList2(0x004AECB0); + } else { + sendEntityMessage(_klayman, 0x1014, _asMatch); + setMessageList2(0x004AECC0); + } + break; + case 0x2002: + if (getGlobalVar(0x20A0C516)) { + sendEntityMessage(_klayman, 0x1014, _asTntMan); + setMessageList2(0x004AECF0, false); + } else if (getGlobalVar(0x0112090A) == 3) { + sendEntityMessage(_klayman, 0x1014, _asTntMan); + if (_klayman->getX() > _asTntMan->getX()) { + setMessageList(0x004AECD0); + } else { + setMessageList(0x004AECE0); + } + } + break; + case 0x4814: + messageList402220(); + break; + case 0x4826: + if (sender == _asTape) { + sendEntityMessage(_klayman, 0x1014, _asTape); + setMessageList(0x004AED38); + } + break; + case 0x4829: + sendMessage(_asRightDoor, 0x4829, 0); + break; + } + return messageResult; +} + +// Scene1202 + +static const uint32 kScene1202Table[] = { + 1, 2, 0, 4, 5, 3, 7, 8, 6, 10, 11, 9, 13, 14, 12, 16, 17, 15 +}; + +static const NPoint kScene1202Points[] = { + {203, 140}, + {316, 212}, + {277, 264}, + {176, 196}, + {275, 159}, + {366, 212}, + {230, 195}, + {412, 212}, + {368, 263}, + {204, 192}, + {365, 164}, + {316, 262}, + {191, 255}, + {280, 213}, + {406, 266}, + {214, 254}, + {316, 158}, + {402, 161} +}; + +static const uint32 kScene1202FileHashes[] = { + 0x1AC00B8, + 0x1AC14B8, + 0x1AC14B8, + 0x1AC30B8, + 0x1AC14B8, + 0x1AC14B8, + 0x1AC00B8, + 0x1AC14B8, + 0x1AC14B8, + 0x1AC90B8, + 0x1AC18B8, + 0x1AC18B8, + 0x1AC30B8, + 0x1AC14B8, + 0x1AC14B8, + 0x1AC50B8, + 0x1AC14B8, + 0x1AC14B8 +}; + +AsScene1202TntItem::AsScene1202TntItem(NeverhoodEngine *vm, Scene *parentScene, int index) + : AnimatedSprite(vm, 900), _parentScene(parentScene), _index(index) { + + int positionIndex; + + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene1202TntItem::handleMessage453FE0); + positionIndex = getSubVar(0x10055D14, _index); + createSurface(900, 37, 67); + _x = kScene1202Points[positionIndex].x; + _y = kScene1202Points[positionIndex].y; + sub4540A0(); +} + +uint32 AsScene1202TntItem::handleMessage453FE0(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + sendMessage(_parentScene, 0x2000, _index); + messageResult = 1; + break; + case 0x2001: + _index2 = (int)param.asInteger(); + sub4540D0(); + break; + } + return messageResult; +} + +uint32 AsScene1202TntItem::handleMessage454060(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + removeCallbacks(); + break; + } + return messageResult; +} + +void AsScene1202TntItem::sub4540A0() { + setFileHash(kScene1202FileHashes[_index], 0, -1); + SetMessageHandler(&AsScene1202TntItem::handleMessage453FE0); + _newHashListIndex = 0; +} + +void AsScene1202TntItem::sub4540D0() { + setFileHash(kScene1202FileHashes[_index], 0, -1); + SetMessageHandler(&AsScene1202TntItem::handleMessage454060); + NextState(&AsScene1202TntItem::sub454100); +} + +void AsScene1202TntItem::sub454100() { + _x = kScene1202Points[_index2].x; + _y = kScene1202Points[_index2].y; + setFileHash(kScene1202FileHashes[_index], 6, -1); + SetMessageHandler(&AsScene1202TntItem::handleMessage454060); + NextState(&AsScene1202TntItem::sub454160); + _playBackwards = true; +} + +void AsScene1202TntItem::sub454160() { + sendMessage(_parentScene, 0x2002, _index); + sub4540A0(); +} + +Scene1202::Scene1202(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule, true), _paletteResource(vm), _soundResource1(vm), + _soundResource2(vm), _soundResource3(vm), _soundResource4(vm), + _flag(true), _soundFlag(false), _counter(0), _index(-1) { + + SetMessageHandler(&Scene1202::handleMessage453C10); + SetUpdateHandler(&Scene1202::update); + + _surfaceFlag = true; + + setBackground(0x60210ED5); + + setPalette(0x60210ED5); + addEntity(_palette); + + _paletteResource.load(0x60250EB5); + _paletteResource.copyPalette(_paletteData); + + insertMouse435(0x10ED160A, 20, 620); + + for (int i = 0; i < 18; i++) { + _asTntItems[i] = insertSprite<AsScene1202TntItem>(this, i); + _vm->_collisionMan->addSprite(_asTntItems[i]); + } + + insertStaticSprite(0x8E8419C1, 1100); + + if (getGlobalVar(0x000CF819)) { + SetMessageHandler(&Scene1202::handleMessage453D90); + } + + _soundResource1.play(0x40106542); + _soundResource2.load(0x40005446); + _soundResource2.load(0x40005446); + _soundResource2.load(0x68E25540); + +} + +Scene1202::~Scene1202() { + if (isSolved()) { + setGlobalVar(0x000CF819, 1); + } +} + +void Scene1202::update() { + Scene::update(); + if (_soundFlag) { + if (!_soundResource4.isPlaying()) + leaveScene(0); + } else if (_counter == 0 && isSolved()) { + SetMessageHandler(&Scene1202::handleMessage453D90); + setGlobalVar(0x000CF819, 1); + doPaletteEffect(); + _soundResource4.play(); + _soundFlag = true; + } else if (_index >= 0 && _counter == 0) { + int index2 = kScene1202Table[_index]; + sendMessage(_asTntItems[_index], 0x2001, getSubVar(0x10055D14, index2)); + sendMessage(_asTntItems[index2], 0x2001, getSubVar(0x10055D14, _index)); + int temp = getSubVar(0x10055D14, index2); + setSubVar(0x10055D14, index2, getSubVar(0x10055D14, _index)); + setSubVar(0x10055D14, _index, temp); + _counter = 2; + _index = -1; + if (_flag) { + _soundResource2.play(); + } else { + _soundResource3.play(); + } + _flag = !_flag; + } +} + +uint32 Scene1202::handleMessage453C10(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = 0; + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x0001: + // TODO: Debug/Cheat stuff + if ((param.asPoint().x <= 20 || param.asPoint().x >= 620) && !_soundFlag) { + leaveScene(0); + } + break; + case 0x000D: + if (param.asInteger() == 0x14210006) { + // TODO: Debug/Cheat stuff + messageResult = 1; + } + break; + case 0x2000: + _index = (int)param.asInteger(); + break; + case 0x2002: + _counter--; + break; + } + return messageResult; +} + +uint32 Scene1202::handleMessage453D90(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x0001: + if (param.asPoint().x <= 20 || param.asPoint().x >= 620) { + leaveScene(0); + } + break; + } + return 0; +} + +bool Scene1202::isSolved() { + return + getSubVar(0x10055D14, 0) == 0 && getSubVar(0x10055D14, 3) == 3 && + getSubVar(0x10055D14, 6) == 6 && getSubVar(0x10055D14, 9) == 9 && + getSubVar(0x10055D14, 12) == 12 && getSubVar(0x10055D14, 15) == 15; +} + +void Scene1202::doPaletteEffect() { +#if 0 // TODO + Palette2 *palette2 = (Palette2*)_palette; + palette2->startFadeToPalette(24); +#endif +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/module1200.h b/engines/neverhood/module1200.h new file mode 100644 index 0000000000..dc8e903472 --- /dev/null +++ b/engines/neverhood/module1200.h @@ -0,0 +1,234 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef NEVERHOOD_MODULE1200_H +#define NEVERHOOD_MODULE1200_H + +#include "neverhood/neverhood.h" +#include "neverhood/module.h" +#include "neverhood/scene.h" + +namespace Neverhood { + +// Module1200 + +class Module1200 : public Module { +public: + Module1200(NeverhoodEngine *vm, Module *parentModule, int which); + virtual ~Module1200(); +protected: + void createScene(int sceneNum, int which); + void updateScene(); +}; + +// Scene1201 + +class AsScene1201Tape : public AnimatedSprite { +public: + AsScene1201Tape(NeverhoodEngine *vm, Scene *parentScene, uint32 nameHash, int surfacePriority, int16 x, int16 y, uint32 fileHash); +protected: + Scene *_parentScene; + uint32 _nameHash; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Class466 : public AnimatedSprite { +public: + Class466(NeverhoodEngine *vm, bool flag); +protected: + SoundResource _soundResource; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void sub40D340(); + void sub40D360(); + void sub40D380(); +}; + +class AsScene1201RightDoor : public AnimatedSprite { +public: + AsScene1201RightDoor(NeverhoodEngine *vm, Sprite *klayman, bool flag); +protected: + SoundResource _soundResource; + Sprite *_klayman; + int _countdown; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void sub40D7E0(); + void sub40D830(); + void sub40D880(); +}; + +class Class464 : public AnimatedSprite { +public: + Class464(NeverhoodEngine *vm); +protected: + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class AsScene1201TntMan : public AnimatedSprite { +public: + AsScene1201TntMan(NeverhoodEngine *vm, Scene *parentScene, Sprite *class466, bool flag); + virtual ~AsScene1201TntMan(); +protected: + Scene *_parentScene; + Sprite *_class466; + Sprite *_sprite; + SoundResource _soundResource; + bool _flag; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 handleMessage40CCD0(int messageNum, const MessageParam ¶m, Entity *sender); + void spriteUpdate40CD10(); + void sub40CD30(); + void sub40CD60(); + void sub40CD90(); +}; + +class Class465 : public AnimatedSprite { +public: + Class465(NeverhoodEngine *vm, Sprite *asTntMan); + ~Class465(); +protected: + Sprite *_asTntMan; + void update(); + void spriteUpdate40D150(); +}; + +class AsScene1201Match : public AnimatedSprite { +public: + AsScene1201Match(NeverhoodEngine *vm, Scene *parentScene); +protected: + Scene *_parentScene; + SoundResource _soundResource; + int _countdown; + int _status; + void update(); + uint32 handleMessage40C2D0(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 handleMessage40C320(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 handleMessage40C360(int messageNum, const MessageParam ¶m, Entity *sender); + void sub40C3E0(); + void sub40C420(); + void sub40C470(); + void sub40C4C0(); + void sub40C4F0(); +}; + +class AsScene1201Creature : public AnimatedSprite { +public: + AsScene1201Creature(NeverhoodEngine *vm, Scene *parentScene, Sprite *klayman); +protected: + Scene *_parentScene; + Sprite *_klayman; + SoundResource _soundResource; + int _countdown1; + int _countdown2; + int _countdown3; + bool _flag; + void update(); + uint32 handleMessage40C710(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 handleMessage40C7B0(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 handleMessage40C830(int messageNum, const MessageParam ¶m, Entity *sender); + void sub40C8E0(); + void sub40C930(); + void sub40C960(); + void sub40C990(); + void sub40C9B0(); + void sub40C9E0(); +}; + +class AsScene1201LeftDoor : public AnimatedSprite { +public: + AsScene1201LeftDoor(NeverhoodEngine *vm, Sprite *klayman); +protected: + Sprite *_klayman; + SoundResource _soundResource; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void sub40D590(); +}; + +class SsScene1201Tnt : public StaticSprite { +public: + SsScene1201Tnt(NeverhoodEngine *vm, uint32 elemIndex, uint32 pointIndex, int16 clipY2); +protected: + uint32 _elemIndex; + int16 _field7A; +}; + +class Scene1201 : public Scene { +public: + Scene1201(NeverhoodEngine *vm, Module *parentModule, int which); + virtual ~Scene1201(); +protected: + // TODO ResourceTable _resourceTable1; + // TODO ResourceTable _resourceTable2; + Sprite *_asMatch; + AsScene1201TntMan *_asTntMan; + Sprite *_asCreature; + Sprite *_class466; + Sprite *_asLeftDoor; + Sprite *_asRightDoor; + Sprite *_asTape; + bool _flag; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +// Scene1202 + +class AsScene1202TntItem : public AnimatedSprite { +public: + AsScene1202TntItem(NeverhoodEngine *vm, Scene *parentScene, int index); +protected: + Scene *_parentScene; + int _index, _index2; + uint32 handleMessage453FE0(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 handleMessage454060(int messageNum, const MessageParam ¶m, Entity *sender); + void sub4540A0(); + void sub4540D0(); + void sub454100(); + void sub454160(); +}; + +class Scene1202 : public Scene { +public: + Scene1202(NeverhoodEngine *vm, Module *parentModule, int which); + virtual ~Scene1202(); +protected: + SoundResource _soundResource1; + SoundResource _soundResource2; + SoundResource _soundResource3; + SoundResource _soundResource4; + PaletteResource _paletteResource; + Sprite *_asTntItems[18]; + int _counter; + int _index; + byte _paletteData[1024]; + bool _soundFlag; + bool _flag; + void update(); + uint32 handleMessage453C10(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 handleMessage453D90(int messageNum, const MessageParam ¶m, Entity *sender); + bool isSolved(); + void doPaletteEffect(); +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_MODULE1200_H */ diff --git a/engines/neverhood/module1300.cpp b/engines/neverhood/module1300.cpp new file mode 100644 index 0000000000..cb5ed15d99 --- /dev/null +++ b/engines/neverhood/module1300.cpp @@ -0,0 +1,1928 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "neverhood/module1300.h" +#include "neverhood/module1000.h" +#include "neverhood/module1200.h" +#include "neverhood/module1400.h" +#include "neverhood/module2200.h" +#include "neverhood/gamemodule.h" +#include "neverhood/diskplayerscene.h" +#include "neverhood/navigationscene.h" +#include "neverhood/smackerscene.h" + +namespace Neverhood { + +Module1300::Module1300(NeverhoodEngine *vm, Module *parentModule, int which) + : Module(vm, parentModule) { + + // TODO Music18hList_add(0x61C090, 0x203197); + // TODO Sound1ChList_addSoundResources(0x61C090, dword_4B2868, true); + // TODO Sound1ChList_setSoundValuesMulti(dword_4B2868, 0, 50, 600, 20, 150); + // TODO Sound1ChList_sub_407C70(0x61C090, 0x48498E46, 0x50399F64, 0); + // TODO Sound1ChList_setVolume(0x48498E46, 70); + // TODO Sound1ChList_setVolume(0x50399F64, 70); + + if (which < 0) { + if (_vm->gameState().sceneNum >= 1 && _vm->gameState().sceneNum <= 17) + createScene(_vm->gameState().sceneNum, -1); + else + createScene(11, 0); + } else { + switch (which) { + case 0: + createScene(11, 0); + break; + case 1: + createScene(13, 0); + break; + case 2: + createScene(14, 0); + break; + case 3: + createScene(15, 0); + break; + case 4: + createScene(7, 0); + break; + case 5: + createScene(5, 1); + break; + case 6: + createScene(5, 5); + break; + case 7: + createScene(3, 0); + break; + case 8: + createScene(1, 0); + break; + case 9: + createScene(2, 0); + break; + case 10: + createScene(6, 0); + break; + case 11: + createScene(4, 0); + break; + default: + createScene(12, 0); + break; + } + } + +} + +Module1300::~Module1300() { + // TODO Sound1ChList_sub_407A50(0x61C090); +} + +void Module1300::createScene(int sceneNum, int which) { + debug("Module1300::createScene(%d, %d)", sceneNum, which); + _vm->gameState().sceneNum = sceneNum; + switch (_vm->gameState().sceneNum) { + case 1: + // TODO Sound1ChList_setSoundValuesMulti(dword_4B2868, false, 0, 0, 0, 0); + // TODO Music18hList_play(0x203197, 0, 2, 1); + _childObject = new Scene1302(_vm, this, which); + break; + case 2: + // TODO Sound1ChList_setSoundValuesMulti(dword_4B2868, false, 0, 0, 0, 0); + // TODO Music18hList_stop(0x203197, 0, 2); + _childObject = new Scene1303(_vm, this, which); + break; + case 3: + // TODO Sound1ChList_setSoundValuesMulti(dword_4B2868, false, 0, 0, 0, 0); + // TODO Music18hList_stop(0x203197, 0, 2); + _childObject = new Scene1304(_vm, this, which); + break; + case 4: + // TODO Sound1ChList_setSoundValuesMulti(dword_4B2868, false, 0, 0, 0, 0); + // TODO Music18hList_play(0x203197, 0, 2, 1); + _childObject = new Scene1305(_vm, this, which); + break; + case 5: + // TODO Sound1ChList_setSoundValuesMulti(dword_4B2868, false, 0, 0, 0, 0); + // TODO Music18hList_play(0x203197, 0, 2, 1); + _childObject = new Scene1306(_vm, this, which); + break; + case 6: + // TODO Sound1ChList_setSoundValuesMulti(dword_4B2868, false, 0, 0, 0, 0); + // TODO Music18hList_play(0x203197, 0, 2, 1); + _childObject = new Scene1307(_vm, this, which); + break; + case 7: + // TODO Sound1ChList_setSoundValuesMulti(dword_4B2868, false, 0, 0, 0, 0); + // TODO Music18hList_play(0x203197, 0, 2, 1); + _childObject = new Scene1308(_vm, this, which); + break; + case 8: + // TODO Sound1ChList_setSoundValuesMulti(dword_4B2868, false, 0, 0, 0, 0); + // TODO Music18hList_stop(0x203197, 0, 2); + _childObject = new DiskplayerScene(_vm, this, 1); + break; + case 9: + // TODO Sound1ChList_setSoundValuesMulti(dword_4B2868, false, 0, 0, 0, 0); + // TODO Music18hList_stop(0x203197, 0, 2); + createSmackerScene(0x20082818, true, true, false); + break; + case 10: + // TODO Sound1ChList_setSoundValuesMulti(dword_4B2868, false, 0, 0, 0, 0); + // TODO Music18hList_stop(0x203197, 0, 2); + createSmackerScene(0x20082828, true, true, false); + break; + case 11: + // TODO Sound1ChList_setSoundValuesMulti(0xdword_4B2868, true, 0, 0, 0, 0); + // TODO Music18hList_stop(0x203197, 0, 2); + createNavigationScene(0x004B27A8, which); + break; + case 12: + // TODO Sound1ChList_setSoundValuesMulti(0xdword_4B2868, true, 0, 0, 0, 0); + // TODO Music18hList_stop(0x203197, 0, 2); + createNavigationScene(0x004B2718, which); + break; + case 13: + // TODO Sound1ChList_setSoundValuesMulti(0xdword_4B2868, true, 0, 0, 0, 0); + // TODO Music18hList_stop(0x203197, 0, 2); + createNavigationScene(0x004B27D8, which); + break; + case 14: + // TODO Sound1ChList_setSoundValuesMulti(0xdword_4B2868, true, 0, 0, 0, 0); + // TODO Music18hList_stop(0x203197, 0, 2); + createNavigationScene(0x004B2808, which); + break; + case 15: + // TODO Sound1ChList_setSoundValuesMulti(0xdword_4B2868, true, 0, 0, 0, 0); + // TODO Music18hList_stop(0x203197, 0, 2); + createNavigationScene(0x004B2838, which); + break; + case 16: + // TODO Sound1ChList_setSoundValuesMulti(dword_4B2868, false, 0, 0, 0, 0); + // TODO Music18hList_stop(0x203197, 0, 2); + _childObject = new Scene1317(_vm, this, which); + break; + case 17: + // TODO: Credits scene + break; + } + SetUpdateHandler(&Module1300::updateScene); + _childObject->handleUpdate(); +} + +void Module1300::updateScene() { + if (!updateChild()) { + switch (_vm->gameState().sceneNum) { + case 1: + if (_moduleResult == 1) { + createScene(4, 0); + } else { + createScene(7, 1); + } + break; + case 2: + createScene(5, 3); + break; + case 3: + createScene(15, 0); + break; + case 4: + createScene(16, -1); + break; + case 5: + if (_moduleResult == 2) { + createScene(8, 0); + } else if (_moduleResult == 3) { + createScene(2, 0); + } else if (_moduleResult == 0) { + leaveModule(0); + } else if (_moduleResult == 1) { + createScene(10, -1); + } + break; + case 6: + createScene(7, 2); + break; + case 7: + if (_moduleResult == 0) { + createScene(13, 0); + } else if (_moduleResult == 1) { + createScene(1, 0); + } else if (_moduleResult == 2) { + createScene(6, 0); + } + break; + case 8: + createScene(5, 2); + break; + case 9: + createScene(5, 0); + break; + case 10: + createScene(14, 0); + break; + case 11: + if (_moduleResult == 0) + createScene(12, 0); + else if (_moduleResult == 1) + createScene(11, 1); + break; + case 12: + if (_moduleResult == 0) + createScene(14, 1); + else if (_moduleResult == 1) + createScene(15, 1); + else if (_moduleResult == 3) + createScene(11, 1); + else if (_moduleResult == 5) + createScene(13, 1); + break; + case 13: + if (_moduleResult == 0) { + createScene(12, 2); + } else if (_moduleResult == 1) { + createScene(7, 0); + } + break; + case 14: + if (_moduleResult == 0) { + createScene(12, 3); + } else if (_moduleResult == 1) { + createScene(9, -1); + } + break; + case 15: + if (_moduleResult == 0) { + createScene(12, 4); + } else if (_moduleResult == 1) { + createScene(3, 0); + } + break; + case 16: + createScene(17, -1); + break; + case 17: + // TODO + break; + } + } +} + +AsScene1302Bridge::AsScene1302Bridge(NeverhoodEngine *vm, Scene *parentScene) + : AnimatedSprite(vm, 1100), _soundResource1(vm), _soundResource2(vm), _parentScene(parentScene) { + + _x = 320; + _y = 240; + createSurface1(0x88148150, 500); + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene1302Bridge::handleMessage); + if (!getGlobalVar(0x13206309)) { + setFileHash(0x88148150, 0, -1); + _newHashListIndex = 0; + } else { + setFileHash(0x88148150, -1, -1); + _newHashListIndex = -2; + } + _soundResource1.load(0x68895082); + _soundResource2.load(0x689BD0C1); +} + +uint32 AsScene1302Bridge::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + removeCallbacks(); + break; + case 0x4808: + stLowerBridge(); + break; + case 0x4809: + stRaiseBridge(); + break; + } + return messageResult; +} + +void AsScene1302Bridge::stLowerBridge() { + setFileHash(0x88148150, 0, -1); + NextState(&AsScene1302Bridge::cbLowerBridgeEvent); + _soundResource2.play(); +} + +void AsScene1302Bridge::stRaiseBridge() { + setFileHash(0x88148150, 7, -1); + _playBackwards = true; + _newHashListIndex = 0; + _soundResource1.play(); +} + +void AsScene1302Bridge::cbLowerBridgeEvent() { + sendMessage(_parentScene, 0x2032, 0); + setFileHash(0x88148150, -1, -1); + _newHashListIndex = -2; +} + +SsScene1302Fence::SsScene1302Fence(NeverhoodEngine *vm) + : StaticSprite(vm, 0x11122122, 200), _soundResource1(vm), _soundResource2(vm) { + + SetUpdateHandler(&SsScene1302Fence::update); + SetMessageHandler(&SsScene1302Fence::handleMessage); + SetSpriteCallback(NULL); + _firstY = _y; + if (getGlobalVar(0x80101B1E)) + _y += 152; + _soundResource1.load(0x7A00400C); + _soundResource2.load(0x78184098); +} + +void SsScene1302Fence::update() { + handleSpriteUpdate(); + StaticSprite::update(); +} + +uint32 SsScene1302Fence::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x4808: + _soundResource1.play(); + SetSpriteCallback(&SsScene1302Fence::suMoveDown); + SetMessageHandler(NULL); + break; + case 0x4809: + _soundResource2.play(); + SetSpriteCallback(&SsScene1302Fence::suMoveUp); + SetMessageHandler(NULL); + break; + } + return messageResult; +} + +void SsScene1302Fence::suMoveDown() { + if (_y < _firstY + 152) + _y += 8; + else { + SetMessageHandler(&SsScene1302Fence::handleMessage); + SetSpriteCallback(NULL); + } +} + +void SsScene1302Fence::suMoveUp() { + if (_y > _firstY) + _y -= 8; + else { + SetMessageHandler(&SsScene1302Fence::handleMessage); + SetSpriteCallback(NULL); + } +} + +Class595::Class595(NeverhoodEngine *vm, Scene *parentScene) + : StaticSprite(vm, 0xB0420130, 1015), _parentScene(parentScene) { + + SetMessageHandler(&Class595::handleMessage); +} + +uint32 Class595::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x482A: + sendMessage(_parentScene, 0x1022, 995); + break; + case 0x482B: + sendMessage(_parentScene, 0x1022, 1015); + break; + } + return messageResult; +} + +Scene1302::Scene1302(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule, true), _soundResource(vm) { + + SetMessageHandler(&Scene1302::handleMessage); + setHitRects(0x004B0858); + setRectList(0x004B0A38); + + setBackground(0x420643C4); + setPalette(0x420643C4); + insertMouse433(0x643C0428); + + _class595 = insertSprite<Class595>(this); + _sprite1 = insertStaticSprite(0x942FC224, 300); + _sprite2 = insertStaticSprite(0x70430830, 1200); + _sprite2->setVisible(false); + _sprite3 = insertStaticSprite(0x16E01E20, 1100); + + _asRing1 = insertSprite<AsScene1002Ring>(this, false, 218, 122, _class595->getDrawRect().y, false); + _asRing2 = insertSprite<AsScene1002Ring>(this, true, 218 + 32, 132, _class595->getDrawRect().y, getGlobalVar(0x13206309)); + _asRing3 = insertSprite<AsScene1002Ring>(this, false, 218 + 32 + 32, 122, _class595->getDrawRect().y, false); + _asRing4 = insertSprite<AsScene1002Ring>(this, true, 218 + 32 + 32 + 32, 132, _class595->getDrawRect().y, getGlobalVar(0x80101B1E)); + _asRing5 = insertSprite<AsScene1002Ring>(this, false, 218 + 32 + 32 + 32 + 32, 115, _class595->getDrawRect().y, false); + + _asBridge = insertSprite<AsScene1302Bridge>(this); + _ssFence = insertSprite<SsScene1302Fence>(); + _ssFence->setClipRect(0, 0, 640, _sprite1->getDrawRect().y2()); + + if (which < 0) { + insertKlayman<KmScene1002>(380, 364, _class595, (Sprite*)NULL); + setMessageList(0x004B0868); + } else { + insertKlayman<KmScene1002>(293, 330, _class595, (Sprite*)NULL); + setMessageList(0x004B0870); + } + + _klayman->setClipRect(0, 0, _sprite3->getDrawRect().x2(), 480); + + _asVenusFlyTrap = insertSprite<AsScene1002VenusFlyTrap>(this, _klayman, true); + _vm->_collisionMan->addSprite(_asVenusFlyTrap); + + sendEntityMessage(_klayman, 0x2007, _asVenusFlyTrap); + +} + +uint32 Scene1302::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = 0; + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x4A845A00) { + sendEntityMessage(_klayman, 0x1014, _asRing1); + } else if (param.asInteger() == 0x43807801) { + if (!getGlobalVar(0x13206309)) { + sendEntityMessage(_klayman, 0x1014, _asRing2); + if (_asVenusFlyTrap->getX() - 10 < 218 + 32 && _asVenusFlyTrap->getX() + 10 > 218 + 32) { + setMessageList(0x004B0940); + } else { + setMessageList(0x004B0938); + } + } else { + setMessageList(0x004B0950); + } + messageResult = 1; + } else if (param.asInteger() == 0x46C26A01) { + sendEntityMessage(_klayman, 0x1014, _asRing3); + } else if (param.asInteger() == 0x468C7B11) { + if (!getGlobalVar(0x80101B1E)) { + sendEntityMessage(_klayman, 0x1014, _asRing4); + if (_asVenusFlyTrap->getX() - 10 < 218 + 32 + 32 + 32 && _asVenusFlyTrap->getX() + 10 > 218 + 32 + 32 + 32) { + setMessageList(0x004B0940); + } else { + setMessageList(0x004B0938); + } + } else { + setMessageList(0x004B0950); + } + messageResult = 1; + } else if (param.asInteger() == 0x42845B19) { + sendEntityMessage(_klayman, 0x1014, _asRing5); + } else if (param.asInteger() == 0x430A6060) { + if (getGlobalVar(0x13206309)) { + setMessageList2(0x004B0910); + } else { + messageList402220(); + } + } else if (param.asInteger() == 0x012E2070) { + if (getGlobalVar(0x13206309)) { + setMessageList2(0x004B0968); + } else { + messageList402220(); + } + } else if (param.asInteger() == 0x11C40840) { + if (_asVenusFlyTrap->getX() >= 260 && _asVenusFlyTrap->getX() <= 342) { + setMessageList(0x004B0878); + } else { + setMessageList(0x004B0978); + } + } + break; + case 0x2000: + if (_klayman->getY() > 360) { + sendEntityMessage(_klayman, 0x1014, _asVenusFlyTrap); + setMessageList2(0x004B08F0); + } else { + setMessageList2(0x004B0920); + } + break; + case 0x2002: + if (_klayman->getX() > 545) { + leaveScene(1); + } + break; + case 0x2032: + _sprite2->setVisible(true); + break; + case 0x4806: + sendMessage(_parentModule, 0x1024, 2); + if (sender == _asRing1) { + _soundResource.play(0x665198C0); + } else if (sender == _asRing2) { + sendMessage(_asBridge, 0x4808, 0); + setGlobalVar(0x13206309, 1); + } else if (sender == _asRing3) { + _soundResource.play(0xE2D389C0); + } else if (sender == _asRing4) { + sendMessage(_ssFence, 0x4808, 0); + setGlobalVar(0x80101B1E, 1); + } else if (sender == _asRing5) { + _soundResource.play(0x40428A09); + } + break; + case 0x4807: + if (sender == _asRing2) { + sendMessage(_asBridge, 0x4809, 0); + setGlobalVar(0x13206309, 0); + _sprite2->setVisible(false); + } else if (sender == _asRing4) { + sendMessage(_ssFence, 0x4809, 0); + setGlobalVar(0x80101B1E, 0); + } else if (sender == _asVenusFlyTrap) { + if (getGlobalVar(0x13206309)) { + sendMessage(_asRing2, 0x4807, 0); + } else { + sendMessage(_asRing4, 0x4807, 0); + } + } + break; + case 0x480F: + if (sender == _asRing2) { + _soundResource.play(0x60755842); + sendMessage(_asBridge, 0x4808, 0); + setGlobalVar(0x13206309, 1); + } else if (sender == _asRing4) { + _soundResource.play(0x60755842); + sendMessage(_ssFence, 0x4808, 0); + setGlobalVar(0x80101B1E, 1); + } + break; + case 0x482A: + sendMessage(_asVenusFlyTrap, 0x482B, 0); + break; + case 0x482B: + sendMessage(_asVenusFlyTrap, 0x482A, 0); + break; + } + return messageResult; +} + +AsScene1303Balloon::AsScene1303Balloon(NeverhoodEngine *vm, Scene *parentScene) + : AnimatedSprite(vm, 1100), _soundResource(vm), _parentScene(parentScene) { + + // TODO createSurface3(200, dword_4AF9F8); + createSurface(200, 640, 480); //TODO: Remeove once the line above is done + _x = 289; + _y = 390; + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene1303Balloon::handleMessage); + SetSpriteCallback(&AnimatedSprite::updateDeltaXY); + setFileHash(0x800278D2, 0, -1); +} + +uint32 AsScene1303Balloon::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + sendMessage(_parentScene, 0x4826, 0); + messageResult = 1; + break; + case 0x2000: + stPopBalloon(); + break; + } + return messageResult; +} + +uint32 AsScene1303Balloon::hmBalloonPopped(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x020B0003) { + _soundResource.play(0x742B0055); + } + break; + case 0x3002: + _soundResource.play(0x470007EE); + stopAnimation(); + SetMessageHandler(NULL); + setVisible(false); + break; + } + return messageResult; +} + +void AsScene1303Balloon::stPopBalloon() { + setFileHash(0xAC004CD0, 0, -1); + SetMessageHandler(&AsScene1303Balloon::hmBalloonPopped); +} + +Scene1303::Scene1303(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule, true) { + + _surfaceFlag = true; + SetMessageHandler(&Scene1303::handleMessage); + setRectList(0x004AF9E8); + + setBackground(0x01581A9C); + setPalette(0x01581A9C); + insertMouse433(0x81A9801D); + + if (!getGlobalVar(0xAC00C0D0)) { + _asBalloon = insertSprite<AsScene1303Balloon>(this); + _vm->_collisionMan->addSprite(_asBalloon); + } + + _sprite1 = insertStaticSprite(0xA014216B, 1100); + + insertKlayman<KmScene1303>(207, 332); + setMessageList(0x004AF9A0); + + _klayman->setClipRect(_sprite1->getDrawRect().x, 0, 640, 480); + +} + +uint32 Scene1303::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2000: + setGlobalVar(0xAC00C0D0, 1); + sendMessage(_asBalloon, 0x2000, 0); + break; + case 0x4826: + if (sender == _asBalloon && getGlobalVar(0x31C63C51)) { + setMessageList(0x004AF9B8); + } + break; + } + return 0; +} + +Class544::Class544(NeverhoodEngine *vm, Scene *parentScene, int surfacePriority, int16 x, int16 y) + : AnimatedSprite(vm, 0x548E9411, surfacePriority, x, y), _parentScene(parentScene) { + + if (getGlobalVar(0x31C63C51)) { + setVisible(false); + SetMessageHandler(NULL); + } else { + SetMessageHandler(&Class544::handleMessage); + } +} + +uint32 Class544::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + sendMessage(_parentScene, 0x4826, 0); + messageResult = 1; + break; + case 0x4806: + setGlobalVar(0x31C63C51, 1); + setVisible(false); + SetMessageHandler(NULL); + break; + } + return messageResult; +} + +Scene1304::Scene1304(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule, true) { + + _surfaceFlag = true; + SetMessageHandler(&Scene1304::handleMessage); + setRectList(0x004B91A8); + + setBackground(0x062C0214); + setPalette(0x062C0214); + insertMouse433(0xC021006A); + + if (getGlobalVar(0xAC00C0D0)) { + _class545 = insertSprite<Class545>(this, 0, 1100, 278, 347); + _vm->_collisionMan->addSprite(_class545); + } else { + _class545 = insertSprite<AnimatedSprite>(0x80106018, 100, 279, 48); + // TODO _class545->setUpdateDeltaXY(); + } + + if (!getGlobalVar(0x31C63C51)) { + _class544 = insertSprite<Class544>(this, 1100, 278, 347); + _vm->_collisionMan->addSprite(_class544); + } else { + _class544 = NULL; + } + + _sprite1 = insertStaticSprite(0x0562E621, 1100); + insertStaticSprite(0x012AE033, 1100); + insertStaticSprite(0x090AF033, 1100); + + if (which < 0) { + insertKlayman<KmScene1304>(217, 347); + setMessageList(0x004B90E8); + } else { + insertKlayman<KmScene1304>(100, 347); + setMessageList(0x004B90F0); + } + + _klayman->setClipRect(_sprite1->getDrawRect().x, 0, 640, 480); + +} + +uint32 Scene1304::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x415634A4) { + if (getGlobalVar(0xAC00C0D0)) { + messageList402220(); + } else { + setMessageList(0x004B9158); + } + } + break; + case 0x4826: + if (sender == _class544) { + sendEntityMessage(_klayman, 0x1014, _class544); + setMessageList(0x004B9130); + } else if (sender == _class545) { + sendEntityMessage(_klayman, 0x1014, _class545); + setMessageList(0x004B9140); + } + break; + } + return 0; +} + +Scene1305::Scene1305(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule, true) { + + _surfaceFlag = true; + SetMessageHandler(&Scene1305::handleMessage); + setRectList(0x004B6E98); + + setBackground(0x28801B64); + setPalette(0x28801B64); + insertMouse433(0x01B60280); + + if (which < 0) { + insertKlayman<KmScene1305>(212, 441); + setMessageList(0x004B6E40); + } else { + insertKlayman<KmScene1305>(212, 441); + setMessageList(0x004B6E48); + } + +} + +uint32 Scene1305::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + return Scene::handleMessage(messageNum, param, sender); +} + +AsScene1306Elevator::AsScene1306Elevator(NeverhoodEngine *vm, Scene *parentScene, AnimatedSprite *asElevatorDoor) + : AnimatedSprite(vm, 1100), _soundResource1(vm), _soundResource2(vm), _soundResource3(vm), + _parentScene(parentScene), _asElevatorDoor(asElevatorDoor), _isUp(false), _isDown(true), + _countdown(0) { + + _x = 320; + _y = 240; + createSurface1(0x043B0270, 100); + setFileHash(0x043B0270, 0, -1); + _newHashListIndex = 0; + SetMessageHandler(&AsScene1306Elevator::handleMessage); + _soundResource1.load(0x1C100E83); + _soundResource2.load(0x1C08CEC5); + _soundResource3.load(0x5D011E87); +} + +void AsScene1306Elevator::update() { + if (_isUp && _countdown != 0 && (--_countdown == 0)) { + stGoingDown(); + } + AnimatedSprite::update(); + if (_frameIndex == 7) { + _soundResource3.play(); + _asElevatorDoor->setVisible(false); + } +} + +void AsScene1306Elevator::upGoingDown() { + AnimatedSprite::update(); + if (_frameIndex == 5) { + _asElevatorDoor->setVisible(true); + } +} + +uint32 AsScene1306Elevator::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2001: + if (_isUp) + _countdown = 144; + messageResult = _isUp ? 1 : 0; + break; + case 0x3002: + removeCallbacks(); + break; + case 0x4808: + if (_isDown) + stGoingUp(); + break; + } + return messageResult; +} + +void AsScene1306Elevator::stGoingUp() { + setVisible(true); + _isDown = false; + SetUpdateHandler(&AsScene1306Elevator::update); + setFileHash(0x043B0270, 0, -1); + NextState(&AsScene1306Elevator::cbGoingUpEvent); + _soundResource1.play(); +} + +void AsScene1306Elevator::cbGoingUpEvent() { + SetUpdateHandler(&AsScene1306Elevator::update); + sendMessage(_parentScene, 0x4808, 0); + _isUp = true; + _countdown = 144; + stopAnimation(); + setVisible(false); +} + +void AsScene1306Elevator::stGoingDown() { + SetUpdateHandler(&AsScene1306Elevator::upGoingDown); + _isUp = false; + setVisible(true); + setFileHash(0x043B0270, -1, -1); + _playBackwards = true; + NextState(&AsScene1306Elevator::cbGoingDownEvent); + _soundResource2.play(); +} + +void AsScene1306Elevator::cbGoingDownEvent() { + _isDown = true; + sendMessage(_parentScene, 0x4809, 0); + SetUpdateHandler(&AsScene1306Elevator::update); + stopAnimation(); +} + +Scene1306::Scene1306(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule, true) { + + if (getGlobalVar(0xC0780812) && !getGlobalVar(0x13382860)) + setGlobalVar(0x13382860, 4); + + _surfaceFlag = true; + SetMessageHandler(&Scene1306::handleMessage); + + setBackground(0x05303114); + setPalette(0x05303114); + insertMouse433(0x0311005B); + + if (!getGlobalVar(0x13382860)) { + _class545 = insertSprite<Class545>(this, 2, 1100, 435, 445); + _vm->_collisionMan->addSprite(_class545); + } + + _ssButton = insertSprite<SsCommonButtonSprite>(this, 0x404A36A0, 100, 0x440C1000); + + _asTape = insertSprite<AsScene1201Tape>(this, 19, 1100, 359, 445, 0x9148A011); + + _asElevatorDoor = insertSprite<AnimatedSprite>(0x043B0270, 90, 320, 240); + _asElevatorDoor->setFileHash(0x043B0270, 6, -1); + _asElevatorDoor->setNewHashListIndex(6); + + _asElevator = insertSprite<AsScene1306Elevator>(this, _asElevatorDoor); + + _sprite1 = insertStaticSprite(0x036A1EE0, 80); + + insertStaticSprite(0x00042313, 1100); + + if (which < 0) { + insertKlayman<KmScene1306>(380, 440); + setMessageList(0x004AFAD0); + sendMessage(this, 0x2000, 0); + _vm->_collisionMan->addSprite(_asTape); + } else if (which == 1) { + insertKlayman<KmScene1306>(136, 440); + sendMessage(_klayman, 0x2000, 1); + setMessageList(0x004AFAF0); + sendMessage(this, 0x2000, 1); + _vm->_collisionMan->addSprite(_asTape); + } else if (which == 2) { + if (getGlobalVar(0xC0418A02)) { + insertKlayman<KmScene1306>(515, 440); + _klayman->setDoDeltaX(1); + } else { + insertKlayman<KmScene1306>(355, 440); + } + setMessageList(0x004AFBC8); + sendMessage(this, 0x2000, 0); + _vm->_collisionMan->addSprite(_asTape); + } else if (which == 3) { + insertKlayman<KmScene1306>(534, 440); + setMessageList(0x004AFC30); + sendMessage(this, 0x2000, 0); + _vm->_collisionMan->addSprite(_asTape); + } else if (which == 4) { + insertKlayman<KmScene1306>(136, 440); + sendMessage(_klayman, 0x2000, 1); + setMessageList(0x004AFC38); + sendMessage(this, 0x2000, 1); + _vm->_collisionMan->addSprite(_asTape); + } else if (which == 5) { + insertKlayman<KmScene1306>(136, 440); + sendMessage(_klayman, 0x2000, 1); + setMessageList(0x004AFB00); + sendMessage(this, 0x2000, 1); + _vm->_collisionMan->addSprite(_asTape); + } else { + insertKlayman<KmScene1306>(286, 408); + setSurfacePriority(_asElevator->getSurface(), 1100); + setSurfacePriority(_asElevatorDoor->getSurface(), 1090); + setSurfacePriority(_sprite1->getSurface(), 1080); + sendMessage(this, 0x2000, 0); + SetMessageHandler(&Scene1306::handleMessage416EB0); + clearRectList(); + sendMessage(_asElevator, 0x4808, 0); + } + +} + +Scene1306::~Scene1306() { + setGlobalVar(0xC0418A02, _klayman->isDoDeltaX() ? 1 : 0); +} + +uint32 Scene1306::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x402064D8) { + sendEntityMessage(_klayman, 0x1014, _ssButton); + } else if (param.asInteger() == 0x01C66840) { + if (sendMessage(_asElevator, 0x2001, 0) != 0) { + setMessageList(0x004AFBD8); + } else { + setMessageList(0x004AFAE0); + } + } else if (param.asInteger() == 0x8E646E00) { + setMessageList(0x004AFAD8); + clearRectList(); + SetMessageHandler(&Scene1306::handleMessage416EB0); + } + break; + case 0x2000: + if (param.asInteger() != 0) { + setRectList(0x004AFD28); + _klayman->setKlaymanTable3(); + } else { + setRectList(0x004AFD18); + _klayman->setKlaymanTable1(); + } + break; + case 0x480B: + if (sender == _ssButton) { + sendMessage(_asElevator, 0x4808, 0); + } + break; + case 0x4826: + if (sender == _class545) { + if (_klayman->getX() >= 249) { + sendEntityMessage(_klayman, 0x1014, _class545); + setMessageList(0x004AFC58); + } + } else if (sender == _asTape) { + if (_klayman->getX() >= 249) { + sendEntityMessage(_klayman, 0x1014, _class545); + setMessageList(0x004AFC68); + } + } + break; + case 0x482A: + setSurfacePriority(_asElevator->getSurface(), 1100); + setSurfacePriority(_asElevatorDoor->getSurface(), 1090); + setSurfacePriority(_sprite1->getSurface(), 1080); + break; + case 0x482B: + setSurfacePriority(_asElevator->getSurface(), 100); + setSurfacePriority(_asElevatorDoor->getSurface(), 90); + setSurfacePriority(_sprite1->getSurface(), 80); + sendMessage(this, 0x2000, 0); + _vm->_collisionMan->addSprite(_asTape); + break; + } + return 0; +} + +uint32 Scene1306::handleMessage416EB0(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x4808: + setMessageList(0x004AFBD0); + SetMessageHandler(&Scene1306::handleMessage); + break; + case 0x4809: + leaveScene(1); + break; + case 0x482A: + setSurfacePriority(_asElevator->getSurface(), 1100); + setSurfacePriority(_asElevatorDoor->getSurface(), 1090); + setSurfacePriority(_sprite1->getSurface(), 1080); + break; + case 0x482B: + setSurfacePriority(_asElevator->getSurface(), 100); + setSurfacePriority(_asElevatorDoor->getSurface(), 90); + setSurfacePriority(_sprite1->getSurface(), 80); + sendMessage(this, 0x2000, 0); + _vm->_collisionMan->addSprite(_asTape); + break; + } + return 0; +} + +static const uint32 kAsScene1307KeyResourceList1[] = { + 0x0438069C, + 0x45B0023C, + 0x05700217 +}; + +static const uint32 kAsScene1307KeyResourceList2[] = { + 0x04441334, + 0x061433F0, + 0x06019390 +}; + +static const uint32 kAsScene1307KeyResourceList3[] = { + 0x11A80030, + 0x178812B1, + 0x1488121C +}; + +static const uint32 *kAsScene1307KeyResourceLists[] = { + kAsScene1307KeyResourceList1, + kAsScene1307KeyResourceList2, + kAsScene1307KeyResourceList3 +}; + +static const int kAsScene1307KeySurfacePriorities[] = { + 700, + 500, + 300, + 100 +}; + +const uint kAsScene1307KeyPointsCount = 12; + +static const NPoint kAsScene1307KeyPoints[] = { + {-2, 0}, + {-5, 0}, + { 5, 0}, + {12, 0}, + {17, 0}, + {25, 0}, + {16, -2}, + {10, -6}, + { 0, -7}, + {-7, -3}, + {-3, 4}, + { 2, 2} +}; + +const uint kAsScene1307KeyFrameIndicesCount = 20; + +static const int16 kAsScene1307KeyFrameIndices[] = { + 1, 4, 8, 11, 15, 16, 17, 17, 17, 16, + 15, 14, 12, 10, 9, 7, 5, 3, 2, 1 +}; + +const int kAsScene1307KeyDivValue = 200; + +const int16 kAsScene1307KeyXDelta = 70; +const int16 kAsScene1307KeyYDelta = -12; + +AsScene1307Key::AsScene1307Key(NeverhoodEngine *vm, Scene *parentScene, uint index, NRect *clipRects) + : AnimatedSprite(vm, 1100), _soundResource1(vm), _soundResource2(vm), _soundResource3(vm), + _soundResource4(vm), _parentScene(parentScene), _index(index), _clipRects(clipRects), + _isClickable(true) { + + NPoint pt; + const uint32 *fileHashes = kAsScene1307KeyResourceLists[_index]; + + _dataResource.load(0x22102142); + _pointList = _dataResource.getPointArray(0xAC849240); + + pt = (*_pointList)[getSubVar(0xA010B810, _index)]; + _x = pt.x; + _y = pt.y; + + // TODO createSurface3(kAsScene1307KeySurfacePriorities[getSubVar(0xA010B810, _index) % 4], fileHashes); + createSurface(kAsScene1307KeySurfacePriorities[getSubVar(0xA010B810, _index) % 4], 640, 480); //TODO: Remeove once the line above is done + + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene1307Key::handleMessage); + + setFileHash(fileHashes[0], 0, -1); + + _soundResource1.load(0xDC4A1280); + _soundResource2.load(0xCC021233); + _soundResource3.load(0xC4C23844); + _soundResource3.load(0xC4523208); + +} + +uint32 AsScene1307Key::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + if (_isClickable) { + sendMessage(_parentScene, 0x4826, 0); + stRemoveKey(); + messageResult = 1; + } + break; + case 0x2000: + _isClickable = param.asInteger() != 0; + break; + case 0x2001: + setSubVar(0xA010B810, _index, param.asInteger()); + stMoveKey(); + break; + case 0x2003: + _soundResource4.play(); + stUnlock(); + break; + case 0x2004: + _soundResource3.play(); + stInsert(); + break; + } + return messageResult; +} + +void AsScene1307Key::suRemoveKey() { + if (_pointIndex < kAsScene1307KeyPointsCount) { + _x += kAsScene1307KeyPoints[_pointIndex].x; + _y += kAsScene1307KeyPoints[_pointIndex].y; + processDelta(); + _pointIndex++; + } else { + SetSpriteCallback(NULL); + } +} + +void AsScene1307Key::suInsertKey() { + if (_pointIndex < kAsScene1307KeyPointsCount) { + _x -= kAsScene1307KeyPoints[kAsScene1307KeyPointsCount - _pointIndex - 1].x; + _y -= kAsScene1307KeyPoints[kAsScene1307KeyPointsCount - _pointIndex - 1].y; + processDelta(); + _pointIndex++; + if (_pointIndex == 7) + _soundResource1.play(); + } else { + SetSpriteCallback(NULL); + sendMessage(_parentScene, 0x2002, 0); + } +} + +void AsScene1307Key::suMoveKey() { + if (_pointIndex < kAsScene1307KeyFrameIndicesCount) { + _frameIndex += kAsScene1307KeyFrameIndices[_pointIndex]; + _x = _prevX + (_deltaX * _frameIndex) / kAsScene1307KeyDivValue; + _y = _prevY + (_deltaY * _frameIndex) / kAsScene1307KeyDivValue; + processDelta(); + _pointIndex++; + } else { + NPoint pt = (*_pointList)[getSubVar(0xA010B810, _index)]; + _x = pt.x + kAsScene1307KeyXDelta; + _y = pt.y + kAsScene1307KeyYDelta; + stInsertKey(); + } +} + +void AsScene1307Key::stRemoveKey() { + const uint32 *fileHashes = kAsScene1307KeyResourceLists[_index]; + _pointIndex = 0; + SetSpriteCallback(&AsScene1307Key::suRemoveKey); + setFileHash(fileHashes[0], 0, -1); + _soundResource2.play(); +} + +void AsScene1307Key::stInsertKey() { + _pointIndex = 0; + sendMessage(_parentScene, 0x1022, kAsScene1307KeySurfacePriorities[getSubVar(0xA010B810, _index) % 4]); + setClipRect(_clipRects[getSubVar(0xA010B810, _index) % 4]); + SetSpriteCallback(&AsScene1307Key::suInsertKey); + _newHashListIndex = -2; +} + +void AsScene1307Key::stMoveKey() { + NPoint pt = (*_pointList)[getSubVar(0xA010B810, _index)]; + int16 newX = pt.x + kAsScene1307KeyXDelta; + int16 newY = pt.y + kAsScene1307KeyYDelta; + sendMessage(_parentScene, 0x1022, 1000); + setClipRect(0, 0, 640, 480); + _prevX = _x; + _prevY = _y; + if (newX == _x && newY == _y) { + stInsertKey(); + } else { + const uint32 *fileHashes = kAsScene1307KeyResourceLists[_index]; + _pointIndex = 0; + _frameIndex = 0; + _deltaX = newX - _x; + _deltaY = newY - _y; + SetSpriteCallback(&AsScene1307Key::suMoveKey); + setFileHash(fileHashes[0], 0, -1); + } +} + +void AsScene1307Key::stUnlock() { + const uint32 *fileHashes = kAsScene1307KeyResourceLists[_index]; + setFileHash(fileHashes[1], 0, -1); + _newHashListIndex = -2; +} + +void AsScene1307Key::stInsert() { + const uint32 *fileHashes = kAsScene1307KeyResourceLists[_index]; + setFileHash(fileHashes[2], 0, -1); + _newHashListIndex = -2; +} + +Scene1307::Scene1307(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule, true), _soundResource(vm), _countdown(0), + _asCurrKey(NULL), _isInsertingKey(false), _doLeaveScene(false), _isPuzzleSolved(false) { + + //DEBUG + setSubVar(0x08D0AB11, 0, 1); + setSubVar(0x08D0AB11, 1, 1); + setSubVar(0x08D0AB11, 2, 1); + + Sprite *tempSprite; + + _vm->gameModule()->initScene1307Vars(); + + _dataResource.load(0x22102142); + _keyHolePoints = _dataResource.getPointArray(0xAC849240); + + for (uint i = 0; i < 16; i++) { + NPoint pt = (*_keyHolePoints)[i]; + _keyHoleRects[i].x1 = pt.x - 15; + _keyHoleRects[i].y1 = pt.y - 15; + _keyHoleRects[i].x2 = pt.x + 15; + _keyHoleRects[i].y2 = pt.y + 15; + } + + _surfaceFlag = true; + SetMessageHandler(&Scene1307::handleMessage); + SetUpdateHandler(&Scene1307::update); + + setBackground(0xA8006200); + setPalette(0xA8006200); + addEntity(_palette); + insertMouse435(0x06204A88, 20, 620); + + tempSprite = insertStaticSprite(0x00A3621C, 800); + _clipRects[0].set(tempSprite->getDrawRect().x, 0, 640, 480); + + tempSprite = insertStaticSprite(0x00A3641C, 600); + _clipRects[1].set(tempSprite->getDrawRect().x, 0, 640, 480); + + tempSprite = insertStaticSprite(0x00A3681C, 400); + _clipRects[2].set(tempSprite->getDrawRect().x, 0, 640, 480); + + tempSprite = insertStaticSprite(0x00A3701C, 200); + _clipRects[3].set(tempSprite->getDrawRect().x, 0, 640, 480); + + for (uint keyIndex = 0; keyIndex < 3; keyIndex++) { + if (getSubVar(0x08D0AB11, keyIndex)) { + _asKeys[keyIndex] = insertSprite<AsScene1307Key>(this, keyIndex, _clipRects); + _vm->_collisionMan->addSprite(_asKeys[keyIndex]); + } else { + _asKeys[keyIndex] = NULL; + } + } + + _soundResource.load(0x68E25540); + +} + +void Scene1307::update() { + Scene::update(); + if (_countdown && (--_countdown == 0)) { + _doLeaveScene = true; + } else if (_countdown == 20) { + _palette->startFadeToWhite(40); + } + if (_doLeaveScene && !_soundResource.isPlaying()) { + leaveScene(1); + setGlobalVar(0x80455A41, 1); + } +} + +uint32 Scene1307::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = 0; + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x0001: + // TODO Debug stuff + if (!_isPuzzleSolved) { + if (param.asPoint().x > 20 && param.asPoint().x < 620) { + if (_asCurrKey && !_isInsertingKey) { + int16 mouseX = param.asPoint().x; + int16 mouseY = param.asPoint().y; + uint clickedKeyHoleIndex; + for (clickedKeyHoleIndex = 0; clickedKeyHoleIndex < 16; clickedKeyHoleIndex++) { + if (mouseX >= _keyHoleRects[clickedKeyHoleIndex].x1 && mouseX <= _keyHoleRects[clickedKeyHoleIndex].x2 && + mouseY >= _keyHoleRects[clickedKeyHoleIndex].y1 && mouseY <= _keyHoleRects[clickedKeyHoleIndex].y2) + break; + } + if (clickedKeyHoleIndex < 16) { + // Check if the clicked keyhole is already occupied with a key + bool occupied = false; + for (uint keyIndex = 0; keyIndex < 3 && !occupied; keyIndex++) { + if (getSubVar(0x08D0AB11, keyIndex) && _asKeys[keyIndex] != _asCurrKey) { + if (getSubVar(0xA010B810, keyIndex) == clickedKeyHoleIndex) + occupied = true; + } + } + if (!occupied) { + // If the keyhole is free, insert the current key + sendMessage(_asCurrKey, 0x2001, clickedKeyHoleIndex); + _isInsertingKey = true; + _mouseClicked = false; + } + } + } + } else if (_countdown == 0 && !_asCurrKey && !_isInsertingKey) { + leaveScene(0); + } + } + break; + // TODO Debug stuff + case 0x2002: + // Check if all keys are in the correct keyholes + if (getSubVar(0x08D0AB11, 0) && getSubVar(0xA010B810, 0) == getSubVar(0x0C10A000, 0) && + getSubVar(0x08D0AB11, 1) && getSubVar(0xA010B810, 1) == getSubVar(0x0C10A000, 1) && + getSubVar(0x08D0AB11, 2) && getSubVar(0xA010B810, 2) == getSubVar(0x0C10A000, 2)) { + // Play unlock animations for all keys + for (uint keyIndex = 0; keyIndex < 3; keyIndex++) { + if (_asKeys[keyIndex]) + sendMessage(_asKeys[keyIndex], 0x2003, 1); + } + _soundResource.play(); + _isPuzzleSolved = true; + _countdown = 47; + } else { + for (uint keyIndex = 0; keyIndex < 3; keyIndex++) { + if (getSubVar(0x08D0AB11, keyIndex) && _asKeys[keyIndex]) { + sendMessage(_asKeys[keyIndex], 0x2000, 1); + } + } + sendMessage(_asCurrKey, 0x2004, 1); + } + _asCurrKey = NULL; + _isInsertingKey = false; + break; + case 0x4826: + _asCurrKey = (Sprite*)sender; + for (uint keyIndex = 0; keyIndex < 3; keyIndex++) { + if (getSubVar(0x08D0AB11, keyIndex) && _asKeys[keyIndex]) { + sendMessage(_asKeys[keyIndex], 0x2000, 0); + } + } + break; + } + return messageResult; +} + +static const uint32 kScene1308FileHashes[] = { + 0x08006320, + 0x10006320, + 0x20006320, + 0x40006320, + 0x80006320, + 0x00006321, + 0x00006322, + 0x00006324, + 0x00006328, + 0x08306320, + 0x10306320, + 0x20306320, + 0x40306320, + 0x80306320, + 0x00306321, + 0x00306322 +}; + +Class549::Class549(NeverhoodEngine *vm, Scene *parentScene) + : AnimatedSprite(vm, 0xBA0AE050, 1100, 320, 240), _soundResource(vm), + _parentScene(parentScene) { + + SetMessageHandler(&Class549::handleMessage); + setVisible(false); + stopAnimation(); +} + +uint32 Class549::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + removeCallbacks(); + break; + case 0x4808: + sub455470(); + break; + case 0x4809: + sub4554F0(); + break; + } + return messageResult; +} + +void Class549::sub455470() { + setFileHash(0xBA0AE050, 0, -1); + setVisible(true); + NextState(&Class549::hide); + _soundResource.play(calcHash("fxDoorOpen38")); +} + +void Class549::hide() { + sendMessage(_parentScene, 0x2000, 0); + stopAnimation(); + setVisible(false); +} + +void Class549::sub4554F0() { + setFileHash(0xBA0AE050, -1, -1); + _playBackwards = true; + setVisible(true); + NextState(&Class549::sub455550); + _soundResource.play(calcHash("fxDoorClose38")); +} + +void Class549::sub455550() { + sendMessage(_parentScene, 0x2001, 0); + stopAnimation(); +} + +Class592::Class592(NeverhoodEngine *vm, Scene *parentScene) + : AnimatedSprite(vm, 0xA08A0851, 1100, 320, 240), _soundResource(vm), + _parentScene(parentScene) { + + SetMessageHandler(&Class592::handleMessage); + NextState(&Class592::sub455710); + _soundResource.play(0x51456049); +} + +uint32 Class592::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + removeCallbacks(); + break; + } + return messageResult; +} + +void Class592::sub455710() { + setFileHash(0x6238B191, 0, -1); + NextState(&Class592::sub455740); + _x = 580; + _y = 383; +} + +void Class592::sub455740() { + sendMessage(_parentScene, 0x2004, 0); + stopAnimation(); + setVisible(false); +} + +Class593::Class593(NeverhoodEngine *vm, Scene *parentScene) + : AnimatedSprite(vm, 0x80180A10, 100, 320, 240), _parentScene(parentScene) { + + SetMessageHandler(&Class593::handleMessage); + setVisible(false); + stopAnimation(); + Entity::_priority = 1200; +} + +uint32 Class593::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2002: + sub4558F0(); + break; + case 0x2003: + sub455920(); + case 0x3002: + removeCallbacks(); + break; + } + return messageResult; +} + +void Class593::sub4558F0() { + setFileHash(0x80180A10, 0, -1); + setVisible(false); + _newHashListIndex = -2; +} + +void Class593::sub455920() { + setFileHash(0x80180A10, -1, -1); + _playBackwards = true; + NextState(&Class593::sub455950); +} + +void Class593::sub455950() { + sendMessage(_parentScene, 0x2003, 0); + stopAnimation(); + setVisible(false); +} + +Class601::Class601(NeverhoodEngine *vm, uint32 fileHash, int index) + : StaticSprite(vm, fileHash, 100) { + + setVisible(false); + _x = _spriteResource.getPosition().x + index * 20; + StaticSprite::update(); +} + +Class513::Class513(NeverhoodEngine *vm) + : AnimatedSprite(vm, 1100), _soundResource(vm) { + + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&Class513::handleMessage); + _x = 286; + _y = 429; + createSurface1(0xA282C472, 100); + setFileHash(0xA282C472, 0, -1); +} + +uint32 Class513::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x66382026) { + _soundResource.play(0x0CD84468); + } else if (param.asInteger() == 0x6E28061C) { + _soundResource.play(0x78C8402C); + } else if (param.asInteger() == 0x462F0410) { + _soundResource.play(0x60984E28); + } + break; + } + return messageResult; +} + +Scene1308::Scene1308(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule, true), _flag1(false) { + + _vm->gameModule()->initScene1307Vars(); + + _surfaceFlag = true; + SetMessageHandler(&Scene1308::handleMessage); + + setBackground(0x41024202); + setPalette(0x41024202); + insertMouse433(0x24206418); + + _asTape = insertSprite<AsScene1201Tape>(this, 17, 1100, 502, 445, 0x9148A011); + _vm->_collisionMan->addSprite(_asTape); + + if (getGlobalVar(0x01023818)) { + insertSprite<Class513>(); + insertSprite<AnimatedSprite>(0x461A1490, 200, 235, 429); + } + + _sprite1 = insertStaticSprite(0x0A042060, 1100); + _class549 = insertSprite<Class549>(this); + _class593 = insertSprite<Class593>(this); + + _class601_1 = insertSprite<Class601>(kScene1308FileHashes[getSubVar(0x0C10A000, 1)], 0); + _class601_2 = insertSprite<Class601>(kScene1308FileHashes[getSubVar(0x0C10A000, 0)], 1); + _class601_2 = insertSprite<Class601>(kScene1308FileHashes[getSubVar(0x0C10A000, 2)], 2); + + _sprite2 = insertStaticSprite(0x40043120, 995); + _sprite3 = insertStaticSprite(0x43003100, 995); + _sprite4 = NULL; + + if (which < 0) { + insertKlayman<KmScene1308>(380, 440); + setMessageList(0x004B57C0); + if (getGlobalVar(0x80455A41)) { + _sprite4 = insertStaticSprite(0x0101A624, 1100); + setRectList(0x004B5990); + } else { + _sprite5 = insertStaticSprite(0x080811A0, 100); + setRectList(0x004B5980); + } + } else if (which == 1) { + insertKlayman<KmScene1308>(640, 440); + setMessageList(0x004B57C8); + if (getGlobalVar(0x80455A41)) { + _sprite4 = insertStaticSprite(0x0101A624, 1100); + setRectList(0x004B5990); + } else { + _sprite5 = insertStaticSprite(0x080811A0, 100); + setRectList(0x004B5980); + } + } else if (which == 2) { + insertKlayman<KmScene1308>(475, 440); + setMessageList(0x004B58B0); + if (getGlobalVar(0x80455A41)) { + _sprite5 = insertSprite<Class592>(this); + _sprite4 = insertStaticSprite(0x0101A624, 1100); + _sprite4->setVisible(false); + } else { + _sprite5 = insertStaticSprite(0x080811A0, 100); + setRectList(0x004B5980); + } + } else { + insertKlayman<KmScene1308>(41, 440); + setMessageList(0x004B57D0); + sendMessage(_class549, 0x4808, 0); + _sprite1->setVisible(false); + if (getGlobalVar(0x80455A41)) { + _sprite4 = insertStaticSprite(0x0101A624, 1100); + _klayman->setVisible(false); + } else { + _sprite5 = insertStaticSprite(0x080811A0, 100); + _klayman->setVisible(false); + } + } + + if (_sprite4) { + _klayman->setClipRect(_sprite1->getDrawRect().x, 0, _sprite4->getDrawRect().x2(), 480); + } else { + _klayman->setClipRect(_sprite1->getDrawRect().x, 0, 640, 480); + } + + if (getGlobalVar(0x04A105B3) == 4) { + _class489 = insertSprite<Class489>(this, _klayman, (Sprite*)NULL); + _vm->_collisionMan->addSprite(_class489); + _class489->setClipRect(0, 0, 640, _sprite2->getDrawRect().y2()); + _class489->setRepl(64, 0); + } else { + _class489 = NULL; + } + +} + +uint32 Scene1308::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x88C11390) { + setRectList(0x004B59A0); + _flag1 = true; + } else if (param.asInteger() == 0x08821382) { + sendEntityMessage(_klayman, 0x1014, _class489); + if (getGlobalVar(0x80455A41)) { + setRectList(0x004B5990); + } else { + setRectList(0x004B5980); + } + _flag1 = false; + } else if (param.asInteger() == 0x4AC68808) { + clearRectList(); + sendMessage(_class549, 0x4809, 0); + _sprite1->setVisible(false); + _klayman->setVisible(false); + } + break; + case 0x1022: + if (sender == _class489) { + if (param.asInteger() >= 1000) + setSurfacePriority(_sprite3->getSurface(), 1100); + else + setSurfacePriority(_sprite3->getSurface(), 995); + } + break; + case 0x2000: + if (getGlobalVar(0x80455A41)) { + setRectList(0x004B5990); + } else { + setRectList(0x004B5980); + } + setMessageList(0x004B57E8, false); + _sprite1->setVisible(true); + _klayman->setVisible(true); + break; + case 0x2001: + leaveScene(0); + break; + case 0x2003: + _class601_1->setVisible(false); + _class601_2->setVisible(false); + _class601_3->setVisible(false); + break; + case 0x2004: + _sprite4->setVisible(true); + setRectList(0x004B5990); + break; + case 0x4807: + sendMessage(_class593, 0x2003, 0); + break; + case 0x480F: + sendMessage(_class593, 0x2002, 0); + _class601_1->setVisible(true); + _class601_2->setVisible(true); + _class601_3->setVisible(true); + break; + case 0x4826: + if (sender == _class489) { + if (_flag1) { + setMessageList2(0x004B5868); + } else { + if (param.asInteger() == 1) { + sendEntityMessage(_klayman, 0x1014, _class489); + setMessageList2(0x004B5848); + } else if (sendMessage(_class489, 0x480C, _klayman->getX() <= _class489->getX() ? 0 : 1) != 0) { + sendEntityMessage(_klayman, 0x1014, _class489); + setMessageList2(0x004B5830); + } else { + setMessageList2(0x004B5800); + } + } + } else if (sender == _asTape) { + if (_flag1) { + setMessageList2(0x004B5868); + } else if (_messageListStatus != 2) { + sendEntityMessage(_klayman, 0x1014, _asTape); + setMessageList2(0x004B58E0); + } + } + break; + } + return 0; +} + +Scene1317::Scene1317(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule, true) { + + SetMessageHandler(&Scene1317::handleMessage); + _smackerPlayer = addSmackerPlayer(new SmackerPlayer(_vm, this, 0x08982841, true, false)); + insertMouse433(0x08284011); + showMouse(false); + _smackerFileHash = 0; + _smackerFlag1 = false; +} + +void Scene1317::update() { + if (_smackerFileHash) { + _smackerPlayer->open(_smackerFileHash, _smackerFlag1); + _smackerFileHash = 0; + } + Scene::update(); +} + +void Scene1317::upChooseKing() { + if (!_klaymanBlinks && _klaymanBlinkCountdown != 0 && (--_klaymanBlinkCountdown == 0)) + _klaymanBlinks = true; + + if (!_klaymanBlinks && _smackerPlayer->getFrameNumber() + 1 >= 2) { + _smackerPlayer->rewind(); + } else if (_klaymanBlinks && _smackerPlayer->getFrameNumber() + 1 >= 6) { + _smackerPlayer->rewind(); + _klaymanBlinks = false; + _klaymanBlinkCountdown = _vm->_rnd->getRandomNumber(30 - 1) + 15; + } + + if (!_klaymanBlinks && _decisionCountdown != 0 && (--_decisionCountdown == 0)) + stNoDecisionYet(); + + if (_smackerFileHash) { + _smackerPlayer->open(_smackerFileHash, _smackerFlag1); + _smackerFileHash = 0; + } + + Scene::update(); + +} + +uint32 Scene1317::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + stChooseKing(); + break; + } + return messageResult; +} + +uint32 Scene1317::hmChooseKing(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x0001: + if (param.asPoint().x >= 21 && param.asPoint().y >= 24 && + param.asPoint().x <= 261 && param.asPoint().y <= 280) { + stHoborgAsKing(); + } else if (param.asPoint().x >= 313 && param.asPoint().y >= 184 && + param.asPoint().x <= 399 && param.asPoint().y <= 379) { + stKlaymanAsKing(); + } else if (param.asPoint().x >= 347 && param.asPoint().y >= 380 && + param.asPoint().x <= 418 && param.asPoint().y <= 474) { + stKlaymanAsKing(); + } + break; + } + return messageResult; +} + +uint32 Scene1317::hmNoDecisionYet(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + stChooseKing(); + break; + } + return messageResult; +} + +uint32 Scene1317::hmHoborgAsKing(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + stEndMovie(); + break; + } + return messageResult; +} + +uint32 Scene1317::hmKlaymanAsKing(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + leaveScene(0); + break; + } + return messageResult; +} + +uint32 Scene1317::hmEndMovie(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + leaveScene(0); + break; + } + return messageResult; +} + +void Scene1317::stChooseKing() { + showMouse(true); + SetMessageHandler(&Scene1317::hmChooseKing); + SetUpdateHandler(&Scene1317::upChooseKing); + _smackerFileHash = 0x10982841; + _smackerFlag1 = true; + _decisionCountdown = 450; + _klaymanBlinks = false; + _klaymanBlinkCountdown = _vm->_rnd->getRandomNumber(30 - 1) + 15; +} + +void Scene1317::stNoDecisionYet() { + showMouse(false); + SetMessageHandler(&Scene1317::hmNoDecisionYet); + SetUpdateHandler(&Scene1317::update); + _smackerFileHash = 0x20982841; + _smackerFlag1 = false; +} + +void Scene1317::stHoborgAsKing() { + showMouse(false); + SetMessageHandler(&Scene1317::hmHoborgAsKing); + SetUpdateHandler(&Scene1317::update); + _smackerFileHash = 0x40982841; + _smackerFlag1 = false; +} + +void Scene1317::stKlaymanAsKing() { + showMouse(false); + SetMessageHandler(&Scene1317::hmKlaymanAsKing); + SetUpdateHandler(&Scene1317::update); + _smackerFileHash = 0x80982841; + _smackerFlag1 = false; +} + +void Scene1317::stEndMovie() { + showMouse(false); + SetMessageHandler(&Scene1317::hmEndMovie); + SetUpdateHandler(&Scene1317::update); + _smackerFileHash = 0x40800711; + _smackerFlag1 = false; +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/module1300.h b/engines/neverhood/module1300.h new file mode 100644 index 0000000000..27e2540407 --- /dev/null +++ b/engines/neverhood/module1300.h @@ -0,0 +1,321 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef NEVERHOOD_MODULE1300_H +#define NEVERHOOD_MODULE1300_H + +#include "neverhood/neverhood.h" +#include "neverhood/module.h" +#include "neverhood/scene.h" +#include "neverhood/smackerplayer.h" + +namespace Neverhood { + +// Module1300 + +class Module1300 : public Module { +public: + Module1300(NeverhoodEngine *vm, Module *parentModule, int which); + virtual ~Module1300(); +protected: + uint32 _musicFileHash; + void createScene(int sceneNum, int which); + void updateScene(); +}; + +class AsScene1302Bridge : public AnimatedSprite { +public: + AsScene1302Bridge(NeverhoodEngine *vm, Scene *parentScene); +protected: + Scene *_parentScene; + SoundResource _soundResource1; + SoundResource _soundResource2; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void stLowerBridge(); + void stRaiseBridge(); + void cbLowerBridgeEvent(); +}; + +class SsScene1302Fence : public StaticSprite { +public: + SsScene1302Fence(NeverhoodEngine *vm); +protected: + SoundResource _soundResource1; + SoundResource _soundResource2; + int16 _firstY; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void suMoveDown(); + void suMoveUp(); +}; + +class Class595 : public StaticSprite { +public: + Class595(NeverhoodEngine *vm, Scene *parentScene); +protected: + Scene *_parentScene; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene1302 : public Scene { +public: + Scene1302(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + SoundResource _soundResource; + Sprite *_asVenusFlyTrap; + Sprite *_asBridge; + Sprite *_ssFence; + Sprite *_asRing1; + Sprite *_asRing2; + Sprite *_asRing3; + Sprite *_asRing4; + Sprite *_asRing5; + Sprite *_class595; + Sprite *_sprite1; + Sprite *_sprite2; + Sprite *_sprite3; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class AsScene1303Balloon : public AnimatedSprite { +public: + AsScene1303Balloon(NeverhoodEngine *vm, Scene *parentScene); +protected: + Scene *_parentScene; + SoundResource _soundResource; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 hmBalloonPopped(int messageNum, const MessageParam ¶m, Entity *sender); + void stPopBalloon(); +}; + +class Scene1303 : public Scene { +public: + Scene1303(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + Sprite *_sprite1; + Sprite *_asBalloon; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Class544 : public AnimatedSprite { +public: + Class544(NeverhoodEngine *vm, Scene *parentScene, int surfacePriority, int16 x, int16 y); +protected: + Scene *_parentScene; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene1304 : public Scene { +public: + Scene1304(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + Sprite *_sprite1; + Sprite *_class545; + Sprite *_class544; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene1305 : public Scene { +public: + Scene1305(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class AsScene1306Elevator : public AnimatedSprite { +public: + AsScene1306Elevator(NeverhoodEngine *vm, Scene *parentScene, AnimatedSprite *asElevatorDoor); +protected: + Scene *_parentScene; + AnimatedSprite *_asElevatorDoor; + SoundResource _soundResource1; + SoundResource _soundResource2; + SoundResource _soundResource3; + bool _isUp; + bool _isDown; + int _countdown; + void update(); + void upGoingDown(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void stGoingUp(); + void cbGoingUpEvent(); + void stGoingDown(); + void cbGoingDownEvent(); +}; + +class Scene1306 : public Scene { +public: + Scene1306(NeverhoodEngine *vm, Module *parentModule, int which); + ~Scene1306(); +protected: + Sprite *_ssButton; + Sprite *_asTape; + AnimatedSprite *_asElevatorDoor; + Sprite *_asElevator; + Sprite *_sprite1; + Sprite *_class545; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 handleMessage416EB0(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class AsScene1307Key : public AnimatedSprite { +public: + AsScene1307Key(NeverhoodEngine *vm, Scene *parentScene, uint index, NRect *clipRects); +protected: + Scene *_parentScene; + SoundResource _soundResource1; + SoundResource _soundResource2; + SoundResource _soundResource3; + SoundResource _soundResource4; + NPointArray *_pointList; + uint _pointIndex; + int _frameIndex; + uint _index; + NRect *_clipRects; + bool _isClickable; + int16 _prevX, _prevY; + int16 _deltaX, _deltaY; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void suRemoveKey(); + void suInsertKey(); + void suMoveKey(); + void stRemoveKey(); + void stInsertKey(); + void stMoveKey(); + void stUnlock(); + void stInsert(); +}; + +class Scene1307 : public Scene { +public: + Scene1307(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + SoundResource _soundResource; + NPointArray *_keyHolePoints; + NRect _keyHoleRects[16]; + NRect _clipRects[4]; + Sprite *_asKeys[3]; + int _countdown; + Sprite *_asCurrKey; + bool _isInsertingKey; + bool _doLeaveScene; + bool _isPuzzleSolved; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Class549 : public AnimatedSprite { +public: + Class549(NeverhoodEngine *vm, Scene *parentScene); +protected: + Scene *_parentScene; + SoundResource _soundResource; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void sub455470(); + void hide(); + void sub4554F0(); + void sub455550(); +}; + +class Class592 : public AnimatedSprite { +public: + Class592(NeverhoodEngine *vm, Scene *parentScene); +protected: + Scene *_parentScene; + SoundResource _soundResource; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void sub455710(); + void sub455740(); +}; + +class Class593 : public AnimatedSprite { +public: + Class593(NeverhoodEngine *vm, Scene *parentScene); +protected: + Scene *_parentScene; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void sub4558F0(); + void sub455920(); + void sub455950(); +}; + +class Class601 : public StaticSprite { +public: + Class601(NeverhoodEngine *vm, uint32 fileHash, int index); +}; + +class Class513 : public AnimatedSprite { +public: + Class513(NeverhoodEngine *vm); +protected: + SoundResource _soundResource; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene1308 : public Scene { +public: + Scene1308(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + Sprite *_asTape; + Sprite *_class549; + Sprite *_class593; + Sprite *_class601_1; + Sprite *_class601_2; + Sprite *_class601_3; + AnimatedSprite *_class489; + Sprite *_sprite1; + Sprite *_sprite2; + Sprite *_sprite3; + Sprite *_sprite4; + Sprite *_sprite5; + bool _flag1; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene1317 : public Scene { +public: + Scene1317(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + SmackerPlayer *_smackerPlayer; + bool _klaymanBlinks; + int _klaymanBlinkCountdown; + int _decisionCountdown; + uint32 _smackerFileHash; + bool _smackerFlag1; + void update(); + void upChooseKing(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 hmChooseKing(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 hmNoDecisionYet(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 hmHoborgAsKing(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 hmKlaymanAsKing(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 hmEndMovie(int messageNum, const MessageParam ¶m, Entity *sender); + void stChooseKing(); + void stNoDecisionYet(); + void stHoborgAsKing(); + void stKlaymanAsKing(); + void stEndMovie(); +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_MODULE1300_H */ diff --git a/engines/neverhood/module1400.cpp b/engines/neverhood/module1400.cpp new file mode 100644 index 0000000000..42dfdb3d5b --- /dev/null +++ b/engines/neverhood/module1400.cpp @@ -0,0 +1,1737 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "neverhood/module1400.h" +#include "neverhood/module1000.h" +#include "neverhood/module2100.h" +#include "neverhood/module2200.h" +#include "neverhood/diskplayerscene.h" +#include "neverhood/gamemodule.h" + +namespace Neverhood { + +Module1400::Module1400(NeverhoodEngine *vm, Module *parentModule, int which) + : Module(vm, parentModule) { + + // TODO Music18hList_add(0x00AD0012, 0x06333232); + // TODO Music18hList_add(0x00AD0012, 0x624A220E); + + if (which < 0) { + createScene(_vm->gameState().sceneNum, -1); + } else { + createScene(0, 0); + } + +} + +Module1400::~Module1400() { + // TODO Music18hList_deleteGroup(0x00AD0012); +} + +void Module1400::createScene(int sceneNum, int which) { + debug("Module1400::createScene(%d, %d)", sceneNum, which); + _vm->gameState().sceneNum = sceneNum; + switch (_vm->gameState().sceneNum) { + case 0: + // TODO Music18hList_play(0x06333232, 0, 2, 1); + _childObject = new Scene1401(_vm, this, which); + break; + case 1: + // TODO Music18hList_stop(0x06333232, 0, 2); + // TODO Music18hList_stop(0x624A220E, 0, 2); + _childObject = new Scene1402(_vm, this, which); + break; + case 2: + // TODO Music18hList_stop(0x06333232, 0, 2); + // TODO Music18hList_play(0x624A220E, 0, 2, 1); + _childObject = new Scene1403(_vm, this, which); + break; + case 3: + // TODO Music18hList_play(0x06333232, 0, 2, 1); + _childObject = new Scene1404(_vm, this, which); + break; + case 4: + // TODO Music18hList_play(0x06333232, 0, 2, 1); + _childObject = new Scene1405(_vm, this, which); + break; + case 5: + // TODO Music18hList_stop(0x06333232, 0, 2); + _childObject = new DiskplayerScene(_vm, this, 2); + break; + case 6: + // TODO Music18hList_stop(0x06333232, 0, 2); + _childObject = new Scene1407(_vm, this, which); + break; + } + SetUpdateHandler(&Module1400::updateScene); + _childObject->handleUpdate(); +} + +void Module1400::updateScene() { + if (!updateChild()) { + switch (_vm->gameState().sceneNum) { + case 0: + if (_moduleResult == 1) { + createScene(1, 0); + } else if (_moduleResult == 2) { + createScene(3, 0); + } else { + leaveModule(0); + } + break; + case 1: + if (_moduleResult == 1) { + createScene(2, 0); + } else if (_moduleResult == 2) { + createScene(6, -1); + } else { + createScene(0, 1); + } + break; + case 2: + createScene(1, 1); + break; + case 3: + if (_moduleResult == 1) { + createScene(4, 0); + } else if (_moduleResult == 2) { + createScene(5, -1); + } else { + createScene(0, 2); + } + break; + case 4: + createScene(3, 1); + break; + case 5: + createScene(3, 2); + break; + case 6: + createScene(1, 2); + break; + } + } +} + +// Scene1401 + +Class525::Class525(NeverhoodEngine *vm) + : AnimatedSprite(vm, 1100), _soundResource1(vm), _soundResource2(vm), + _countdown1(0), _countdown2(0) { + + // TODO createSurface3(900, dword_4B6768); + createSurface(900, 640, 480); //TODO: Remeove once the line above is done + _x = 454; + _y = 217; + SetUpdateHandler(&Class525::update4662A0); + SetMessageHandler(&Class525::handleMessage466320); + setFileHash(0x4C210500, 0, -1); +} + +Class525::~Class525() { + // TODO Sound1ChList_sub_407AF0(0x01104C08); +} + +void Class525::update4662A0() { + AnimatedSprite::update(); + if (_countdown1 != 0 && (--_countdown1 == 0)) { + sub466460(); + } + if (_countdown2 != 0 && (--_countdown2 == 0)) { + // TODO Sound1ChList_addSoundResource(0x01104C08, 0x4A116437, true); + // TODO Sound1ChList_playLooping(0x4A116437); + } +} + +void Class525::update466300() { + AnimatedSprite::update(); + if (_countdown1 != 0) { + _countdown1--; + } +} + +uint32 Class525::handleMessage466320(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x0A8A1490) { + _soundResource2.play(0x6AB6666F); + } + break; + case 0x2000: + _countdown1 = 70; + _countdown2 = 8; + sub466420(); + break; + case 0x483A: + sub4664B0(); + break; + } + return messageResult; +} + +uint32 Class525::handleMessage4663C0(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + if (_countdown1 != 0) { + sub466420(); + } else { + sub466460(); + } + SetMessageHandler(&Class525::handleMessage466320); + SetUpdateHandler(&Class525::update4662A0); + break; + } + return messageResult; +} + +void Class525::sub466420() { + setFileHash(0x4C240100, 0, -1); + _soundResource1.play(0x4A30063F); +} + +void Class525::sub466460() { + // TODO Sound1ChList_deleteSoundByHash(0x4A116437); + _soundResource1.play(0x4A120435); + setFileHash(0x4C210500, 0, -1); +} + +void Class525::sub4664B0() { + setFileHash(0x6C210810, 0, -1); + SetMessageHandler(&Class525::handleMessage4663C0); + SetUpdateHandler(&Class525::update466300); +} + +Class526::Class526(NeverhoodEngine *vm, Sprite *class525) + : AnimatedSprite(vm, 1100), _soundResource(vm), _class525(class525) { + + // TODO createSurface3(100, dword_4B6778); + createSurface(100, 640, 480); //TODO: Remeove once the line above is done + _x = 478; + _y = 433; + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&Class526::handleMessage); + setFileHash(0xA282C472, 0, -1); +} + +uint32 Class526::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x66382026) { + _soundResource.play(0x0CD84468); + } else if (param.asInteger() == 0x6E28061C) { + _soundResource.play(0x78C8402C); + } else if (param.asInteger() == 0x462F0410) { + _soundResource.play(0x60984E28); + } + break; + case 0x4839: + sub466770(); + break; + } + return messageResult; +} + +void Class526::spriteUpdate466720() { + AnimatedSprite::updateDeltaXY(); + if (_rect.y1 <= 150) { + _soundResource.play(0x0E32247F); + stopAnimation(); + SetSpriteCallback(NULL); + SetMessageHandler(NULL); + setVisible(false); + } +} + +void Class526::sub466770() { + setFileHash(0x34880040, 0, -1); + SetSpriteCallback(&Class526::spriteUpdate466720); +} + +Class527::Class527(NeverhoodEngine *vm, Sprite *class526) + : AnimatedSprite(vm, 1100), _soundResource(vm), _class526(class526) { + + // TODO createSurface3(200, dword_4B6768); + createSurface(200, 640, 480); //TODO: Remeove once the line above is done + _x = 427; + _y = 433; + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&Class527::handleMessage); + setFileHash(0x461A1490, 0, -1); +} + +uint32 Class527::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x4839: + sub466970(); + break; + } + return messageResult; +} + +void Class527::spriteUpdate466920() { + AnimatedSprite::updateDeltaXY(); + if (_rect.y1 <= 150) { + _soundResource.play(0x18020439); + stopAnimation(); + SetSpriteCallback(NULL); + SetMessageHandler(NULL); + setVisible(false); + } +} + +void Class527::sub466970() { + setFileHash(0x103B8020, 0, -1); + SetSpriteCallback(&Class527::spriteUpdate466920); +} + +Class528::Class528(NeverhoodEngine *vm, Sprite *klayman, bool flag) + : AnimatedSprite(vm, 1100), _soundResource(vm), _klayman(klayman), _countdown(0) { + + _x = 320; + _y = 240; + createSurface1(0x04551900, 100); + SetUpdateHandler(&Class528::update); + SetMessageHandler(&Class528::handleMessage); + _newHashListIndex = -2; + if (flag) { + _flag = true; + setFileHash(0x04551900, -1,- 1); + _countdown = 48; + } else { + _flag = false; + stopAnimation(); + setVisible(false); + } +} + +void Class528::update() { + if (_countdown != 0 && (--_countdown == 0)) { + sub466C50(); + } + AnimatedSprite::update(); +} + + +uint32 Class528::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2001: + if (_flag) + _countdown = 168; + messageResult = _flag ? 1 : 0; + break; + case 0x3002: + removeCallbacks(); + break; + case 0x4808: + _countdown = 168; + if (_flag) + sub466BF0(); + break; + } + return messageResult; +} + +void Class528::sub466BF0() { + _flag = true; + setVisible(true); + setFileHash(0x04551900, 0, -1); + _newHashListIndex = -2; + _soundResource.play(calcHash("fxDoorOpen24")); +} + +void Class528::sub466C50() { + _flag = false; + setVisible(true); + setFileHash(0x04551900, -1, -1); + _soundResource.play(calcHash("fxDoorClose24")); + _playBackwards = true; + NextState(&Class528::sub466CB0); +} + +void Class528::sub466CB0() { + stopAnimation(); + setVisible(false); +} + +static const Class489Item kClass489Items[] = { + {{154, 453}, 4, 2, 0, -1, 0, 1}, + {{104, 391}, 4, -1, -1, -1, 1, 1}, + {{ 22, 447}, 6, -1, -1, -1, 1, 1}, + {{112, 406}, 2, -1, -1, -1, 1, 0}, + {{262, 433}, 1, 1, 0, -1, 0, 0} +}; + +Class489::Class489(NeverhoodEngine *vm, Scene *parentScene, Sprite *klayman, Sprite *class525) + : AnimatedSprite(vm, 1100), _parentScene(parentScene), _klayman(klayman), _class525(class525), + _soundResource1(vm), _soundResource2(vm), _soundResource3(vm) { + + _class489Item = &kClass489Items[getGlobalVar(0x04A105B3)]; + // TODO createSurface3(990, dword_4B26D8); + createSurface(990, 640, 480); //TODO: Remeove once the line above is done + setFileHash(0x10E3042B, 0, -1); + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&Class489::handleMessage); + _x = getGlobalVar(0x04A10F33) * 108 + _class489Item->point.x; + _flag = true; + sub434C80(); + setDoDeltaX(1); + if ((int8)getGlobalVar(0x04A10F33) == _class489Item->varIndex2) { + sub434E90(); + } + _soundResource3.load(0xC8C2507C); +} + +Class489::~Class489() { + // TODO Sound1ChList_sub_407AF0(0x05331081); +} + +uint32 Class489::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + sendMessage(_parentScene, 0x4826, 0); + messageResult = 1; + break; + case 0x4807: + setGlobalVar(0x04A10F33, (_x - _class489Item->point.x) / 108); + if ((int8)getGlobalVar(0x04A10F33) == _class489Item->varIndex2) { + sub434E60(); + } else { + sub434DD0(); + } + break; + case 0x480B: + if (param.asInteger() != 1) { + if ((int8)getGlobalVar(0x04A10F33) < _class489Item->varIndex1) { + incGlobalVar(0x04A10F33, 1); + } + } else if (getGlobalVar(0x04A10F33) > 0) { + incGlobalVar(0x04A10F33, -1); + } + sub434DF0(); + break; + case 0x480C: + if (param.asInteger() != 1) { + messageResult = (int8)getGlobalVar(0x04A10F33) < _class489Item->varIndex1 ? 1 : 0; + } else { + messageResult = getGlobalVar(0x04A10F33) > 0 ? 1 : 0; + } + break; + case 0x482A: + sendMessage(_parentScene, 0x1022, 990); + break; + case 0x482B: + sendMessage(_parentScene, 0x1022, 1010); + break; + case 0x4828: + sub435040(); + break; + } + return messageResult; +} + +uint32 Class489::handleMessage4348E0(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + if (param.asPoint().x - _x >= 17 && param.asPoint().x - _x <= 56 && + param.asPoint().y - _y >= -120 && param.asPoint().y - _y <= -82) { + sendMessage(_parentScene, 0x4826, 1); + } else { + sendMessage(_parentScene, 0x4826, 0); + } + messageResult = 1; + break; + case 0x4807: + sendMessage(_parentScene, 0x4807, 0); + sub434F80(); + break; + case 0x480B: + if (param.asInteger() != 1) { + if ((int8)getGlobalVar(0x04A10F33) < _class489Item->varIndex1) { + incGlobalVar(0x04A10F33, 1); + } + } else if (getGlobalVar(0x04A10F33) > 0) { + incGlobalVar(0x04A10F33, -1); + } + sub434FF0(); + break; + case 0x480C: + if (param.asInteger() != 1) { + messageResult = (int8)getGlobalVar(0x04A10F33) < _class489Item->varIndex1 ? 1 : 0; + } else { + messageResult = getGlobalVar(0x04A10F33) > 0 ? 1 : 0; + } + break; + case 0x480F: + sub434EC0(); + break; + case 0x482A: + sendMessage(_parentScene, 0x1022, 990); + break; + case 0x482B: + sendMessage(_parentScene, 0x1022, 1010); + break; + } + return messageResult; +} + +uint32 Class489::handleMessage434B20(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + removeCallbacks(); + break; + } + return messageResult; +} + +void Class489::spriteUpdate434B60() { + if (_x <= _klayman->getX()) + _x = _klayman->getX() - 100; + else + _x = _klayman->getX() + 100; + sub434C80(); + if (_remX == _x) { + if (getGlobalVar(0x04A10F33) == 0 && _class489Item->flag4 != 0) { + sendMessage(_parentScene, 0x1019, 0); + incGlobalVar(0x04A105B3, -1); + setGlobalVar(0x04A10F33, kClass489Items[getGlobalVar(0x04A105B3)].varIndex1); + } else if ((int8)getGlobalVar(0x04A10F33) == _class489Item->varIndex1 && _class489Item->flag != 0) { + sendMessage(_parentScene, 0x1019, 1); + incGlobalVar(0x04A105B3, +1); + setGlobalVar(0x04A10F33, 0); + } + } + Sprite::processDelta(); +} + +void Class489::sub434C80() { + + bool soundFlag = false; + + _y = _class489Item->point.y; + + if (_class489Item->index1 != -1) { + int16 elX = _class489Item->index1 * 108 + _class489Item->point.x; + if (elX - 20 < _x && elX + 20 > _x) { + soundFlag = true; + _y = _class489Item->point.y + 10; + } + } + + if (_class489Item->flag2 != -1) { + int16 elX = _class489Item->index1 * 108 + _class489Item->point.x; + if (elX - 20 < _x && elX + 20 > _x) { + soundFlag = true; + _y = _class489Item->point.y + 10; + } + } + + if (_class489Item->varIndex2 != -1) { + int16 elX = _class489Item->varIndex2 * 108 + _class489Item->point.x; + if (elX - 20 < _x && elX + 20 > _x) { + soundFlag = true; + _y = _class489Item->point.y + 10; + } + } + + if (_flag) { + if (!soundFlag) { + _flag = false; + } + } else if (soundFlag) { + _soundResource2.play(0x5440E474); + _flag = true; + } + +} + +void Class489::sub434D80() { + AnimatedSprite::updateDeltaXY(); + if (_rect.y1 <= 150) { + sendMessage(_class525, 0x483A, 0); + stopAnimation(); + SetMessageHandler(&Sprite::handleMessage); + SetSpriteCallback(NULL); + setVisible(false); + } +} + +void Class489::sub434DD0() { + SetSpriteCallback(NULL); + SetMessageHandler(&Class489::handleMessage); + setFileHash(0x10E3042B, 0, -1); +} + +void Class489::sub434DF0() { + _remX = getGlobalVar(0x04A10F33) * 108 + _class489Item->point.x; + setFileHash(0x14A10137, 0, -1); + SetSpriteCallback(&Class489::spriteUpdate434B60); + SetMessageHandler(&Class489::handleMessage); + _soundResource2.play(0xEC008474); +} + +void Class489::sub434E60() { + SetSpriteCallback(NULL); + SetMessageHandler(&Class489::handleMessage434B20); + setFileHash(0x80C32213, 0, -1); + NextState(&Class489::sub434E90); +} + +void Class489::sub434E90() { + SetSpriteCallback(NULL); + SetMessageHandler(&Class489::handleMessage4348E0); + setFileHash(0xD23B207F, 0, -1); +} + +void Class489::sub434EC0() { + setFileHash(0x50A80517, 0, -1); + SetMessageHandler(&Class489::handleMessage434B20); + SetSpriteCallback(NULL); + NextState(&Class489::sub434F40); + setGlobalVar(0x12A10DB3, 1); + _soundResource1.play(0xCC4A8456); + // TODO Sound1ChList_addSoundResource(0x05331081, 0xCE428854, true); + // TODO Sound1ChList_playLooping(0xCE428854); +} + +void Class489::sub434F40() { + sendMessage(_parentScene, 0x480F, 0); + setFileHash(0xD833207F, 0, -1); + SetSpriteCallback(NULL); + SetMessageHandler(&Class489::handleMessage4348E0); +} + +void Class489::sub434F80() { + setFileHash(0x50A94417, 0, -1); + SetSpriteCallback(NULL); + SetMessageHandler(&Class489::handleMessage434B20); + NextState(&Class489::sub434E90); + setGlobalVar(0x12A10DB3, 0); + _soundResource1.play(0xCC4A8456); + // TODO Sound1ChList_deleteSoundByHash(0xCE428854); +} + +void Class489::sub434FF0() { + _remX = getGlobalVar(0x04A10F33) * 108 + _class489Item->point.x; + setFileHash(0x22CB4A33, 0, -1); + SetSpriteCallback(&Class489::spriteUpdate434B60); + SetMessageHandler(&Class489::handleMessage434B20); + NextState(&Class489::sub434DF0); +} + +void Class489::sub435040() { + setGlobalVar(0x04A105B3, 4); + setGlobalVar(0x04A10F33, 0); + SetSpriteCallback(&Class489::sub434D80); + SetMessageHandler(&Sprite::handleMessage); + setFileHash(0x708D4712, 0, -1); + _soundResource3.play(); +} + +Scene1401::Scene1401(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule, true), _flag(false), _class427(NULL), _class489(NULL), + _class525(NULL), _class526(NULL), _class527(NULL), _class528(NULL), + _sprite1(NULL), _sprite2(NULL), _sprite3(NULL), _ssButton(NULL) { + + SetMessageHandler(&Scene1401::handleMessage); + SetUpdateHandler(&Scene1401::update); + setRectList(0x004B6758); + _surfaceFlag = true; + + setBackground(0x08221FA5); + setPalette(0x08221FA5); + insertMouse433(0x21FA108A); + + _class427 = insertSprite<Class427>(this, 0x980F3124, 0x12192892, 100, 0); + _class525 = insertSprite<Class525>(); + + if (!getGlobalVar(0x01023818)) { + _class526 = insertSprite<Class526>(_class525); + _class527 = insertSprite<Class527>(_class525); + } + + _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) { + insertKlayman<KmScene1401>(380, 447); + setMessageList(0x004B65C8); + _sprite1->setVisible(false); + } else if (which == 1) { + insertKlayman<KmScene1401>(0, 447); + setMessageList(0x004B65D0); + _sprite1->setVisible(false); + } else if (which == 2) { + insertKlayman<KmScene1401>(660, 447); + setMessageList(0x004B65D8); + _sprite1->setVisible(false); + } else { + insertKlayman<KmScene1401>(290, 413); + setMessageList(0x004B65E8); + _sprite1->setVisible(false); + } + + if (getGlobalVar(0x04A105B3) == 2) { + _class489 = insertSprite<Class489>(this, _klayman, _class525); + _vm->_collisionMan->addSprite(_class489); + if (getGlobalVar(0x04A10F33) == 6) { + sendEntityMessage(_klayman, 0x1014, _class489); + _klayman->setX(_class489->getX() + 100); + _klayman->processDelta(); + setMessageList(0x004B6670); + } else if (getGlobalVar(0x04A10F33) == 0) { + sendEntityMessage(_klayman, 0x1014, _class489); + _klayman->setX(_class489->getX() - 100); + _klayman->processDelta(); + setMessageList(0x004B6670); + } + _class489->setClipRect(_sprite3->getDrawRect().x, _sprite2->getDrawRect().y, 640, 480); + } + + _klayman->setClipRect(_sprite3->getDrawRect().x, 0, 640, 480); + + if (which == 0 && _class489) { + sendMessage(_class489, 0x482B, 0); + } + + _class528 = insertSprite<Class528>(_klayman, which == 1); + +} + +void Scene1401::update() { + Scene::update(); + if (_class489 && !_flag && _class489->getY() < 360) { + _sprite2->setVisible(true); + _flag = true; + } else { + _sprite2->setVisible(false); + } +} + +uint32 Scene1401::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x02144CB1) { + sendEntityMessage(_klayman, 0x1014, _class427); + } else if (param.asInteger() == 0x402064D8) { + sendEntityMessage(_klayman, 0x1014, _ssButton); + } else if (param.asInteger() == 0x01C66840) { + if (sendMessage(_class528, 0x2001, 0) != 0) { + setMessageList(0x004B6690); + } else { + setMessageList(0x004B66B0); + } + } + break; + case 0x1019: + if (param.asInteger() != 0) { + leaveScene(2); + } else { + leaveScene(1); + } + break; + case 0x480B: + if (sender == _class427) { + sendMessage(_class525, 0x2000, 0); + if (!getGlobalVar(0x01023818)) { + sendMessage(_class526, 0x4839, 0); + sendMessage(_class527, 0x4839, 0); + setGlobalVar(0x01023818, 1); + } + if (_class489 && _class489->getX() > 404 && _class489->getX() < 504) { + sendMessage(_class489 , 0x4839, 0); + } + } else if (sender == _ssButton) { + sendMessage(_ssButton, 0x4808, 0); + } + break; + case 0x4826: + if (sender == _class489) { + if (sendMessage(_class489, 0x480C, _klayman->getX() > _class489->getX() ? 1 : 0) != 0) { + sendEntityMessage(_klayman, 0x1014, _class489); + setMessageList2(0x004B6658); + } else { + setMessageList2(0x004B65F0); + } + } + break; + case 0x482A: + _sprite1->setVisible(true); + if (_class489) { + sendMessage(_class489, 0x482B, 0); + } + break; + case 0x482B: + _sprite1->setVisible(false); + if (_class489) { + sendMessage(_class489, 0x482A, 0); + } + break; + } + return 0; +} + +// Scene1402 + +Class454::Class454(NeverhoodEngine *vm, uint32 fileHash, int surfacePriority) + : StaticSprite(vm, fileHash, surfacePriority) { + + SetFilterY(&Sprite::defFilterY); + SetUpdateHandler(&StaticSprite::update); + +} + +Class482::Class482(NeverhoodEngine *vm, Scene *parentScene, int which) + : AnimatedSprite(vm, 1100), _parentScene(parentScene), _soundResource1(vm), + _soundResource2(vm) { + + // TODO createSurface3(900, dword_4B6768); + createSurface(900, 640, 480); //TODO: Remeove once the line above is done + + SetFilterY(&Sprite::defFilterY); + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&Class482::handleMessage); + _x = 279; + _y = 270; + if (which == 2) { + setFileHash(0x20060259, 0, -1); + _soundResource1.play(0x419014AC); + _soundResource2.load(0x61901C29); + } else if (which == 1) { + setFileHash(0x210A0213, 0, -1); + _soundResource1.play(0x41809C6C); + } else { + setFileHash(0x20060259, 0, -1); + _soundResource2.load(0x61901C29); + _newHashListIndex = -2; + } +} + +uint32 Class482::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2002: + _soundResource2.play(); + setFileHash(0x20060259, -1, -1); + _playBackwards = true; + NextState(&Class482::sub428530); + break; + case 0x3002: + removeCallbacks(); + break; + } + return messageResult; +} + +void Class482::sub428500() { + sendMessage(_parentScene, 0x2000, 0); + stopAnimation(); + setVisible(false); +} + +void Class482::sub428530() { + sendMessage(_parentScene, 0x2001, 0); + stopAnimation(); + setVisible(false); +} + +void Class482::sub428560() { + sendMessage(_parentScene, 0x2003, 0); + stopAnimation(); +} + +Scene1402::Scene1402(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule, true), _flag(false), _class482(NULL), _class489(NULL) { + + SetMessageHandler(&Scene1402::handleMessage); + + setBackground(0x231482F0); + setBackgroundY(-10); + // TODO g_screen->field_26 = 0; + setPalette(0x231482F0); + _palette->addPalette(0x91D3A391, 0, 64, 0); + insertMouse433(0x482F4239); + + _class454_1 = insertSprite<Class454>(0x15402D64, 1100); + _class454_2 = insertSprite<Class454>(0x10A02120, 1100); + _class454_3 = insertSprite<Class454>(0x60882BE0, 1100); + + if (getGlobalVar(0x70A1189C)) + setRectList(0x004B0C48); + else + setRectList(0x004B0C98); + + if (which < 0) { + insertKlayman<KmScene1402>(377, 391); + setMessageList(0x004B0B48); + if (!getGlobalVar(0x70A1189C)) { + _class482 = insertSprite<Class482>(this, 0); + } + } else if (which == 1) { + insertKlayman<KmScene1402>(42, 391); + setMessageList(0x004B0B50); + } else if (which == 2) { + insertKlayman<KmScene1402>(377, 391); + setMessageList(0x004B0B60); + _klayman->setDoDeltaX(1); + if (getGlobalVar(0x70A1189C)) { + _class482 = insertSprite<Class482>(this, 1); + clearRectList(); + showMouse(false); + sub428220(); + } else { + _class482 = insertSprite<Class482>(this, 0); + } + } else { + insertKlayman<KmScene1402>(513, 391); + setMessageList(0x004B0B58); + if (!getGlobalVar(0x70A1189C)) { + _class482 = insertSprite<Class482>(this, 2); + sub428220(); + } + } + + if (_class482) { + _class482->setClipRect(0, 0, 640, _class454_3->getDrawRect().y2()); + } + + if (getGlobalVar(0x4A105B3) == 1) { + _class489 = insertSprite<Class489>(this, _klayman, (Sprite*)NULL); + _vm->_collisionMan->addSprite(_class489); + if (getGlobalVar(0x4A10F33) == 4) { + sendEntityMessage(_klayman, 0x1014, _class489); + _klayman->setX(_class489->getX() + 100); + _klayman->processDelta(); + setMessageList(0x004B0BD0); + } else if (getGlobalVar(0x4A10F33) == 0) { + sendEntityMessage(_klayman, 0x1014, _class489); + _klayman->setX(_class489->getX() - 100); + _klayman->processDelta(); + setMessageList(0x004B0BD0); + } + _class489->setClipRect(_class454_1->getDrawRect().x, 0, _class454_2->getDrawRect().x, _class454_3->getDrawRect().y2()); + } + + _klayman->setClipRect(_class454_1->getDrawRect().x, 0, _class454_2->getDrawRect().x2(), _class454_3->getDrawRect().y2()); + +} + +void Scene1402::update() { + if (_flag) { + setBackgroundY(_vm->_rnd->getRandomNumber(10 - 1) - 10); + // TODO g_screen->field_26 = -10 - _background->getDrawRect().y; + } else { + setBackgroundY(-10); + // TODO g_screen->field_26 = 0; + SetUpdateHandler(&Scene::update); + } + Scene::update(); + if (_class482) { + _class482->setClipRect(0, 0, 640, _class454_3->getDrawRect().y2()); + } + _klayman->setClipRect(_class454_1->getDrawRect().x, 0, _class454_2->getDrawRect().x2(), _class454_3->getDrawRect().y2()); +} + +uint32 Scene1402::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x00F43389) { + if (getGlobalVar(0x70A1189C)) { + leaveScene(0); + } else { + clearRectList(); + _klayman->setVisible(false); + showMouse(false); + sendMessage(_class482, 0x2002, 0); + sub428220(); + } + } + break; + case 0x1019: + if (param.asInteger()) { + leaveScene(0); + } else { + leaveScene(1); + } + break; + case 0x2000: + sub428230(); + showMouse(true); + setRectList(0x004B0C48); + break; + case 0x2001: + sub428230(); + leaveScene(0); + break; + case 0x2003: + sub428230(); + break; + case 0x4826: + if (sender == _class489) { + if (sendMessage(_class489, 0x408C, _klayman->getX() > _class489->getX() ? 1 : 0) != 0) { + sendEntityMessage(_klayman, 0x1014, _class489); + setMessageList2(0x004B0BB8); + } else { + setMessageList2(0x004B0B68); + } + } + } + return 0; +} + +void Scene1402::sub428220() { + _flag = true; + SetUpdateHandler(&Scene1402::update); +} + +void Scene1402::sub428230() { + _flag = false; +} + +// Scene1407 + +static const int16 kScene1407MouseFloorY[] = { + 106, 150, 191, 230, 267, 308, 350, 395 +}; + +static const struct { + int16 x; + int16 floorIndex; + int16 sectionIndex; + int16 nextHoleIndex; +} kScene1407MouseHoles[] = { + {125, 0, 0, 7}, + {452, 7, 21, 0}, + {337, 4, 11, 4}, + {286, 6, 17, 6}, + {348, 6, 17, 39}, + {536, 6, 18, 42}, + {111, 1, 3, 18}, + {203, 1, 3, 38}, + {270, 1, 3, 9}, + {197, 5, 14, 3}, + {252, 5, 14, 35}, + {297, 5, 14, 7}, + {359, 5, 14, 8}, + {422, 4, 12, 26}, + {467, 4, 12, 2}, + {539, 4, 12, 40}, + {111, 5, 13, 17}, + {211, 0, 1, 20}, + {258, 0, 1, 11}, + {322, 0, 1, 16}, + { 99, 6, 16, 31}, + {142, 6, 16, 27}, + {194, 6, 16, 12}, + {205, 2, 6, 45}, + {264, 2, 6, 10}, + { 98, 4, 10, 2}, + {152, 4, 10, 37}, + {199, 4, 10, 13}, + {258, 4, 10, 16}, + {100, 7, 19, 43}, + {168, 7, 19, 23}, + {123, 3, 8, 14}, + {181, 3, 8, 39}, + {230, 3, 8, 28}, + {292, 3, 8, 22}, + {358, 3, 8, 36}, + {505, 3, 9, 44}, + {400, 2, 7, 34}, + {454, 2, 7, 32}, + {532, 2, 7, 46}, + {484, 5, 15, 25}, + {529, 5, 15, 30}, + {251, 7, 20, 48}, + {303, 7, 20, 21}, + {360, 7, 20, 33}, + {503, 0, 2, 5}, + {459, 1, 4, 19}, + {530, 1, 4, 42}, + {111, 2, 5, 47}, + {442, 6, 18, 1} +}; + +static const struct { + int16 x1, x2; + int16 goodHoleIndex; +} kScene1407MouseSections[] = { + {100, 149, 0}, + {182, 351, 17}, + {430, 524, 45}, + { 89, 293, 7}, + {407, 555, 47}, + { 89, 132, 48}, + {178, 303, 23}, + {367, 551, 38}, + {105, 398, 31}, + {480, 537, 36}, + { 84, 275, 27}, + {318, 359, 2}, + {402, 560, 15}, + { 91, 132, 16}, + {179, 400, 10}, + {461, 552, 41}, + { 86, 218, 21}, + {267, 376, 4}, + {420, 560, 49}, + { 77, 188, 30}, + {237, 394, 44}, + {438, 515, 5} +}; + +AsScene1407Mouse::AsScene1407Mouse(NeverhoodEngine *vm, Scene *parentScene) + : AnimatedSprite(vm, 1100), _parentScene(parentScene), _currSectionIndex(0) { + + // TODO createSurface3(100, dword_4B05B0); + createSurface(100, 640, 480); //TODO: Remeove once the line above is done + + SetUpdateHandler(&AnimatedSprite::update); + _x = 108; + _y = 106; + stIdleLookAtGoodHole(); +} + +void AsScene1407Mouse::suWalkTo() { + int16 xdelta = _walkDestX - _x; + if (xdelta > _deltaX) + xdelta = _deltaX; + else if (xdelta < -_deltaX) + xdelta = -_deltaX; + _deltaX = 0; + if (_walkDestX == _x) { + sendMessage(this, 0x1019, 0); + } else { + _x += xdelta; + processDelta(); + } +} + +void AsScene1407Mouse::upGoThroughHole() { + if (_countdown != 0 && (--_countdown == 0)) { + SetUpdateHandler(&AnimatedSprite::update); + removeCallbacks(); + } + AnimatedSprite::update(); +} + +uint32 AsScene1407Mouse::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x0001: + { + int16 mouseX = param.asPoint().x; + int16 mouseY = param.asPoint().y; + int holeIndex; + for (holeIndex = 0; holeIndex < 50; holeIndex++) { + int16 holeX = kScene1407MouseHoles[holeIndex].x; + int16 holeY = kScene1407MouseFloorY[kScene1407MouseHoles[holeIndex].floorIndex]; + if (mouseX >= holeX - 14 && mouseX <= holeX + 14 && mouseY >= holeY - 36 && mouseY <= holeY) + break; + } + if (holeIndex < 50 && kScene1407MouseHoles[holeIndex].sectionIndex == _currSectionIndex) { + _nextHoleIndex = kScene1407MouseHoles[holeIndex].nextHoleIndex; + _walkDestX = kScene1407MouseHoles[holeIndex].x; + stWalkToHole(); + } else { + if (mouseX < kScene1407MouseSections[_currSectionIndex].x1) { + _walkDestX = kScene1407MouseSections[_currSectionIndex].x1; + } else if (mouseX > kScene1407MouseSections[_currSectionIndex].x2) { + _walkDestX = kScene1407MouseSections[_currSectionIndex].x2; + } else { + _walkDestX = mouseX; + } + stWalkToDest(); + } + } + break; + case 0x1019: + removeCallbacks(); + break; + case 0x2001: + { + // Reset the position + // Find the nearest hole and go through it, and exit at the first hole + int16 distance = 640; + int matchIndex = 50; + for (int index = 0; index < 50; index++) { + if (kScene1407MouseHoles[index].sectionIndex == _currSectionIndex) { + if (ABS(kScene1407MouseHoles[index].x - _x) < distance) { + matchIndex = index; + distance = ABS(kScene1407MouseHoles[index].x - _x); + } + } + } + if (matchIndex < 50) { + _nextHoleIndex = 0; + _walkDestX = kScene1407MouseHoles[matchIndex].x; + stWalkToHole(); + } + } + break; + } + return messageResult; +} + +void AsScene1407Mouse::stIdleLookAtGoodHole() { + setDoDeltaX(kScene1407MouseHoles[kScene1407MouseSections[_currSectionIndex].goodHoleIndex].x < _x ? 1 : 0); + setFileHash(0x72215194, 0, -1); + SetSpriteCallback(NULL); + SetMessageHandler(&AsScene1407Mouse::handleMessage); +} + +void AsScene1407Mouse::stWalkToDest() { + if (_walkDestX != _x) { + setDoDeltaX(_walkDestX < _x ? 1 : 0); + setFileHash(0x22291510, 0, -1); + SetSpriteCallback(&AsScene1407Mouse::suWalkTo); + SetMessageHandler(&AsScene1407Mouse::handleMessage); + NextState(&AsScene1407Mouse::stIdleLookAtGoodHole); + } +} + +void AsScene1407Mouse::stWalkToHole() { + setDoDeltaX(_walkDestX < _x ? 1 : 0); + setFileHash(0x22291510, 0, -1); + SetSpriteCallback(&AsScene1407Mouse::suWalkTo); + SetMessageHandler(&AsScene1407Mouse::handleMessage); + NextState(&AsScene1407Mouse::stGoThroughHole); +} + +void AsScene1407Mouse::stGoThroughHole() { + setFileHash(0x72215194, 0, -1); + SetSpriteCallback(NULL); + SetMessageHandler(NULL); + SetUpdateHandler(&AsScene1407Mouse::upGoThroughHole); + NextState(&AsScene1407Mouse::stArriveAtHole); + setVisible(false); + _countdown = 12; +} + +void AsScene1407Mouse::stArriveAtHole() { + _currSectionIndex = kScene1407MouseHoles[_nextHoleIndex].sectionIndex; + _x = kScene1407MouseHoles[_nextHoleIndex].x; + _y = kScene1407MouseFloorY[kScene1407MouseHoles[_nextHoleIndex].floorIndex]; + if (_nextHoleIndex == 1) { + sendMessage(_parentScene, 0x2000, 0); + _walkDestX = 512; + stWalkToDest(); + setVisible(true); + } else { + _walkDestX = _x + 14; + stWalkToDest(); + setVisible(true); + } +} + +Scene1407::Scene1407(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule, true), _soundResource(vm), _puzzleSolvedCountdown(0), + _resetButtonCountdown(0) { + + _surfaceFlag = true; + + SetMessageHandler(&Scene1407::handleMessage); + SetUpdateHandler(&Scene1407::update); + + setBackground(0x00442225); + setPalette(0x00442225); + insertMouse435(0x4222100C, 20, 620); + + _asMouse = insertSprite<AsScene1407Mouse>(this); + _ssResetButton = insertStaticSprite(0x12006600, 100); + _ssResetButton->setVisible(false); + +} + +void Scene1407::update() { + Scene::update(); + if (_puzzleSolvedCountdown != 0 && (--_puzzleSolvedCountdown == 0)) { + leaveScene(1); + } else if (_resetButtonCountdown != 0 && (--_resetButtonCountdown == 0)) { + _ssResetButton->setVisible(false); + } +} + +uint32 Scene1407::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x0001: + if (_puzzleSolvedCountdown == 0) { + // TODO: Debug/Cheat stuff + 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); + _soundResource.play(0x44045000); + _resetButtonCountdown = 12; + } else { + // Handle the mouse + sendMessage(_asMouse, messageNum, param); + } + } + break; + case 0x000D: + // TODO: Debug/Cheat stuff + break; + case 0x2000: + // The mouse got the cheese (nomnom) + setGlobalVar(0x70A1189C, 1); + _soundResource.play(0x68E25540); + showMouse(false); + _puzzleSolvedCountdown = 72; + break; + } + return 0; +} + +// Scene1403 + +Scene1403::Scene1403(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule, true), _class489(NULL), _flag(false) { + + SetMessageHandler(&Scene1403::handleMessage); + + setRectList(0x004B1FF8); + _surfaceFlag = true; + + setBackground(0x2110A234); + setPalette(0x2110A234); + insertMouse433(0x0A230219); + + _class401_1 = insertStaticSprite(0x01102A33, 100); + _class401_1->setVisible(false); + + _class401_2 = insertStaticSprite(0x04442520, 995); + + _class401_3 = insertStaticSprite(0x08742271, 995); + + _asTape1 = insertSprite<AsScene1201Tape>(this, 12, 1100, 201, 468, 0x9148A011); + _vm->_collisionMan->addSprite(_asTape1); + _asTape1->setRepl(64, 0); + + _asTape2 = insertSprite<AsScene1201Tape>(this, 16, 1100, 498, 468, 0x9048A093); + _vm->_collisionMan->addSprite(_asTape2); + _asTape2->setRepl(64, 0); + + if (which < 0) { + insertKlayman<KmScene1402>(380, 463); + setMessageList(0x004B1F18); + } else { + insertKlayman<KmScene1402>(640, 463); + setMessageList(0x004B1F20); + } + _klayman->setRepl(64, 0); + + if (getGlobalVar(0x04A105B3) == 4) { + _class489 = insertSprite<Class489>(this, _klayman, (Sprite*)NULL); + _vm->_collisionMan->addSprite(_class489); + if (getGlobalVar(0x04A10F33) == 4) { + sendEntityMessage(_klayman, 0x1014, _class489); + _klayman->setX(_class489->getX() + 100); + _klayman->processDelta(); + setMessageList(0x004B1F70); + } + _class489->setClipRect(0, 0, 640, _class401_2->getDrawRect().y2()); + _class489->setRepl(64, 0); + } + +} + +uint32 Scene1403::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x88C11390) { + setRectList(0x004B2008); + _flag = true; + } else if (param.asInteger() == 0x08821382) { + sendEntityMessage(_klayman, 0x1014, _class489); + setRectList(0x004B1FF8); + _flag = false; + } + break; + case 0x1019: + leaveScene(0); + break; + case 0x1022: + if (sender == _class489) { + if (param.asInteger() >= 1000) { + setSurfacePriority(_class401_3->getSurface(), 1100); + } else { + setSurfacePriority(_class401_3->getSurface(), 995); + } + } + break; + case 0x4807: + _class401_1->setVisible(false); + break; + case 0x480F: + _class401_1->setVisible(true); + break; + case 0x4826: + if (sender == _class489) { + if (_flag) { + setMessageList2(0x004B1FA8); + } else if (param.asInteger() == 1) { + sendEntityMessage(_klayman, 0x1014, _class489); + setMessageList2(0x004B1F88); + } else if (sendMessage(_class489, 0x480C, _klayman->getX() > _class489->getX() ? 1 : 0) != 0) { + sendEntityMessage(_klayman, 0x1014, _class489); + setMessageList2(0x004B1F58); + } else { + setMessageList2(0x004B1F28); + } + } else if (sender == _asTape1 || sender == _asTape2) { + if (_flag) { + setMessageList2(0x004B1FA8); + } else if (_messageListStatus != 2) { + sendEntityMessage(_klayman, 0x1014, sender); + setMessageList2(0x004B1FB8); + } + } + break; + } + return 0; +} + +// Scene1404 + +Scene1404::Scene1404(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule, true), _class489(NULL), _class545(NULL) { + + if (getGlobalVar(0xC0780812) && !getGlobalVar(0x13382860)) { + setGlobalVar(0x13382860, 5); + } + + SetMessageHandler(&Scene1404::handleMessage); + _surfaceFlag = true; + + setRectList(0x004B8D80); + + setBackground(0xAC0B006F); + setPalette(0xAC0B006F); + _palette->addPalette(0x00801510, 0, 65, 0); + insertMouse433(0xB006BAC8); + + if (getGlobalVar(0x13382860) == 5) { + _class545 = insertSprite<Class545>(this, 2, 1100, 267, 411); + _vm->_collisionMan->addSprite(_class545); + } + + _sprite1 = insertStaticSprite(0x1900A1F8, 1100); + + _asTape = insertSprite<AsScene1201Tape>(this, 14, 1100, 281, 411, 0x9148A011); + _vm->_collisionMan->addSprite(_asTape); + + if (which < 0) { + insertKlayman<KmScene1404>(376, 406); + setMessageList(0x004B8C28); + } else if (which == 1) { + insertKlayman<KmScene1404>(376, 406); + setMessageList(0x004B8C30); + } else if (which == 2) { + if (getGlobalVar(0xC0418A02)) { + insertKlayman<KmScene1404>(347, 406); + _klayman->setDoDeltaX(1); + } else { + insertKlayman<KmScene1404>(187, 406); + } + setMessageList(0x004B8D28); + } else { + insertKlayman<KmScene1404>(30, 406); + setMessageList(0x004B8C38); + } + + if (getGlobalVar(0x04A105B3) == 3) { + _class489 = insertSprite<Class489>(this, _klayman, (Sprite*)NULL); + _vm->_collisionMan->addSprite(_class489); + if (getGlobalVar(0x04A10F33) == 0) { + sendEntityMessage(_klayman, 0x1014, _class489); + _klayman->setX(_class489->getX() - 100); + _klayman->processDelta(); + setMessageList(0x004B8CB8); + } + _class489->setClipRect(_sprite1->getDrawRect().x, 0, 640, 480); + } + + _klayman->setClipRect(_sprite1->getDrawRect().x, 0, 640, 480); + +} + +Scene1404::~Scene1404() { + setGlobalVar(0xC0418A02, _klayman->isDoDeltaX() ? 1 : 0); +} + +uint32 Scene1404::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x410650C2) { + if (_class489 && _class489->getX() == 220) { + setMessageList(0x004B8C40); + } else { + setMessageList(0x004B8CE8); + } + } + break; + case 0x1019: + leaveScene(0); + break; + case 0x4826: + if (sender == _class489) { + if (sendMessage(_class489, 0x480C, _klayman->getX() > _class489->getX() ? 1 : 0) != 0) { + sendEntityMessage(_klayman, 0x1014, _class489); + setMessageList2(0x004B8CA0); + } else { + setMessageList2(0x004B8C40); + } + } else if (sender == _asTape && _messageListStatus != 2) { + sendEntityMessage(_klayman, 0x1014, _asTape); + setMessageList(0x004B8CD0); + } else if (sender == _class545 && _messageListStatus != 2) { + sendEntityMessage(_klayman, 0x1014, _class545); + setMessageList(0x004B8D18); + } + break; + } + return 0; +} + +// Scene1405 + +static const NPoint kAsScene1405TileItemPositions[] = { + {100, 80}, + {162, 78}, + {222, 76}, + {292, 76}, + {356, 82}, + {422, 84}, + {488, 86}, + {550, 90}, + {102, 134}, + {164, 132}, + {224, 136}, + {294, 136}, + {360, 136}, + {422, 138}, + {484, 144}, + {548, 146}, + { 98, 196}, + {160, 200}, + {228, 200}, + {294, 202}, + {360, 198}, + {424, 200}, + {482, 202}, + {548, 206}, + { 98, 260}, + {160, 264}, + {226, 260}, + {296, 262}, + {358, 260}, + {424, 262}, + {486, 264}, + {550, 266}, + { 94, 322}, + {160, 316}, + {226, 316}, + {296, 320}, + {358, 322}, + {422, 324}, + {488, 322}, + {550, 322}, + { 98, 380}, + {160, 376}, + {226, 376}, + {294, 378}, + {356, 380}, + {420, 380}, + {490, 378}, + {552, 376} +}; + +AsScene1405Tile::AsScene1405Tile(NeverhoodEngine *vm, Scene1405 *parentScene, uint32 index) + : AnimatedSprite(vm, 1100), _parentScene(parentScene), _soundResource(vm), + _index(index), _countdown(0), _flag(false) { + + _soundResource.load(0x05308101); + // TODO _soundResource.setPan + _x = kAsScene1405TileItemPositions[_index].x; + _y = kAsScene1405TileItemPositions[_index].y; + createSurface1(0x844B805C, 1100); + setVisible(false); + if (getSubVar(0xCCE0280F, _index)) + _countdown = _vm->_rnd->getRandomNumber(36 - 1) + 1; + SetUpdateHandler(&AsScene1405Tile::update); + SetMessageHandler(&AsScene1405Tile::handleMessage); + + debug("getSubVar(0x0C65F80B, _index) = %d", getSubVar(0x0C65F80B, _index)); + + setFileHash(0x844B805C, getSubVar(0x0C65F80B, _index), -1); + _newHashListIndex = (int16)getSubVar(0x0C65F80B, _index); +} + +void AsScene1405Tile::update() { + updateAnim(); + updatePosition(); + if (_countdown != 0 && (--_countdown == 0)) { + show(); + } +} + +uint32 AsScene1405Tile::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + if (getSubVar(0xCCE0280F, _index) == 0 && _parentScene->getCountdown() == 0) { + show(); + sendMessage(_parentScene, 0x2000, _index); + } + messageResult = 1; + break; + } + return messageResult; +} + +void AsScene1405Tile::show() { + if (!_flag) { + _flag = true; + _soundResource.play(); + setVisible(true); + } +} + +void AsScene1405Tile::hide() { + if (_flag) { + _flag = false; + _soundResource.play(); + setVisible(false); + } +} + +Scene1405::Scene1405(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule, true), _soundResource(vm), _selectFirstTile(true), + _tilesLeft(48), _countdown(0) { + + _vm->gameModule()->initScene1405Vars(); + _surfaceFlag = true; + + setBackground(0x0C0C007D); + setPalette(0x0C0C007D); + insertMouse435(0xC00790C8, 20, 620); + + // TODO: Some debug code: Leave two matching tiles open + for (int i = 0; i < 48; i++) + setSubVar(0xCCE0280F, i, 1); + int debugIndex = 0; + setSubVar(0xCCE0280F, debugIndex, 0); + for (int i = 0; i < 48; i++) { + if (i != debugIndex && getSubVar(0x0C65F80B, i) == getSubVar(0x0C65F80B, debugIndex)) { + setSubVar(0xCCE0280F, i, 0); + break; + } + } + + for (uint32 index = 0; index < 48; index++) { + _tiles[index] = insertSprite<AsScene1405Tile>(this, index); + _vm->_collisionMan->addSprite(_tiles[index]); + if (getSubVar(0xCCE0280F, index)) + _tilesLeft--; + } + + _soundResource.load(0x68E25540); + + SetMessageHandler(&Scene1405::handleMessage); + SetUpdateHandler(&Scene1405::update); + +} + +void Scene1405::update() { + Scene::update(); + if (_countdown != 0 && (--_countdown == 0)) { + _tilesLeft = 48; + _tiles[_firstTileIndex]->hide(); + _tiles[_secondTileIndex]->hide(); + for (uint32 i = 0; i < 48; i++) { + if (getSubVar(0xCCE0280F, i)) { + _tiles[i]->hide(); + setSubVar(0xCCE0280F, i, 0); + } + } + } +} + +uint32 Scene1405::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x0001: + // TODO: Debug/Cheat stuff + if (param.asPoint().x <= 20 || param.asPoint().x >= 620) { + leaveScene(0); + } + break; + case 0x000D: + // TODO: Debug/Cheat stuff + break; + case 0x2000: + if (_selectFirstTile) { + _firstTileIndex = param.asInteger(); + _selectFirstTile = false; + } else { + _secondTileIndex = param.asInteger(); + if (_firstTileIndex != _secondTileIndex) { + _selectFirstTile = true; + if (getSubVar(0x0C65F80B, _secondTileIndex) == getSubVar(0x0C65F80B, _firstTileIndex)) { + setSubVar(0xCCE0280F, _firstTileIndex, 1); + setSubVar(0xCCE0280F, _secondTileIndex, 1); + _tilesLeft -= 2; + if (_tilesLeft == 0) { + _soundResource.play(); + } + } else { + _countdown = 10; + } + } + } + break; + } + return 0; +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/module1400.h b/engines/neverhood/module1400.h new file mode 100644 index 0000000000..d256b82134 --- /dev/null +++ b/engines/neverhood/module1400.h @@ -0,0 +1,296 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef NEVERHOOD_MODULE1400_H +#define NEVERHOOD_MODULE1400_H + +#include "neverhood/neverhood.h" +#include "neverhood/module.h" +#include "neverhood/scene.h" +#include "neverhood/module1200.h" + +namespace Neverhood { + +class Module1400 : public Module { +public: + Module1400(NeverhoodEngine *vm, Module *parentModule, int which); + virtual ~Module1400(); +protected: + void createScene(int sceneNum, int which); + void updateScene(); +}; + +// Scene1401 + +class Class525 : public AnimatedSprite { +public: + Class525(NeverhoodEngine *vm); + virtual ~Class525(); +protected: + int _countdown1; + int _countdown2; + SoundResource _soundResource1; + SoundResource _soundResource2; + void update4662A0(); + void update466300(); + uint32 handleMessage466320(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 handleMessage4663C0(int messageNum, const MessageParam ¶m, Entity *sender); + void sub466420(); + void sub466460(); + void sub4664B0(); +}; + +class Class526 : public AnimatedSprite { +public: + Class526(NeverhoodEngine *vm, Sprite *class525); +protected: + Sprite *_class525; + SoundResource _soundResource; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void spriteUpdate466720(); + void sub466770(); +}; + +class Class527 : public AnimatedSprite { +public: + Class527(NeverhoodEngine *vm, Sprite *class526); +protected: + Sprite *_class526; + SoundResource _soundResource; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void spriteUpdate466920(); + void sub466970(); +}; + +class Class528 : public AnimatedSprite { +public: + Class528(NeverhoodEngine *vm, Sprite *klayman, bool flag); +protected: + Sprite *_klayman; + SoundResource _soundResource; + int _countdown; + bool _flag; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void sub466BF0(); + void sub466C50(); + void sub466CB0(); +}; + +struct Class489Item { + NPoint point; + int8 varIndex1; + int8 varIndex2; + int8 index1; + int8 flag2; + int8 flag4; + int8 flag; +}; + +class Class489 : public AnimatedSprite { +public: + Class489(NeverhoodEngine *vm, Scene *parentScene, Sprite *klayman, Sprite *class525); + virtual ~Class489(); +protected: + Scene *_parentScene; + Sprite *_klayman; + Sprite *_class525; + const Class489Item *_class489Item; + SoundResource _soundResource1; + SoundResource _soundResource2; + SoundResource _soundResource3; + int16 _remX; + bool _flag; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 handleMessage4348E0(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 handleMessage434B20(int messageNum, const MessageParam ¶m, Entity *sender); + void spriteUpdate434B60(); + void sub434C80(); + void sub434D80(); + void sub434DD0(); + void sub434DF0(); + void sub434E60(); + void sub434E90(); + void sub434EC0(); + void sub434F40(); + void sub434F80(); + void sub434FF0(); + void sub435040(); +}; + +class Scene1401 : public Scene { +public: + Scene1401(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + bool _flag; + Sprite *_class427; + Class489 *_class489; + Sprite *_class525; + Sprite *_class526; + Sprite *_class527; + Sprite *_class528; + Sprite *_sprite1; + Sprite *_sprite2; + Sprite *_sprite3; + Sprite *_ssButton; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +// Scene1402 + +class Class454 : public StaticSprite { +public: + Class454(NeverhoodEngine *vm, uint32 fileHash, int surfacePriority); +}; + +class Class482 : public AnimatedSprite { +public: + Class482(NeverhoodEngine *vm, Scene *parentScene, int which); +protected: + Scene *_parentScene; + SoundResource _soundResource1; + SoundResource _soundResource2; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void sub428500(); + void sub428530(); + void sub428560(); +}; + +class Scene1402 : public Scene { +public: + Scene1402(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + Sprite *_class454_1; + Sprite *_class454_2; + Sprite *_class454_3; + Sprite *_class482; + Class489 *_class489; + bool _flag; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void sub428220(); + void sub428230(); +}; + +// Scene1407 + +class AsScene1407Mouse : public AnimatedSprite { +public: + AsScene1407Mouse(NeverhoodEngine *vm, Scene *parentScene); +protected: + Scene *_parentScene; + int16 _walkDestX; + int16 _currSectionIndex; + int16 _nextHoleIndex; + int _countdown; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void suWalkTo(); + void upGoThroughHole(); + void stIdleLookAtGoodHole(); + void stWalkToDest(); + void stWalkToHole(); + void stGoThroughHole(); + void stArriveAtHole(); +}; + +class Scene1407 : public Scene { +public: + Scene1407(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + SoundResource _soundResource; + Sprite *_asMouse; + Sprite *_ssResetButton; + int _puzzleSolvedCountdown; + int _resetButtonCountdown; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +// Scene1403 + +class Scene1403 : public Scene { +public: + Scene1403(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + Sprite *_class401_1; + Sprite *_class401_2; + Sprite *_class401_3; + AsScene1201Tape *_asTape1; + AsScene1201Tape *_asTape2; + Class489 *_class489; + bool _flag; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +// Scene1404 + +class Scene1404 : public Scene { +public: + Scene1404(NeverhoodEngine *vm, Module *parentModule, int which); + virtual ~Scene1404(); +protected: + Sprite *_sprite1; + Sprite *_asTape; + Class489 *_class489; + Sprite *_class545; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +// Scene1405 + +class Scene1405; + +class AsScene1405Tile : public AnimatedSprite { +public: + AsScene1405Tile(NeverhoodEngine *vm, Scene1405 *parentScene, uint32 index); + void show(); + void hide(); +protected: + Scene1405 *_parentScene; + SoundResource _soundResource; + bool _flag; + uint32 _index; + int _countdown; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene1405 : public Scene { +public: + Scene1405(NeverhoodEngine *vm, Module *parentModule, int which); + int getCountdown() const { return _countdown; } +protected: + SoundResource _soundResource; + bool _selectFirstTile; + int _firstTileIndex; + int _secondTileIndex; + int _tilesLeft; + int _countdown; + AsScene1405Tile *_tiles[48]; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_MODULE1400_H */ diff --git a/engines/neverhood/module1500.cpp b/engines/neverhood/module1500.cpp new file mode 100644 index 0000000000..76afb956ff --- /dev/null +++ b/engines/neverhood/module1500.cpp @@ -0,0 +1,147 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "neverhood/module1500.h" + +namespace Neverhood { + +Module1500::Module1500(NeverhoodEngine *vm, Module *parentModule, int which, bool flag) + : Module(vm, parentModule), _flag(flag) { + + if (which < 0) { + createScene(_vm->gameState().sceneNum, -1); + } else { + createScene(3, -1); + } + +} + +void Module1500::createScene(int sceneNum, int which) { + debug("Module1500::createScene(%d, %d)", sceneNum, which); + _vm->gameState().sceneNum = sceneNum; + switch (_vm->gameState().sceneNum) { + case 0: + _childObject = new Scene1501(_vm, this, 0x8420221D, 0xA61024C4, 150, 48); + break; + case 1: + _childObject = new Scene1501(_vm, this, 0x30050A0A, 0x58B45E58, 110, 48); + break; + case 2: + sendMessage(_parentModule, 0x0800, 0); + createSmackerScene(0x001A0005, true, true, true); + break; + case 3: + _childObject = new Scene1501(_vm, this, 0x0CA04202, 0, 110, 48); + break; + } + SetUpdateHandler(&Module1500::updateScene); + _childObject->handleUpdate(); +} + +void Module1500::updateScene() { + if (!updateChild()) { + switch (_vm->gameState().sceneNum) { + case 0: + createScene(1, -1); + break; + case 1: + if (_flag) { + createScene(2, -1); + } else { + leaveModule(0); + } + 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, true), _soundResource(vm), + _countdown3(countdown3), _countdown2(countdown2), _countdown1(0), _flag(false) { + + SetUpdateHandler(&Scene1501::update); + SetMessageHandler(&Scene1501::handleMessage); + + _surfaceFlag = true; + + setBackground(backgroundFileHash); + + setPalette(); + addEntity(_palette); + _palette->addBasePalette(backgroundFileHash, 0, 256, 0); + _palette->startFadeToPalette(12); + + /* + if (soundFileHash != 0) { + _soundResource.set(soundFileHash); + _soundResource.load(); + _soundResource.play(); + } + */ + +} + +void Scene1501::update() { + + Scene::update(); + + // TODO: Since these countdowns are used a lot, maybe these can be wrapped in a class/struct + // so the code gets a little cleaner. + + if (_countdown1 != 0) { + _countdown1--; + if (_countdown1 == 0) { + _vm->_screen->clear(); + leaveScene(0); + } + } else if ((_countdown2 != 0 && (--_countdown2 == 0)) /*|| !_soundResource.isPlaying()*/) { + _countdown1 = 12; + _palette->startFadeToBlack(11); + } + + if (_countdown3 != 0) + _countdown3--; + + if (_countdown3 == 0 && _flag && _countdown1 == 0) { + _countdown1 = 12; + _palette->startFadeToBlack(11); + } + +} + +uint32 Scene1501::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Scene::handleMessage(messageNum, param, sender); + if (messageNum == 0x0009) { + _flag = true; + } + return messageResult; +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/module1500.h b/engines/neverhood/module1500.h new file mode 100644 index 0000000000..eeabec0618 --- /dev/null +++ b/engines/neverhood/module1500.h @@ -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 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +// TODO: I couldn't come up with a better name than 'Module' so far + +#ifndef NEVERHOOD_MODULE1500_H +#define NEVERHOOD_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, bool flag); +protected: + bool _flag; + 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: + SoundResource _soundResource; + int _countdown1; + int _countdown2; + int _countdown3; + bool _flag; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_MODULE1500_H */ diff --git a/engines/neverhood/module1600.cpp b/engines/neverhood/module1600.cpp new file mode 100644 index 0000000000..c510601642 --- /dev/null +++ b/engines/neverhood/module1600.cpp @@ -0,0 +1,1486 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "neverhood/module1600.h" +#include "neverhood/gamemodule.h" +#include "neverhood/module1200.h" +#include "neverhood/module2200.h" + +namespace Neverhood { + +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); + } + + // TODO Sound1ChList_addSoundResources(0x1A008D8, dword_4B3BB0, true); + // TODO Sound1ChList_setSoundValuesMulti(dword_4B3BB0, true, 50, 600, 5, 150); + // TODO Sound1ChList_sub_407C70(0x1A008D8, 0x41861371, 0x43A2507F, 0); + +} + +Module1600::~Module1600() { + // TODO Sound1ChList_sub_407A50(0x1A008D8); +} + +void Module1600::createScene(int sceneNum, int which) { + debug("Module1600::createScene(%d, %d)", sceneNum, which); + _vm->gameState().sceneNum = sceneNum; + switch (_vm->gameState().sceneNum) { + case 0: + createNavigationScene(0x004B39D0, which); + break; + case 1: + createNavigationScene(0x004B3A30, which); + break; + case 2: + createNavigationScene(0x004B3A60, which); + break; + case 3: + createNavigationScene(0x004B3A90, which); + break; + case 4: + createNavigationScene(0x004B3B20, which); + break; + case 5: + createNavigationScene(0x004B3B50, which); + break; + case 6: + createNavigationScene(0x004B3B80, which); + break; + case 7: + _childObject = new Scene1608(_vm, this, which); + break; + case 8: + _childObject = new Scene1609(_vm, this, which); + break; + case 1001: + if (getGlobalVar(0xA0808898) == 1) { + createSmackerScene(0x80050200, true, true, false); + } else if (getGlobalVar(0xA0808898) == 2) { + createSmackerScene(0x80090200, true, true, false); + } else { + createSmackerScene(0x80000200, true, true, false); + } + if (getGlobalVar(0xA0808898) >= 2) + setGlobalVar(0xA0808898, 0); + else + incGlobalVar(0xA0808898, +1); + break; + } + SetUpdateHandler(&Module1600::updateScene); + _childObject->handleUpdate(); +} + +void Module1600::updateScene() { + if (!updateChild()) { + switch (_vm->gameState().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; + } + } +} + +Class521::Class521(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y) + : AnimatedSprite(vm, 1000), _parentScene(parentScene) { + + SetUpdateHandler(&Class521::update); + SetMessageHandler(&Class521::handleMessage); + SetSpriteCallback(NULL); + + // TODO createSurface2(200, dword_4AF4C0); + createSurface(200, 640, 480); //TODO: Remove once the line above is done + _x = x; + _y = y; + + _field100 = 0; + _exitDirection = 0; + _currPointIndex = 0; + _againDestPtFlag = 0; + _stepError = 0; + _againDestPointFlag = 0; + _steps = 0; + _flag10E = 0; + _moreY = 0; + _flag10F = 0; + _flag113 = 0; + _flag114 = 1; + _flag11A = 0; + _newDeltaXType = -1; + _field11E = 0; + _pathPoints = NULL; + _rectList = NULL; + + setFileHash(0xD4220027, 0, -1); + setDoDeltaX(getGlobalVar(0x21E60190)); + +} + +Class521::~Class521() { + if (_finalizeStateCb == AnimationCallback(&Class521::sub45D620)) { + setGlobalVar(0x21E60190, !getGlobalVar(0x21E60190)); + } +} + +void Class521::setPathPoints(NPointArray *pathPoints) { + _pathPoints = pathPoints; +} + +void Class521::update() { + if (_newDeltaXType >= 0) { + setDoDeltaX(_newDeltaXType); + _newDeltaXType = -1; + } + AnimatedSprite::update(); + if (_againDestPtFlag && _moreY == 0 && !_flag10F) { + _againDestPtFlag = 0; + _againDestPointFlag = 0; + sendPointMessage(this, 0x2004, _againDestPt); + } else if (_againDestPointFlag && _moreY == 0 && !_flag10F) { + _againDestPointFlag = 0; + sendMessage(this, 0x2003, _againDestPointIndex); + } + sub45CE10(); + sub45E0A0(); +} + +void Class521::update45C790() { + Class521::update(); + if (++_idleCounter >= _idleCounterMax) + sub45D050(); + sub45E0A0(); +} + +uint32 Class521::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1019: + SetSpriteCallback(NULL); + break; + /* NOTE: Implemented in setPathPoints + case 0x2000: + case 0x2001: + */ + case 0x2002: + // Set the current position without moving + _currPointIndex = param.asInteger(); + _stepError = 0; + _x = pathPoint(_currPointIndex).x; + _y = pathPoint(_currPointIndex).y; + break; + case 0x2003: + // Move to a point by its index + { + int newPointIndex = param.asInteger(); + if (_moreY <= 0 && !_flag10F) { + _someX = pathPoint(newPointIndex).x; + _someY = pathPoint(newPointIndex).y; + if (_currPointIndex < newPointIndex) { + moveToNextPoint(); + } else if (_currPointIndex == newPointIndex && _stepError == 0) { + if (_currPointIndex == 0) { + _moreY = 0; + sendMessage(_parentScene, 0x2005, 0); + } else if (_currPointIndex == (int)_pathPoints->size()) { + _moreY = 0; + sendMessage(_parentScene, 0x2006, 0); + } + } else { + moveToPrevPoint(); + } + } else { + _againDestPointFlag = 1; + _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 (_moreY <= 0 && !_flag10F) { + // 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; + _someX = pt.x; + _someY = pt.y; + minMatchDistance = calcDistance(_someX, _someY, _x, _y) + 1; + for (int i = _currPointIndex + 1; i < (int)_pathPoints->size(); i++) { + distance = calcDistance(_someX, _someY, pathPoint(i).x, pathPoint(i).y); + if (distance >= minMatchDistance) + break; + minMatchDistance = distance; + minMatchIndex = i; + } + for (int i = _currPointIndex; i >= 0; i--) { + distance = calcDistance(_someX, _someY, pathPoint(i).x, pathPoint(i).y); + if (distance >= minMatchDistance) + break; + minMatchDistance = distance; + minMatchIndex = i; + } + if (minMatchIndex == -1) { + if (_currPointIndex == 0) { + moveToPrevPoint(); + } else { + SetSpriteCallback(NULL); + } + } else { + if (minMatchIndex > _currPointIndex) { + moveToNextPoint(); + } else { + moveToPrevPoint(); + } + } + } else { + _againDestPtFlag = 1; + _againDestPt = pt; + } + } + break; + case 0x2007: + _moreY = param.asInteger(); + _steps = 0; + _flag10E = 0; + SetSpriteCallback(&Class521::suMoveToPrevPoint); + _lastDistance = 640; + break; + case 0x2008: + _moreY = param.asInteger(); + _steps = 0; + _flag10E = 0; + SetSpriteCallback(&Class521::suMoveToNextPoint); + _lastDistance = 640; + break; + case 0x2009: + sub45CF80(); + break; + case 0x200A: + sub45CFB0(); + break; + /* NOTE: Implemented in setRectList + case 0x200B: + case 0x200C: + */ + case 0x200E: + sub45D180(); + break; + case 0x200F: + sub45CD00(); + _newDeltaXType = param.asInteger(); + break; + } + return messageResult; +} + +uint32 Class521::handleMessage45CC30(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Class521::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (_flag10F && param.asInteger() == 0x025424A2) { + removeCallbacks(); + } + break; + case 0x3002: + removeCallbacks(); + break; + } + return messageResult; +} + +uint32 Class521::handleMessage45CCA0(int messageNum, const MessageParam ¶m, Entity *sender) { + switch (messageNum) { + case 0x2009: + sub45CF80(); + break; + case 0x3002: + sendMessage(_parentScene, 0x200A, 0); + SetMessageHandler(&Class521::handleMessage); + break; + } + return 0; +} + +void Class521::sub45CD00() { + bool doDeltaX = _doDeltaX; + SetSpriteCallback(NULL); + _againDestPtFlag = 0; + _againDestPointFlag = 0; + _flag10E = 0; + _flag10F = 0; + _flag113 = 0; + _flag114 = 0; + _flag11A = 0; + _rectList = NULL; + SetMessageHandler(&Class521::handleMessage45CC30); + NextState(&Class521::sub45CFE0); + setFileHash(0x35698F78, 0, -1); + SetMessageHandler(&Class521::handleMessage45CC30); + SetUpdateHandler(&Class521::update45C790); + FinalizeState(&Class521::sub45D040); + setDoDeltaX(doDeltaX ? 1 : 0); + _currMoveDirection = 0; + _newMoveDirection = 0; + _steps = 0; + _idleCounter = 0; + _idleCounterMax = _vm->_rnd->getRandomNumber(64 - 1) + 24; +} + +void Class521::sub45CDC0() { + if (_value112 == 1) { + _lastDistance = 640; + _flag113 = 0; + _flag10E = 0; + SetSpriteCallback(&Class521::suMoveToNextPoint); + } else if (_value112 == 2) { + _lastDistance = 640; + _flag113 = 0; + _flag10E = 0; + SetSpriteCallback(&Class521::suMoveToPrevPoint); + } +} + +void Class521::sub45CE10() { + if (_flag10E && !_flag113 && !_flag10F) { + removeCallbacks(); + _flag114 = 0; + _flag113 = 1; + setFileHash(0x192ADD30, 0, -1); + SetMessageHandler(&Class521::handleMessage45CC30); + SetUpdateHandler(&Class521::update); + NextState(&Class521::sub45CFE0); + } else if (!_flag10E && _steps && _flag113) { + removeCallbacks(); + _flag113 = 0; + setFileHash(0x9966B138, 0, -1); + SetMessageHandler(&Class521::handleMessage45CC30); + SetUpdateHandler(&Class521::update); + NextState(&Class521::sub45D100); + } else { + bool flag = false; + uint index = 0; + if (_rectList && _rectList->size() > 0) { + while (index < _rectList->size()) { + NRect r = (*_rectList)[index]; + if (_x >= r.x1 && _x <= r.x2 && _y >= r.y1 && _y <= r.y2) + break; + } + if (index < _rectList->size() && !_flag11A) + flag = true; + _flag11A = index < _rectList->size(); + } + if (flag) { + removeCallbacks(); + sub45D0A0(); + } else if (_newMoveDirection != _currMoveDirection && _flag114 && !_flag10F) { + removeCallbacks(); + _currMoveDirection = _newMoveDirection; + sub45D100(); + } + } +} + +void Class521::sub45CF80() { + setFileHash(0xA86A9538, 0, -1); + SetMessageHandler(&Class521::handleMessage45CC30); + SetUpdateHandler(&Class521::update); + NextState(&Class521::sub45CFE0); +} + +void Class521::sub45CFB0() { + setFileHash(0xA86A9538, -1, -1); + _playBackwards = true; + SetMessageHandler(&Class521::handleMessage45CCA0); + SetUpdateHandler(&Class521::update); +} + +void Class521::sub45CFE0() { + setFileHash(0x35698F78, 0, -1); + SetMessageHandler(&Class521::handleMessage); + SetUpdateHandler(&Class521::update45C790); + FinalizeState(&Class521::sub45D040); + _idleCounter = 0; + _currMoveDirection = 0; + _newMoveDirection = 0; + _steps = 0; + _idleCounterMax = _vm->_rnd->getRandomNumber(64 - 1) + 24; +} + +void Class521::sub45D040() { + SetUpdateHandler(&Class521::update); +} + +void Class521::sub45D050() { + setFileHash(0xB579A77C, 0, -1); + SetMessageHandler(&Class521::handleMessage45CC30); + SetUpdateHandler(&Class521::update); + NextState(&Class521::sub45CFE0); + _idleCounter = 0; + _idleCounterMax = _vm->_rnd->getRandomNumber(64 - 1) + 24; +} + +void Class521::sub45D0A0() { + _flag10F = 1; + removeCallbacks(); + setFileHash(0x9C220DA4, 0, -1); + SetMessageHandler(&Class521::handleMessage45CC30); + SetUpdateHandler(&Class521::update); + FinalizeState(&Class521::sub45D0E0); +} + +void Class521::sub45D0E0() { + _flag10F = 0; + _newMoveDirection = 0; + sub45D100(); +} + +void Class521::sub45D100() { + _flag114 = 1; + if (_currMoveDirection == 1) { + setFileHash(0xD4AA03A4, 0, -1); + } else if (_currMoveDirection == 3) { + setFileHash(0xD00A1364, 0, -1); + } else if ((_currMoveDirection == 2 && _doDeltaX) || (_currMoveDirection == 4 && !_doDeltaX)) { + sub45D180(); + } else { + setFileHash(0xD4220027, 0, -1); + } + setGlobalVar(0x21E60190, _doDeltaX ? 1 : 0); +} + +void Class521::sub45D180() { + _flag10F = 1; + removeCallbacks(); + setFileHash(0xF46A0324, 0, -1); + _value112 = 0; + SetMessageHandler(&Class521::handleMessage45CC30); + SetUpdateHandler(&Class521::update); + FinalizeState(&Class521::sub45D620); + sub45CDC0(); +} + +void Class521::moveToNextPoint() { + if (_currPointIndex >= (int)_pathPoints->size() - 1) { + _moreY = 0; + sendMessage(this, 0x1019, 0); + sendMessage(_parentScene, 0x2006, 0); + } else { + NPoint nextPt = pathPoint(_currPointIndex + 1); + NPoint currPt = pathPoint(_currPointIndex); + if (ABS(nextPt.y - currPt.y) <= ABS(nextPt.x - currPt.x) && nextPt.x >= currPt.x && + (_currMoveDirection == 4 || _currMoveDirection == 2)) { + if (_currMoveDirection == 4) + _currMoveDirection = 2; + else if (_currMoveDirection == 2) + _currMoveDirection = 4; + if (_flag113) + sub45D390(); + else + sub45D350(); + } else { + if (_steps == 0) { + removeCallbacks(); + _flag113 = 0; + setFileHash(0x9966B138, 0, -1); + SetMessageHandler(&Class521::handleMessage45CC30); + SetUpdateHandler(&Class521::update); + NextState(&Class521::sub45D100); + } + _flag10E = 0; + SetSpriteCallback(&Class521::suMoveToNextPoint); + _lastDistance = 640; + } + } +} + +void Class521::sub45D350() { + removeCallbacks(); + _flag10F = 1; + _flag10E = 1; + setFileHash(0x192ADD30, 0, -1); + SetMessageHandler(&Class521::handleMessage45CC30); + SetUpdateHandler(&Class521::update); + NextState(&Class521::sub45D390); +} + +void Class521::sub45D390() { + removeCallbacks(); + _flag10F = 1; + setFileHash(0xF46A0324, 0, -1); + SetMessageHandler(&Class521::handleMessage45CC30); + SetUpdateHandler(&Class521::update); + FinalizeState(&Class521::sub45D620); + _value112 = 1; + sub45CDC0(); +} + +void Class521::moveToPrevPoint() { + if (_currPointIndex == 0 && _stepError == 0) { + _moreY = 0; + sendMessage(this, 0x1019, 0); + sendMessage(_parentScene, 0x2005, 0); + } else { + NPoint prevPt; + NPoint currPt; + if (_stepError == 0) { + prevPt = pathPoint(_currPointIndex - 1); + currPt = pathPoint(_currPointIndex); + } else { + prevPt = pathPoint(_currPointIndex); + currPt = pathPoint(_currPointIndex + 1); + } + if (ABS(prevPt.y - currPt.y) <= ABS(prevPt.x - currPt.x) && currPt.x >= prevPt.x && + (_currMoveDirection == 2 || _currMoveDirection == 4)) { + if (_currMoveDirection == 2) + _currMoveDirection = 4; + else if (_currMoveDirection == 4) + _currMoveDirection = 2; + if (_flag113) + sub45D5D0(); + else + sub45D580(); + } else { + if (_steps == 0) { + removeCallbacks(); + _flag113 = 0; + setFileHash(0x9966B138, 0, -1); + SetMessageHandler(&Class521::handleMessage45CC30); + SetUpdateHandler(&Class521::update); + NextState(&Class521::sub45D100); + } + _flag10E = 0; + SetSpriteCallback(&Class521::suMoveToPrevPoint); + _lastDistance = 640; + } + } +} + +void Class521::sub45D580() { + _flag10F = 1; + _flag10E = 1; + FinalizeState(NULL); + setFileHash(0x192ADD30, 0, -1); + SetMessageHandler(&Class521::handleMessage45CC30); + SetUpdateHandler(&Class521::update); + NextState(&Class521::sub45D5D0); +} + +void Class521::sub45D5D0() { + _flag10F = 1; + FinalizeState(NULL); + setFileHash(0xF46A0324, 0, -1); + SetMessageHandler(&Class521::handleMessage45CC30); + SetUpdateHandler(&Class521::update); + FinalizeState(&Class521::sub45D620); + _value112 = 2; + sub45CDC0(); +} + +void Class521::sub45D620() { + _flag10F = 0; + _newMoveDirection = 0; + setDoDeltaX(2); + sub45D100(); +} + +void Class521::suMoveToNextPoint() { + int16 newX = _x, newY = _y; + + if (_currPointIndex >= (int)_pathPoints->size()) { + _moreY = 0; + sendMessage(this, 0x1019, 0); + sendMessage(_parentScene, 0x2006, 0); + return; + } + + if (_flag10E) { + if (_steps <= 0) { + sendMessage(this, 0x1019, 0); + return; + } else { + _steps--; + } + } else if (_steps < 11) { + _steps++; + } + + bool firstTime = true; + _anotherY = _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; + } + _anotherY = 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 (_moreY != 0) { + _x = newX; + _y = newY; + _moreY -= _anotherY; + if (_moreY <= 0) { + _flag10E = 1; + _moreY = 0; + } + } else { + int distance = calcDistance(_someX, _someY, _x, _y); + _x = newX; + _y = newY; + if (newX > 20 && newX < 620 && newY > 20 && newY < 460) { + _exitDirection = 0; + _field100 = 1; + } else if (_field100) { + _someX = pathPoint(_pathPoints->size() - 1).x; + _someY = pathPoint(_pathPoints->size() - 1).y; + _field100 = 0; + 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 && _flag10E) { + _flag10E = 0; + _steps = 11; + } + } + if ((distance < 20 && _exitDirection == 0 && _lastDistance < distance) || + (_exitDirection == 0 && _lastDistance + 20 < distance)) + _flag10E = 1; + if (distance < _lastDistance) + _lastDistance = distance; + if (_currPointIndex == (int)_pathPoints->size() - 1) { + _flag10E = 1; + _moreY = 0; + sendMessage(this, 0x1019, 0); + sendMessage(_parentScene, 0x2006, 0); + } + } + +} + +void Class521::suMoveToPrevPoint() { + int16 newX = _x, newY = _y; + + if (_currPointIndex == 0 && _stepError == 0) { + _moreY = 0; + sendMessage(this, 0x1019, 0); + sendMessage(_parentScene, 0x2005, 0); + return; + } + + if (_flag10E) { + if (_steps <= 0) { + sendMessage(this, 0x1019, 0); + return; + } else { + _steps--; + } + } else if (_steps < 11) { + _steps++; + } + + bool firstTime = true; + _anotherY = _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; + } + _anotherY = 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 (_moreY != 0) { + _x = newX; + _y = newY; + _moreY -= _anotherY; + if (_moreY <= 0) { + _flag10E = 1; + _moreY = 0; + } + } else { + int distance = calcDistance(_someX, _someY, _x, _y); + _x = newX; + _y = newY; + if (newX > 20 && newX < 620 && newY > 20 && newY < 460) { + _exitDirection = 0; + _field100 = 1; + } else if (_field100) { + _someX = pathPoint(0).x; + _someY = pathPoint(0).y; + _field100 = 0; + 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 && _flag10E) { + _flag10E = 0; + _steps = 11; + } + } + if ((distance < 20 && _exitDirection == 0 && _lastDistance < distance) || + (_exitDirection == 0 && _lastDistance + 20 < distance)) + _flag10E = 1; + if (distance < _lastDistance) + _lastDistance = distance; + if (_currPointIndex == 0 && _stepError == 0) { + _flag10E = 1; + _moreY = 0; + sendMessage(this, 0x1019, 0); + sendMessage(_parentScene, 0x2005, 0); + } + } + +} + +void Class521::sub45E0A0() { + // TODO +} + +Class546::Class546(NeverhoodEngine *vm, Scene *parentScene) + : AnimatedSprite(vm, 0x08C80144, 900, 320, 240), _soundResource(vm), + _parentScene(parentScene) { + + setVisible(false); + SetMessageHandler(&Class546::handleMessage); + stopAnimation(); +} + +uint32 Class546::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + removeCallbacks(); + break; + case 0x4808: + sub44D710(); + break; + case 0x4809: + sub44D790(); + break; + } + return messageResult; +} + +void Class546::sub44D710() { + setFileHash(0x08C80144, 0, -1); + setVisible(true); + NextState(&Class546::sub44D760); + _soundResource.play(calcHash("fxDoorOpen23")); +} + +void Class546::sub44D760() { + sendMessage(_parentScene, 0x2033, 0); + stopAnimation(); + setVisible(false); +} + +void Class546::sub44D790() { + setFileHash(0x08C80144, -1, -1); + setVisible(true); + NextState(&Class546::sub44D7F0); + _soundResource.play(calcHash("fxDoorClose23")); +} + +void Class546::sub44D7F0() { + sendMessage(_parentScene, 0x2034, 0); + stopAnimation(); +} + +Class547::Class547(NeverhoodEngine *vm, int16 x, int16 y) + : AnimatedSprite(vm, 0x1209E09F, 1100, x, y) { + + setDoDeltaX(1); + setFileHash(0x1209E09F, 1, -1); + _newHashListIndex = 1; +} + +Class548::Class548(NeverhoodEngine *vm, int16 x, int16 y) + : AnimatedSprite(vm, 0x1209E09F, 100, x, y) { + + setDoDeltaX(1); + _newHashListIndex = 0; +} + +Class518::Class518(NeverhoodEngine *vm, Class521 *class521) + : AnimatedSprite(vm, 1100), _class521(class521) { + + SetUpdateHandler(&Class518::update); + createSurface1(0x60281C10, 150); + setFileHash(0x60281C10, -1, -1); + _newHashListIndex = -2; +} + +void Class518::update() { + _x = _class521->getX(); + _y = _class521->getY(); + AnimatedSprite::update(); +} + +Scene1608::Scene1608(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule, true), _class521(NULL), _countdown1(0) { + + setGlobalVar(0x21E60190, 1); + + _surfaceFlag = true; + SetMessageHandler(&Scene1608::handleMessage44D2A0); + + _class545 = insertSprite<Class545>(this, 1, 1100, 198, 220); + _vm->_collisionMan->addSprite(_class545); + + if (which < 0) { + if (_vm->gameState().which == 1) + which = 1; + else { + setRectList(0x004B47D0); + insertKlayman<KmScene1608>(380, 438); + _klayman2 = _klayman; + _flag4 = false; + _class546 = insertSprite<Class546>(this); + _sprite1 = insertStaticSprite(0x7D0404E8, 1100); + setMessageList(0x004B46A8); + setBackground(0x10080E01); + setPalette(0x10080E01); + _asTape = insertSprite<AsScene1201Tape>(this, 13, 1100, 412, 443, 0x9148A011); + _vm->_collisionMan->addSprite(_asTape); + _klayman->setClipRect(_sprite1->getDrawRect().x, 0, 640, 480); + SetUpdateHandler(&Scene1608::update44CE90); + insertMouse433(0x80E05108); + insertStaticSprite(0x4B18F868, 1200); + } + } else if (which == 0) { + _vm->gameState().which = 0; + setRectList(0x004B47D0); + insertKlayman<KmScene1608>(0, 438); + _klayman2 = _klayman; + _flag4 = false; + setMessageList(0x004B46B0); + setBackground(0x10080E01); + setPalette(0x10080E01); + _asTape = insertSprite<AsScene1201Tape>(this, 13, 1100, 412, 443, 0x9148A011); + _vm->_collisionMan->addSprite(_asTape); + insertMouse433(0x80E05108); + _sprite1 = insertStaticSprite(0x7D0404E8, 1100); + _class546 = insertSprite<Class546>(this); + _klayman->setClipRect(_sprite1->getDrawRect().x, 0, 640, 480); + SetUpdateHandler(&Scene1608::update44CE90); + sendMessage(_class546, 0x4808, 0); + insertStaticSprite(0x4B18F868, 1200); + } else if (which == 2) { + _vm->gameState().which = 1; + _dataResource.load(0x003C0492); + _roomPathPoints = _dataResource.getPointArray(calcHash("meArchroArchRoomPath")); + setBackground(0x98001604); + setPalette(0x98001604); + _palette->addPalette("paPodRed", 65, 31, 65); + insertMouse433(0x01600988); + _sprite2 = insertStaticSprite(0x491F38A8, 1100); + _class521 = createSprite<Class521>(this, 375, 227); // Create but don't add to the sprite list yet + _class547 = insertSprite<Class547>(375, 227); + _class548 = insertSprite<Class548>(375, 227); + _class521->setVisible(false); + if (getGlobalVar(0xC0418A02)) { + insertKlayman<KmScene1608>(373, 220); + _klayman->setDoDeltaX(1); + } else { + insertKlayman<KmScene1608>(283, 220); + } + _klayman2 = _klayman; + setMessageList(0x004B47A8); + SetMessageHandler(&Scene1608::handleMessage44D3C0); + SetUpdateHandler(&Scene1608::update44CED0); + // NOTE: Setting the point array was handled by messages 0x2000 (array) and 0x2001 (count) in the original + _class521->setPathPoints(_roomPathPoints); + sendMessage(_class521, 0x2002, _roomPathPoints->size() - 1); + _sprite3 = insertStaticSprite(0xB47026B0, 1100); + _rect1.set(_sprite3->getDrawRect().x, _sprite3->getDrawRect().y, 640, _sprite2->getDrawRect().y2()); + _rect3.set(_sprite2->getDrawRect().x, _sprite3->getDrawRect().y, 640, _sprite2->getDrawRect().y2()); + _rect2 = _rect1; + _rect2.y2 = 215; + _klayman->setClipRect(_rect1); + _class521->setClipRect(_rect1); + _class547->setClipRect(_rect1); + _class548->setClipRect(_rect1); + _asTape = insertSprite<AsScene1201Tape>(this, 13, 1100, 412, 443, 0x9148A011); + _vm->_collisionMan->addSprite(_asTape); + insertSprite<Class518>(_class521)->setClipRect(_rect1); + _flag4 = false; + _flag2 = false; + _flag1 = 0; + setRectList(0x004B4810); + } + + // NOTE: Not in the else because 'which' is set to 1 in the true branch + if (which == 1) { + _vm->gameState().which = 1; + _dataResource.load(0x003C0492); + _roomPathPoints = _dataResource.getPointArray(calcHash("meArchroArchRoomPath")); + setBackground(0x98001604); + setPalette(0x98001604); + _palette->addPalette("paPodRed", 65, 31, 65); + insertMouse433(0x01600988); + _class521 = insertSprite<Class521>(this, 375, 227); + _class547 = insertSprite<Class547>(375, 227); + _class548 = insertSprite<Class548>(375, 227); + _sprite2 = insertStaticSprite(0x491F38A8, 1100); + _klayman2 = createSprite<KmScene1608>(this, 439, 220); // Special Klayman handling... + sendMessage(_klayman2, 0x2032, 1); + _klayman2->setDoDeltaX(1); + SetMessageHandler(&Scene1608::handleMessage44D470); + SetUpdateHandler(&Scene1608::update44D1E0); + _class547->setVisible(false); + _class548->setVisible(false); + // NOTE: Setting the point array was handled by messages 0x2000 (array) and 0x2001 (count) in the original + _class521->setPathPoints(_roomPathPoints); + sendMessage(_class521, 0x2002, 0); + sendMessage(_class521, 0x2008, 90); + _sprite3 = insertStaticSprite(0xB47026B0, 1100); + _rect1.set(_sprite3->getDrawRect().x, _sprite3->getDrawRect().y, 640, _sprite2->getDrawRect().y2()); + _rect3.set(_sprite2->getDrawRect().x, _sprite3->getDrawRect().y, 640, _sprite2->getDrawRect().y2()); + _rect2 = _rect1; + _rect2.y2 = 215; + _klayman2->setClipRect(_rect1); + _class521->setClipRect(_rect1); + _class547->setClipRect(_rect1); + _class548->setClipRect(_rect1); + _asTape = insertSprite<AsScene1201Tape>(this, 13, 1100, 412, 443, 0x9148A011); + // ... _vm->_collisionMan->addSprite(_asTape); + insertSprite<Class518>(_class521)->setClipRect(_rect1); + _flag4 = true; + _flag2 = true; + _flag1 = 0; + } + + _palette->addPalette("paKlayRed", 0, 64, 0); + +} + +Scene1608::~Scene1608() { + setGlobalVar(0xC0418A02, _klayman2->isDoDeltaX() ? 1 : 0); + // Weird + if (_flag4) { + delete _klayman2; + } else { + delete _class521; + } +} + +void Scene1608::update44CE90() { + Scene::update(); + if (_countdown1 != 0 && (--_countdown1 == 0)) { + leaveScene(0); + } +} + +void Scene1608::update44CED0() { + Scene::update(); + if (_flag1 == 1) { + removeSurface(_klayman->getSurface()); + removeEntity(_klayman); + addSprite(_class521); + _flag4 = true; + clearRectList(); + SetUpdateHandler(&Scene1608::update44CFE0); + SetMessageHandler(&Scene1608::handleMessage44D510); + _class547->setVisible(false); + _class548->setVisible(false); + _class521->setVisible(true); + sendMessage(_class521, 0x2009, 0); + _class521->handleUpdate(); + _klayman = NULL; + _flag1 = 0; + } + if (_klayman2->getX() <= 375) { + _klayman2->setClipRect(_rect1); + } else { + _klayman2->setClipRect(_rect2); + } +} + +void Scene1608::update44CFE0() { + Scene::update(); + if (_mouseClicked) { + if (_mouseClickPos.x <= 329 && _class521->getX() == 375 && _class521->getY() == 227) { + sendMessage(_class521, 0x200A, 0); + SetUpdateHandler(&Scene1608::update44D0C0); + } else { + sendPointMessage(_class521, 0x2004, _mouseClickPos); + SetMessageHandler(&Scene1608::handleMessage44D470); + SetUpdateHandler(&Scene1608::update44D1E0); + } + _mouseClicked = false; + } + if (_klayman2->getX() <= 375) { + _klayman2->setClipRect(_rect1); + } else { + _klayman2->setClipRect(_rect2); + } +} + +void Scene1608::update44D0C0() { + Scene::update(); + if (_flag1 == 2) { + _klayman = _klayman2; + removeSurface(_class521->getSurface()); + removeEntity(_class521); + addSprite(_klayman); + _flag4 = false; + SetMessageHandler(&Scene1608::handleMessage44D3C0); + SetUpdateHandler(&Scene1608::update44CED0); + setRectList(0x004B4810); + _class547->setVisible(true); + _class548->setVisible(true); + _class521->setVisible(false); + setMessageList(0x004B4748); + runMessageList(); + _klayman->handleUpdate(); + _flag1 = 0; + } + if (_klayman2->getX() <= 375) { + _klayman2->setClipRect(_rect1); + } else { + _klayman2->setClipRect(_rect2); + } +} + +void Scene1608::update44D1E0() { + Scene::update(); + if (_mouseClicked) { + sendPointMessage(_class521, 0x2004, _mouseClickPos); + _mouseClicked = false; + } + if (_class521->getX() < 300) { + if (_flag2) { + _flag2 = false; + _class521->setClipRect(_rect1); + if (!_class521->isDoDeltaX()) + sendMessage(_class521, 0x200E, 0); + } + } else if (!_flag2) { + _flag2 = true; + _class521->setClipRect(_rect3); + } +} + +uint32 Scene1608::handleMessage44D2A0(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x20250B1A) { + clearRectList(); + _klayman->setVisible(false); + showMouse(false); + _sprite1->setVisible(false); + sendMessage(_class546, 0x4809, 0); + _countdown1 = 28; + } + break; + case 0x200D: + sendMessage(_parentModule, 0x200D, 0); + break; + case 0x4826: + if (sender == _asTape) { + sendEntityMessage(_klayman2, 0x1014, _asTape); + setMessageList(0x004B4770); + } else if (sender == _class545) { + setMessageList(0x004B46C8); + } + break; + } + return 0; +} + +uint32 Scene1608::handleMessage44D3C0(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x60842040) { + _flag1 = true; + } + break; + case 0x200D: + sendMessage(_parentModule, 0x200D, 0); + break; + case 0x4826: + if (sender == _class545) { + sendEntityMessage(_klayman2, 0x1014, _class545); + setMessageList(0x004B4760); + } + break; + } + return 0; +} + +uint32 Scene1608::handleMessage44D470(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2005: + leaveScene(1); + break; + case 0x2006: + SetMessageHandler(&Scene1608::handleMessage44D510); + SetUpdateHandler(&Scene1608::update44CFE0); + sendMessage(_class521, 0x200F, 1); + break; + case 0x200D: + sendMessage(_parentModule, 0x200D, 0); + break; + } + return 0; +} + +uint32 Scene1608::handleMessage44D510(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x200A: + _flag1 = 2; + break; + case 0x200D: + sendMessage(_parentModule, 0x200D, 0); + break; + } + return 0; +} + +Scene1609::Scene1609(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule, true), _soundResource(vm), _countdown1(1), + _index1(0), _index3(0), _flag5(true), _flag6(false) { + + // TODO _vm->gameModule()->initScene3011Vars(); + _index2 = getGlobalVar(0x2414C2F2); + + _surfaceFlag = true; + SetMessageHandler(&Scene1609::handleMessage); + SetUpdateHandler(&Scene1609::update); + + setBackground(0x92124A14); + setPalette(0x92124A14); + + for (int i = 0; i < 12; i++) + _asSymbols[i] = insertSprite<AsScene3011Symbol>(i, false); + + _ssButton = insertSprite<SsScene3011Button>(this, true); + _vm->_collisionMan->addSprite(_ssButton); + + insertMouse435(0x24A10929, 20, 620); + + _soundResource.load(0x68E25540); + +} + +void Scene1609::update() { + if (!_flag6 && _countdown1 != 0 && (--_countdown1 == 0)) { + if (_flag5) { + _index1++; + if (_index1 >= 12) + _index1 = 0; + _asSymbols[_index3]->change(_index1 + 12, _index1 == (int)getSubVar(0x04909A50, _index2)); + _flag5 = false; + _countdown1 = 36; + } else { + _asSymbols[_index3]->hide(); + _flag5 = true; + _countdown1 = 12; + } + } + if (_flag6 && !_soundResource.isPlaying()) { + leaveScene(1); + } + Scene::update(); +} + +uint32 Scene1609::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x0001: + // TODO: Debug stuff + if (param.asPoint().x <= 20 || param.asPoint().x >= 620) + leaveScene(0); + break; + // TODO: Debug stuff + case 0x2000: + if (!_flag6) { + if (_flag5) + _asSymbols[_index3]->change(_index1 + 12, false); + _asSymbols[_index3]->stopSound(); + _index3++; + if (_index3 >= 12) { + if (testVars()) { + _soundResource.play(); + setGlobalVar(0x2C531AF8, 1); + _flag6 = true; + } else { + _index3 = 0; + for (int i = 0; i < 12; i++) + _asSymbols[i]->hide(); + } + } + _flag5 = true; + _countdown1 = 1; + } + break; + } + return 0; +} + +bool Scene1609::testVars() { + int index1 = 0; + do { + int cmpIndex = _asSymbols[0]->getIndex(); + if (!_asSymbols[0]->getFlag1()) + cmpIndex -= 12; + if ((int)getSubVar(0x04909A50, index1) == cmpIndex) + break; + index1++; + } while(1); + for (int index2 = 0; index2 < 12; index2++) { + int cmpIndex = _asSymbols[index2]->getIndex(); + if (!_asSymbols[index2]->getFlag1()) + cmpIndex -= 12; + if ((int)getSubVar(0x04909A50, index1) != cmpIndex) + return false; + _index1++; + if (_index1 >= 12) + _index1 = 0; + _index2++; + } + return true; +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/module1600.h b/engines/neverhood/module1600.h new file mode 100644 index 0000000000..efb2b943ab --- /dev/null +++ b/engines/neverhood/module1600.h @@ -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 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef NEVERHOOD_MODULE1600_H +#define NEVERHOOD_MODULE1600_H + +#include "neverhood/neverhood.h" +#include "neverhood/module.h" +#include "neverhood/scene.h" +#include "neverhood/module3000.h" + +namespace Neverhood { + +// Module1600 + +class Module1600 : public Module { +public: + Module1600(NeverhoodEngine *vm, Module *parentModule, int which); + virtual ~Module1600(); +protected: + void createScene(int sceneNum, int which); + void updateScene(); +}; + +class Class521 : public AnimatedSprite { +public: + Class521(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y); + ~Class521(); + void setPathPoints(NPointArray *pathPoints); +protected: + Scene *_parentScene; + NPointArray *_pathPoints; + NRectArray *_rectList; + int _newMoveDirection; + int _currMoveDirection; + int _exitDirection; + int _currPointIndex; + NPoint _againDestPt; + int _againDestPtFlag; + int _steps; + int _stepError; + int _idleCounter; + int _idleCounterMax; + int _lastDistance; + int _field100; + int _againDestPointFlag; + int _flag10E; + int _moreY; + int _flag10F; + int _flag113; + int _flag114; + int _flag11A; + int _newDeltaXType; + int _field11E; + int _againDestPointIndex; + int _value112; + int _anotherY; + int16 _someX, _someY; + NPoint pathPoint(uint index) { return (*_pathPoints)[index]; } + void update(); + void update45C790(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 handleMessage45CC30(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 handleMessage45CCA0(int messageNum, const MessageParam ¶m, Entity *sender); + void sub45CD00(); + void sub45CDC0(); + void sub45CE10(); + void sub45CF80(); + void sub45CFB0(); + void sub45CFE0(); + void sub45D040(); + void sub45D050(); + void sub45D0A0(); + void sub45D0E0(); + void sub45D100(); + void sub45D180(); + void moveToNextPoint(); + void sub45D350(); + void sub45D390(); + void moveToPrevPoint(); + void sub45D580(); + void sub45D5D0(); + void sub45D620(); + void suMoveToNextPoint(); + void suMoveToPrevPoint(); + void sub45E0A0(); +}; + +class Class546 : public AnimatedSprite { +public: + Class546(NeverhoodEngine *vm, Scene *parentScene); +protected: + Scene *_parentScene; + SoundResource _soundResource; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void sub44D710(); + void sub44D760(); + void sub44D790(); + void sub44D7F0(); +}; + +class Class547 : public AnimatedSprite { +public: + Class547(NeverhoodEngine *vm, int16 x, int16 y); +}; + +class Class548 : public AnimatedSprite { +public: + Class548(NeverhoodEngine *vm, int16 x, int16 y); +}; + +class Class518 : public AnimatedSprite { +public: + Class518(NeverhoodEngine *vm, Class521 *class521); +protected: + Class521 *_class521; + void update(); +}; + +class Scene1608 : public Scene { +public: + Scene1608(NeverhoodEngine *vm, Module *parentModule, int which); + ~Scene1608(); +protected: + Class521 *_class521; + Sprite *_class545; + Sprite *_class546; + Sprite *_class547; + Sprite *_class548; + Sprite *_sprite1; + Sprite *_sprite2; + Sprite *_sprite3; + Sprite *_asTape; + Klayman *_klayman2; + NRect _rect1; + NRect _rect2; + NRect _rect3; + int _flag1; + bool _flag2; + bool _flag3; + bool _flag4; + int _countdown1; + NPointArray *_roomPathPoints; + void update44CE90(); + void update44CED0(); + void update44CFE0(); + void update44D0C0(); + void update44D1E0(); + uint32 handleMessage44D2A0(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 handleMessage44D3C0(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 handleMessage44D470(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 handleMessage44D510(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene1609 : public Scene { +public: + Scene1609(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + SoundResource _soundResource; + Sprite *_ssButton; + AsScene3011Symbol *_asSymbols[12]; + int _index1; + int _index2; + int _index3; + int _countdown1; + bool _flag5; + bool _flag6; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + bool testVars(); +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_MODULE1600_H */ diff --git a/engines/neverhood/module1700.cpp b/engines/neverhood/module1700.cpp new file mode 100644 index 0000000000..ae7a7fdc7a --- /dev/null +++ b/engines/neverhood/module1700.cpp @@ -0,0 +1,278 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "neverhood/module1700.h" + +namespace Neverhood { + +Module1700::Module1700(NeverhoodEngine *vm, Module *parentModule, int which) + : Module(vm, parentModule), _soundResource(vm) { + + // TODO Music18hList_add(0x04212331); + // TODO Sound1ChList_addSoundResources(0x04212331, dword_4AE930, true); + // TODO Sound1ChList_setSoundValuesMulti(dword_4AE930, 1, 50, 600, 5, 150); + // TODO Sound1ChList_sub_407C70(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() { + // TODO Sound1ChList_sub_407A50(0x04212331); +} + +void Module1700::createScene(int sceneNum, int which) { + debug("Module1700::createScene(%d, %d)", sceneNum, which); + _vm->gameState().sceneNum = sceneNum; + switch (_vm->gameState().sceneNum) { + case 0: + // TODO Sound1ChList_setSoundValuesMulti(dword_4AE930, 0, 0, 0, 0, 0); + createSmackerScene(0x3028A005, true, true, false); + break; + case 1: + createNavigationScene(0x004AE8B8, which); + break; + case 2: + createNavigationScene(0x004AE8E8, which); + break; + case 3: + // TODO Sound1ChList_setSoundValuesMulti(dword_4AE930, 0, 0, 0, 0, 0); + createSmackerScene(0x01190041, true, true, false); + break; + case 4: + // TODO Sound1ChList_setSoundValuesMulti(dword_4AE930, 0, 0, 0, 0, 0); + // TODO Music18hList_play(0x31114225, 0, 2, 1); + _childObject = new Scene1705(_vm, this, which); + break; + } + SetUpdateHandler(&Module1700::updateScene); + _childObject->handleUpdate(); +} + +void Module1700::updateScene() { + if (!updateChild()) { + switch (_vm->gameState().sceneNum) { + case 0: + // TODO Sound1ChList_setSoundValuesMulti(dword_4AE930, 1, 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 (!_soundResource.isPlaying()) { + // TODO _soundResource.setVolume(60); + _soundResource.play(0x58B45E58); + } + createScene(2, 2); + } + break; + case 3: + createScene(4, 0); + break; + case 4: + leaveModule(1); + break; + } + } +} + +// Scene1705 + +static const uint32 kScene1705FileHashes[] = { + 0x910EA801, + 0x920EA801, + 0x940EA801, + 0x980EA801, + 0x800EA801, + 0xB00EA801, + 0xD00EA801, + 0x100EA801, + 0x900EA800, + 0xD10EA801, + 0x110EA801, + 0x910EA800 +}; + +Class602::Class602(NeverhoodEngine *vm, uint32 fileHash, int index) + : StaticSprite(vm, fileHash, 100) { + + _x = _spriteResource.getPosition().x + index * 30; + _y = _spriteResource.getPosition().y + 160; + StaticSprite::update(); +} + +Class606::Class606(NeverhoodEngine *vm, Scene *parentScene, int index, int surfacePriority, int16 x, int16 y, uint32 fileHash) + : StaticSprite(vm, fileHash, surfacePriority, x - 24, y - 4), _parentScene(parentScene), _index(index) { + + if (!getSubVar(0x02038314, _index) && !getSubVar(0x02720344, _index)) { + SetMessageHandler(&Class606::handleMessage); + } else { + setVisible(false); + SetMessageHandler(NULL); + } + _deltaRect = _drawRect; + _deltaRect.x -= 4; + _deltaRect.y -= 8; + _deltaRect.width += 8; + _deltaRect.height += 16; + Sprite::processDelta(); +} + +uint32 Class606::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + sendMessage(_parentScene, 0x4826, 0); + messageResult = 1; + break; + case 0x4806: + setSubVar(0x02038314, _index, 1); + setVisible(false); + SetMessageHandler(NULL); + break; + } + return messageResult; +} + +Scene1705::Scene1705(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule, true), _paletteArea(1) { + + Sprite *tempSprite; + + setGlobalVar(0xE7498218, 1); + // TODO _vm->initVarsScene1705(); + + SetMessageHandler(&Scene1705::handleMessage); + SetUpdateHandler(&Scene1705::update); + + setHitRects(0x004B69D8); + + _surfaceFlag = true; + + setBackground(0x03118226); + + setPalette(0x03118226); + _palette->addBasePalette(0x91D3A391, 0, 64, 0); + _palette->copyBasePalette(0, 256, 0); + addEntity(_palette); + + insertMouse433(0x18222039); + + insertSprite<Class602>(kScene1705FileHashes[getSubVar(0x0A4C0A9A, 0)], 0); + insertSprite<Class602>(kScene1705FileHashes[getSubVar(0x0A4C0A9A, 1)], 1); + insertSprite<Class602>(kScene1705FileHashes[getSubVar(0x0A4C0A9A, 2)], 2); + + _sprite = insertStaticSprite(0x31313A22, 1100); + + _class606 = insertSprite<Class606>(this, 15, 1100, 238, 439, 0x02363852); + _vm->_collisionMan->addSprite(_class606); + + which = 4; + + if (which < 0) { + insertKlayman<KmScene1705>(231, 434); + setMessageList(0x004B69E8); + sendMessage(this, 0x2000, 0); + _klayman->setClipRect(0, 0, _sprite->getDrawRect().x2(), 480); + } else if (which == 1) { + insertKlayman<KmScene1705>(431, 434); + sendMessage(_klayman, 0x2000, 1); + setMessageList(0x004B6A08, false); + sendMessage(this, 0x2000, 1); + _klayman->setClipRect(0, 0, _sprite->getDrawRect().x2(), 480); + } else if (which == 2) { + insertKlayman<KmScene1705>(431, 434); + sendMessage(_klayman, 0x2000, 1); + setMessageList(0x004B6AA0, false); + sendMessage(this, 0x2000, 1); + _klayman->setClipRect(0, 0, _sprite->getDrawRect().x2(), 480); + } else if (which == 3) { + insertKlayman<KmScene1705>(431, 434); + sendMessage(_klayman, 0x2000, 1); + setMessageList(0x004B6A18, false); + sendMessage(this, 0x2000, 1); + _klayman->setClipRect(0, 0, _sprite->getDrawRect().x2(), 480); + } else { + insertKlayman<KmScene1705>(231, 74); + sendMessage(_klayman, 0x2000, 0); + setMessageList(0x004B69F0); + sendMessage(this, 0x2000, 0); + tempSprite = insertStaticSprite(0x30303822, 1100); + _klayman->setClipRect(0, tempSprite->getDrawRect().y, _sprite->getDrawRect().x2(), 480); + } + +} + +void Scene1705::update() { + Scene::update(); + if (_klayman->getX() < 224 && _paletteArea != 0) { + _palette->addBasePalette(0xF2210C15, 0, 64, 0); + _palette->startFadeToPalette(12); + _paletteArea = 0; + } else if (_klayman->getX() >= 224 && _paletteArea == 0) { + _palette->addBasePalette(0x91D3A391, 0, 64, 0); + _palette->startFadeToPalette(12); + _paletteArea = 1; + } +} + +uint32 Scene1705::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2000: + if (param.asInteger()) { + setRectList(0x004B6B40); + _klayman->setKlaymanTable3(); + } else { + setRectList(0x004B6B30); + _klayman->setKlaymanTable1(); + } + break; + case 0x4826: + if (sender == _class606 && _klayman->getX() <= 318) { + sendEntityMessage(_klayman, 0x1014, sender); + setMessageList(0x004B6AC0); + } + break; + } + return 0; +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/module1700.h b/engines/neverhood/module1700.h new file mode 100644 index 0000000000..f7388484b2 --- /dev/null +++ b/engines/neverhood/module1700.h @@ -0,0 +1,72 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef NEVERHOOD_MODULE1700_H +#define NEVERHOOD_MODULE1700_H + +#include "neverhood/neverhood.h" +#include "neverhood/module.h" +#include "neverhood/scene.h" +#include "neverhood/smackerscene.h" + +namespace Neverhood { + +class Module1700 : public Module { +public: + Module1700(NeverhoodEngine *vm, Module *parentModule, int which); + virtual ~Module1700(); +protected: + SoundResource _soundResource; + void createScene(int sceneNum, int which); + void updateScene(); +}; + +// Scene1705 + +class Class602 : public StaticSprite { +public: + Class602(NeverhoodEngine *vm, uint32 fileHash, int index); +}; + +class Class606 : public StaticSprite { +public: + Class606(NeverhoodEngine *vm, Scene *parentScene, int index, int surfacePriority, int16 x, int16 y, uint32 fileHash); +protected: + Scene *_parentScene; + int _index; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene1705 : public Scene { +public: + Scene1705(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + Sprite *_sprite; + Sprite *_class606; + int _paletteArea; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_MODULE1700_H */ diff --git a/engines/neverhood/module1800.cpp b/engines/neverhood/module1800.cpp new file mode 100644 index 0000000000..cce72b0a11 --- /dev/null +++ b/engines/neverhood/module1800.cpp @@ -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 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "neverhood/module1800.h" +#include "neverhood/navigationscene.h" + +namespace Neverhood { + +Module1800::Module1800(NeverhoodEngine *vm, Module *parentModule, int which) + : Module(vm, parentModule) { + + // TODO Sound1ChList_addSoundResources(0x04A14718, dword_4AFE70); + // TODO Sound1ChList_setSoundValuesMulti(dword_4AFE70, 1, 50, 600, 10, 150); + // TODO Sound1ChList_sub_407C70(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() { + // TODO Sound1ChList_sub_407A50(0x04A14718); +} + +void Module1800::createScene(int sceneNum, int which) { + static const byte kNavigationTypes00[] = {1, 0, 2, 0}; + static const byte kNavigationTypes01[] = {5}; + debug("Module1800::createScene(%d, %d)", sceneNum, which); + _vm->gameState().sceneNum = sceneNum; + switch (_vm->gameState().sceneNum) { + case 0: + createNavigationScene(0x004AFD38, which, kNavigationTypes00); + break; + case 1: + createNavigationScene(0x004AFD98, which, kNavigationTypes01); + break; + case 2: + createSmackerScene(0x006C0085, true, true, false); + break; + case 3: + createNavigationScene(0x004AFDB0, which); + break; + case 4: + createNavigationScene(0x004AFDE0, which); + break; + case 5: + createNavigationScene(0x004AFE40, which); + break; + case 6: + // TODO Sound1ChList_sub_407A50(0x04A14718); + createSmackerScene(0x08D84010, true, true, false); + break; + case 7: + // TODO Sound1ChList_setSoundValuesMulti(dword_4AFE70, 0, 0, 0, 0, 0); + createSmackerScene(0x0168B121, true, true, false); + break; + case 8: + // TODO _childObject = new CreditsScene(_vm, this, 0); + break; + case 9: + // NOTE: Newly introduced sceneNum 9 (was duplicate 3 with own update handler) + createSmackerScene(0x0A840C01, true, true, false); + break; + } + SetUpdateHandler(&Module1800::updateScene); + _childObject->handleUpdate(); +} + +void Module1800::updateScene() { + if (!updateChild()) { + switch (_vm->gameState().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(9, -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: + leaveModule(1); + // TODO GameState stuff + break; + case 9: + leaveModule(0); + break; + } + } else { + switch (_vm->gameState().sceneNum) { + case 0: +#if 0 // TODO + NavigationScene *navigationScene = (NavigationScene*)_childObject; + if (navigationScene->soundFlag1 && navigationScene->index == 2) { + // TODO Sound1ChList_sub_4080B0(false); + } +#endif + break; + } + } +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/module1800.h b/engines/neverhood/module1800.h new file mode 100644 index 0000000000..8ec8dfecea --- /dev/null +++ b/engines/neverhood/module1800.h @@ -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 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef NEVERHOOD_MODULE1800_H +#define NEVERHOOD_MODULE1800_H + +#include "neverhood/neverhood.h" +#include "neverhood/module.h" +#include "neverhood/scene.h" + +namespace Neverhood { + +// Module1800 + +class Module1800 : public Module { +public: + Module1800(NeverhoodEngine *vm, Module *parentModule, int which); + virtual ~Module1800(); +protected: + void createScene(int sceneNum, int which); + void updateScene(); +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_MODULE1800_H */ diff --git a/engines/neverhood/module1900.cpp b/engines/neverhood/module1900.cpp new file mode 100644 index 0000000000..7bac3ffc96 --- /dev/null +++ b/engines/neverhood/module1900.cpp @@ -0,0 +1,691 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "neverhood/module1900.h" +#include "neverhood/gamemodule.h" + +namespace Neverhood { + +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); + } + + // TODO Sound1ChList_addSoundResources(0x04E1C09C, dword_4B8800, true); + // TODO Sound1ChList_setSoundValuesMulti(dword_4B8800, true, 50, 600, 5, 150); + +} + +Module1900::~Module1900() { + // TODO Sound1ChList_sub_407A50(0x04E1C09C); +} + +void Module1900::createScene(int sceneNum, int which) { + debug("Module1900::createScene(%d, %d)", sceneNum, which); + _vm->gameState().sceneNum = sceneNum; + switch (_vm->gameState().sceneNum) { + case 0: + _childObject = new Scene1901(_vm, this, which); + break; + case 6: + _childObject = new Scene1907(_vm, this, which); + break; + } + SetUpdateHandler(&Module1900::updateScene); + _childObject->handleUpdate(); +} + +void Module1900::updateScene() { + if (!updateChild()) { + switch (_vm->gameState().sceneNum) { + case 0: + if (_moduleResult == 1) { + createScene(6, 0); + } else { + leaveModule(0); + } + break; + case 6: + createScene(0, 1); + break; + } + } +} + +// Scene1901 + +Scene1901::Scene1901(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule, true) { + + Sprite *tempSprite; + + _surfaceFlag = true; + + setRectList(0x004B34C8); + + setBackground(0x01303227); + setPalette(0x01303227); + insertMouse433(0x0322301B); + + insertStaticSprite(0x42213133, 1100); + + if (!getGlobalVar(0xA9035F60)) { + insertStaticSprite(0x40A40168, 100); + } else if (getGlobalVar(0x09221A62)) { + insertStaticSprite(0x124404C4, 100); + setGlobalVar(0x2050861A, 1); + } else { + insertStaticSprite(0x02840064, 100); + } + + if (which < 0) { + insertKlayman<KmScene1901>(120, 380); + setMessageList(0x004B3408); + } else if (which == 1) { + insertKlayman<KmScene1901>(372, 380); + setMessageList(0x004B3410); + } else { + insertKlayman<KmScene1901>(0, 380); + setMessageList(0x004B3400); + } + + tempSprite = insertStaticSprite(0x4830A402, 1100); + _klayman->setClipRect(tempSprite->getDrawRect().x, 0, 640, 480); + +} + +static const NPoint kAsScene1907SymbolGroundPositions[] = { + {160, 310}, + { 90, 340}, + {210, 335}, + {210, 380}, + {310, 340}, + {290, 400}, + {400, 375}, + {370, 435}, + {475, 415} +}; + +static const NPoint kAsScene1907SymbolPluggedInPositions[] = { + {275, 125}, + {244, 125}, + {238, 131}, + {221, 135}, + {199, 136}, + {168, 149}, + {145, 152}, + {123, 154}, + {103, 157} +}; + +static const NPoint kAsScene1907SymbolGroundHitPositions[] = { + {275, 299}, + {244, 299}, + {238, 305}, + {221, 309}, + {199, 310}, + {168, 323}, + {145, 326}, + {123, 328}, + {103, 331} +}; + +static const NPoint kAsScene1907SymbolPluggedInDownPositions[] = { + {275, 136}, + {244, 156}, + {238, 183}, + {221, 207}, + {199, 228}, + {168, 262}, + {145, 285}, + {123, 307}, + {103, 331} +}; + +static const uint32 kAsScene1907SymbolFileHashes[] = { + 0x006A1034, + 0x006A1010, + 0x006A1814, + 0x006A1016, + 0x006A0014, + 0x002A1014, + 0x00EA1014, + 0x206A1014, + 0x046A1414 +}; + +int AsScene1907Symbol::_symbolFlag1 = 0; +int AsScene1907Symbol::_symbolFlag2 = 0; + +AsScene1907Symbol::AsScene1907Symbol(NeverhoodEngine *vm, Scene1907 *parentScene, int elementIndex, int positionIndex) + : AnimatedSprite(vm, 1000 - positionIndex), _soundResource1(vm), _soundResource2(vm), _soundResource3(vm), + _parentScene(parentScene), _elementIndex(elementIndex), _isMoving(false) { + + _symbolFlag1 = 0; + _symbolFlag2 = 0; + + if (getGlobalVar(0xA9035F60)) { + _isPluggedIn = true; + _currPositionIndex = elementIndex; + if (!getGlobalVar(0x09221A62)) { + _x = kAsScene1907SymbolPluggedInPositions[_currPositionIndex].x; + _y = kAsScene1907SymbolPluggedInPositions[_currPositionIndex].y; + } else { + _x = kAsScene1907SymbolPluggedInDownPositions[_currPositionIndex].x; + _y = kAsScene1907SymbolPluggedInDownPositions[_currPositionIndex].y; + } + createSurface1(kAsScene1907SymbolFileHashes[_elementIndex], 1000 + _currPositionIndex); + setFileHash(kAsScene1907SymbolFileHashes[_elementIndex], -1, -1); + _newHashListIndex = -2; + } else { + _isPluggedIn = false; + _currPositionIndex = positionIndex; + _soundResource1.load(0x74231924); + _soundResource2.load(0x36691914); + _soundResource3.load(0x5421D806); + _parentScene->setPositionFree(_currPositionIndex, false); + _x = kAsScene1907SymbolGroundPositions[_currPositionIndex].x; + _y = kAsScene1907SymbolGroundPositions[_currPositionIndex].y; + createSurface1(kAsScene1907SymbolFileHashes[_elementIndex], 1000 + _currPositionIndex); + setFileHash(kAsScene1907SymbolFileHashes[_elementIndex], 0, -1); + _newHashListIndex = 0; + } + _deltaRect.set(0, 0, 80, 80); + Sprite::processDelta(); + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene1907Symbol::handleMessage); + +} + +void AsScene1907Symbol::update() { + AnimatedSprite::updateAnim(); + handleSpriteUpdate(); + AnimatedSprite::updatePosition(); + if (_symbolFlag1 && !_symbolFlag2) + _symbolFlag1 = 0; +} + +uint32 AsScene1907Symbol::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + if (!_isPluggedIn && !_symbolFlag1) { + tryToPlugIn(); + messageResult = 1; + } else { + messageResult = 0; + } + break; + } + return messageResult; +} + +uint32 AsScene1907Symbol::hmTryToPlugIn(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + removeCallbacks(); + break; + } + return messageResult; +} + +void AsScene1907Symbol::suTryToPlugIn() { + _currStep++; + _x -= _deltaX; + _y -= _deltaY; + if (_currStep == 16) { + _x -= _smallDeltaX; + _y -= _smallDeltaY; + SetSpriteCallback(NULL); + } +} + +void AsScene1907Symbol::suFallOff() { + if (_fallOffDelay != 0) { + _fallOffDelay--; + } else { + _y += _yAccel; + _yAccel += 8; + if (_y >= kAsScene1907SymbolGroundHitPositions[_currPositionIndex].y) { + _y = kAsScene1907SymbolGroundHitPositions[_currPositionIndex].y; + stFallOffHitGround(); + } + } +} + +void AsScene1907Symbol::suFallOffHitGround() { + + if (_x == _someX - _xBreak) + _x -= _smallDeltaX; + else + _x -= _deltaX; + + if (_y == kAsScene1907SymbolGroundHitPositions[_currPositionIndex].y) { + _y -= _someY; + } + + if (_currStep < 8) { + _y -= _yAccel; + _yAccel -= 4; + if (_yAccel < 0) + _yAccel = 0; + } else if (_currStep < 15) { + _y += _yAccel; + _yAccel += 4; + } else { + _y = kAsScene1907SymbolGroundPositions[_newPositionIndex].y; + cbFallOffHitGroundEvent(); + } + + _currStep++; +} + +void AsScene1907Symbol::suMoveDown() { + _y += _yIncr; + if (_yIncr < 11) + _yIncr++; + if (_y >= kAsScene1907SymbolPluggedInDownPositions[_elementIndex].y) { + _y = kAsScene1907SymbolPluggedInDownPositions[_elementIndex].y; + _isMoving = false; + SetSpriteCallback(NULL); + } +} + +void AsScene1907Symbol::suMoveUp() { + _y -= _yIncr; + if (getGlobalVar(0x10938830)) { + 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; + SetSpriteCallback(NULL); + } +} + +void AsScene1907Symbol::tryToPlugIn() { + _isPluggedIn = true; + _symbolFlag2++; + _newPositionIndex = _parentScene->getNextPosition(); + _parentScene->setPositionFree(_currPositionIndex, true); + sendMessage(_parentScene, 0x1022, 1100 + _newPositionIndex); + setFileHash(kAsScene1907SymbolFileHashes[_elementIndex], 0, -1); + SetUpdateHandler(&AsScene1907Symbol::update); + SetMessageHandler(&AsScene1907Symbol::hmTryToPlugIn); + SetSpriteCallback(&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 { + _symbolFlag1 = 1; + 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; + setFileHash(kAsScene1907SymbolFileHashes[_elementIndex], -1, 0); + _playBackwards = true; + _newHashListIndex = -2; + _currStep = 0; + _yAccel = 1; + SetUpdateHandler(&AsScene1907Symbol::update); + SetMessageHandler(&AsScene1907Symbol::handleMessage); + SetSpriteCallback(&AsScene1907Symbol::suFallOff); +} + +void AsScene1907Symbol::stFallOffHitGround() { + _soundResource2.play(); + sendMessage(_parentScene, 0x1022, 1000 + _newPositionIndex); + // TODO: Meh... + Entity::_priority = 1000 - _newPositionIndex; + _vm->_collisionMan->removeSprite(this); + _vm->_collisionMan->addSprite(this); + SetSpriteCallback(&AsScene1907Symbol::suFallOffHitGround); + NextState(&AsScene1907Symbol::cbFallOffHitGroundEvent); + _newHashListIndex = 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 (_symbolFlag2) + _symbolFlag2--; + setFileHash(kAsScene1907SymbolFileHashes[_elementIndex], 0, -1); + _newHashListIndex = 0; + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene1907Symbol::handleMessage); + SetSpriteCallback(NULL); + processDelta(); + _soundResource3.play(); +} + +void AsScene1907Symbol::stPlugIn() { + _soundResource1.play(); + _currPositionIndex = _newPositionIndex; + stopAnimation(); + SetMessageHandler(&AsScene1907Symbol::handleMessage); + SetSpriteCallback(NULL); + if (_elementIndex == 8) + sendMessage(_parentScene, 0x2001, 0); +} + +void AsScene1907Symbol::stPlugInFail() { + _currPositionIndex = _newPositionIndex; + stopAnimation(); + _parentScene->plugInFailed(); +} + +void AsScene1907Symbol::moveUp() { + setFileHash(kAsScene1907SymbolFileHashes[_elementIndex], -1, -1);//???? + stopAnimation(); + SetMessageHandler(&AsScene1907Symbol::handleMessage); + SetSpriteCallback(&AsScene1907Symbol::suMoveUp); + _yIncr = 1; + _isMoving = true; +} + +void AsScene1907Symbol::moveDown() { + setFileHash(kAsScene1907SymbolFileHashes[_elementIndex], -1, -1);//???? + stopAnimation(); + SetMessageHandler(&AsScene1907Symbol::handleMessage); + SetSpriteCallback(&AsScene1907Symbol::suMoveDown); + _yIncr = 4; + _isMoving = true; +} + +SsScene1907UpDownButton::SsScene1907UpDownButton(NeverhoodEngine *vm, Scene1907 *parentScene, AsScene1907Symbol *AsScene1907Symbol) + : StaticSprite(vm, 1400), _soundResource(vm), _parentScene(parentScene), _AsScene1907Symbol(AsScene1907Symbol), + _countdown1(0) { + + _spriteResource.load2(0x64516424); + createSurface(1400, _spriteResource.getDimensions().width, _spriteResource.getDimensions().height); + setVisible(false); + _drawRect.set(0, 0, _spriteResource.getDimensions().width, _spriteResource.getDimensions().height); + _deltaRect = _drawRect; + _x = _spriteResource.getPosition().x; + _y = _spriteResource.getPosition().y; + processDelta(); + _needRefresh = true; + _soundResource.load(0x44061000); + SetUpdateHandler(&SsScene1907UpDownButton::update); + SetMessageHandler(&SsScene1907UpDownButton::handleMessage); + if (getGlobalVar(0xA9035F60)) { + if (getGlobalVar(0x09221A62)) + setToDownPosition(); + else + setToUpPosition(); + } +} + +void SsScene1907UpDownButton::update() { + StaticSprite::update(); + if (_countdown1 != 0 && (--_countdown1 == 0)) { + setVisible(false); + sendMessage(_parentScene, 0x2000, 0); + } +} + +uint32 SsScene1907UpDownButton::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + if (_countdown1 == 0 && !_AsScene1907Symbol->isMoving() && getGlobalVar(0xA9035F60)) { + setVisible(true); + _countdown1 = 4; + StaticSprite::update(); + _soundResource.play(); + } + messageResult = 1; + } + return messageResult; +} + +void SsScene1907UpDownButton::setToUpPosition() { + _y = _spriteResource.getPosition().y; + Sprite::processDelta(); + StaticSprite::update(); +} + +void SsScene1907UpDownButton::setToDownPosition() { + _y = _spriteResource.getPosition().y + 174; + Sprite::processDelta(); + StaticSprite::update(); +} + +AsScene1907WaterHint::AsScene1907WaterHint(NeverhoodEngine *vm) + : AnimatedSprite(vm, 1400) { + + createSurface1(0x110A1061, 1500); + _x = 320; + _y = 240; + setFileHash(0x110A1061, 0, -1); + _newHashListIndex = 0; + setVisible(false); + _needRefresh = true; + AnimatedSprite::updatePosition(); + SetUpdateHandler(&AsScene1907WaterHint::update); + SetMessageHandler(&Sprite::handleMessage); +} + +void AsScene1907WaterHint::update() { + AnimatedSprite::updateAnim(); + AnimatedSprite::updatePosition(); +} + +uint32 AsScene1907WaterHint::handleMessage46BA20(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + removeCallbacks(); + break; + } + return messageResult; +} + +void AsScene1907WaterHint::show() { + setVisible(true); + setFileHash(0x110A1061, 0, -1); + SetMessageHandler(&AsScene1907WaterHint::handleMessage46BA20); + NextState(&AsScene1907WaterHint::hide); +} + +void AsScene1907WaterHint::hide() { + stopAnimation(); + setVisible(false); + SetMessageHandler(&Sprite::handleMessage); +} + +Scene1907::Scene1907(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule, true), _soundResource1(vm), _soundResource2(vm), + _soundResource3(vm), _soundResource4(vm), _currMovingSymbolIndex(0), _pluggedInCount(0), + _moveDownCountdown(0), _moveUpCountdown(0), _countdown3(0), _hasPlugInFailed(false) { + + _surfaceFlag = true; + + //setGlobalVar(0x10938830, 1); + + 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()); + _vm->_collisionMan->addSprite(_asSymbols[i]); + } + + _ssUpDownButton = insertSprite<SsScene1907UpDownButton>(this, _asSymbols[8]); + _vm->_collisionMan->addSprite(_ssUpDownButton); + + _asWaterHint = insertSprite<AsScene1907WaterHint>(); + + insertMouse435(0x28E0120E, 20, 620); + + SetMessageHandler(&Scene1907::handleMessage); + SetUpdateHandler(&Scene1907::update); + + if (getGlobalVar(0xA9035F60)) + _pluggedInCount = 9; + + _soundResource1.load(0x72004A10); + _soundResource2.load(0x22082A12); + _soundResource3.load(0x21100A10); + _soundResource4.load(0x68E25540); + +} + +void Scene1907::update() { + Scene::update(); + + if (_hasPlugInFailed) { + int fallOffDelay = 0; + _hasPlugInFailed = false; + for (int i = 0; i < 9; i++) { + AsScene1907Symbol *asSymbol = _asSymbols[8 - i]; + if (asSymbol->isPluggedIn()) { + asSymbol->fallOff(getRandomPositionIndex(), fallOffDelay); + fallOffDelay += _vm->_rnd->getRandomNumber(10 - 1) + 4; + } + } + } + + if (_moveDownCountdown != 0 && (--_moveDownCountdown == 0)) { + _asSymbols[_currMovingSymbolIndex]->moveDown(); + if (_currMovingSymbolIndex > 0) { + _moveDownCountdown = 2; + _currMovingSymbolIndex--; + } + } + + if (_moveUpCountdown != 0 && (--_moveUpCountdown == 0)) { + _moveDownCountdown = 0; + for (int i = 0; i < 9; i++) + _asSymbols[i]->moveUp(); + } + + if (_countdown3 != 0 && (--_countdown3 == 0)) { + _asWaterHint->show(); + _moveUpCountdown = 4; + } + +} + +uint32 Scene1907::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x0001: + // TODO DEBUG stuff + if ((param.asPoint().x <= 20 || param.asPoint().x >= 620) && + !_hasPlugInFailed && _moveDownCountdown == 0 && _moveUpCountdown == 0 && _countdown3 == 0) { + leaveScene(0); + } + break; + // TODO Debug stuff + case 0x2000: + if (getGlobalVar(0x09221A62)) { + _soundResource1.play(); + for (int i = 0; i < 9; i++) + _asSymbols[i]->moveUp(); + _ssUpDownButton->setToUpPosition(); + setGlobalVar(0x09221A62, 0); + } else { + if (!getGlobalVar(0x10938830)) { + _soundResource3.play(); + _countdown3 = 5; + } else { + _soundResource2.play(); + _ssUpDownButton->setToDownPosition(); + setGlobalVar(0x09221A62, 1); + } + _moveDownCountdown = 1; + _currMovingSymbolIndex = 8; + } + break; + case 0x2001: + _soundResource4.play(); + setGlobalVar(0xA9035F60, 1); + break; + } + return 0; +} + +void Scene1907::plugInFailed() { + _pluggedInCount = 0; + _hasPlugInFailed = true; +} + +int Scene1907::getRandomPositionIndex() { + bool flag = false; + int index = 0; + for (int i = 0; i < 9; i++) { + if (_positionFree[i]) + flag = true; + } + if (flag) { + flag = false; + while (!flag) { + index = _vm->_rnd->getRandomNumber(9 - 1); + if (_positionFree[index]) + flag = true; + } + } + return index; +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/module1900.h b/engines/neverhood/module1900.h new file mode 100644 index 0000000000..7b41c5780b --- /dev/null +++ b/engines/neverhood/module1900.h @@ -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 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef NEVERHOOD_MODULE1900_H +#define NEVERHOOD_MODULE1900_H + +#include "neverhood/neverhood.h" +#include "neverhood/module.h" +#include "neverhood/scene.h" +#include "neverhood/module1200.h" + +namespace Neverhood { + +class Module1900 : public Module { +public: + Module1900(NeverhoodEngine *vm, Module *parentModule, int which); + virtual ~Module1900(); +protected: + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void createScene(int sceneNum, int which); + void updateScene(); +}; + +// Scene1901 + +class Scene1901 : public Scene { +public: + Scene1901(NeverhoodEngine *vm, Module *parentModule, int which); +}; + +// Scene1907 + +class Scene1907; + +class AsScene1907Symbol : public AnimatedSprite { +public: + AsScene1907Symbol(NeverhoodEngine *vm, Scene1907 *parentScene, int elementIndex, int positionIndex); + void moveUp(); + void moveDown(); + void fallOff(int newPositionIndex, int fallOffDelay); + bool isPluggedIn() { return _isPluggedIn; } + bool isMoving() { return _isMoving; } +protected: + SoundResource _soundResource1; + SoundResource _soundResource2; + SoundResource _soundResource3; + 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 int _symbolFlag1; + static int _symbolFlag2; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 hmTryToPlugIn(int messageNum, const MessageParam ¶m, Entity *sender); + void suTryToPlugIn(); + void suFallOff(); + void suFallOffHitGround(); + void suMoveDown(); + void suMoveUp(); + void tryToPlugIn(); + void stFallOffHitGround(); + void cbFallOffHitGroundEvent(); + void stPlugIn(); + void stPlugInFail(); +}; + +class AsScene1907WaterHint : public AnimatedSprite { +public: + AsScene1907WaterHint(NeverhoodEngine *vm); + void show(); +protected: + void update(); + uint32 handleMessage46BA20(int messageNum, const MessageParam ¶m, Entity *sender); + void hide(); +}; + +class SsScene1907UpDownButton : public StaticSprite { +public: + SsScene1907UpDownButton(NeverhoodEngine *vm, Scene1907 *parentScene, AsScene1907Symbol *AsScene1907Symbol); + void setToUpPosition(); + void setToDownPosition(); +protected: + SoundResource _soundResource; + Scene1907 *_parentScene; + AsScene1907Symbol *_AsScene1907Symbol; + int _countdown1; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene1907 : public Scene { +public: + Scene1907(NeverhoodEngine *vm, Module *parentModule, int which); + void plugInFailed(); + void setPositionFree(int index, bool value) { _positionFree[index] = value; } + int getNextPosition() { return _pluggedInCount++; } +protected: + SoundResource _soundResource1; + SoundResource _soundResource2; + SoundResource _soundResource3; + SoundResource _soundResource4; + AsScene1907Symbol *_asSymbols[9]; + SsScene1907UpDownButton *_ssUpDownButton; + AsScene1907WaterHint *_asWaterHint; + int _currMovingSymbolIndex; + int _pluggedInCount; + int _moveDownCountdown; + int _moveUpCountdown; + int _countdown3; + bool _hasPlugInFailed; + bool _positionFree[9]; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + int getRandomPositionIndex(); +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_MODULE1900_H */ diff --git a/engines/neverhood/module2000.cpp b/engines/neverhood/module2000.cpp new file mode 100644 index 0000000000..9c0843c3e1 --- /dev/null +++ b/engines/neverhood/module2000.cpp @@ -0,0 +1,155 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "neverhood/module2000.h" +#include "neverhood/gamemodule.h" +#include "neverhood/navigationscene.h" + +namespace Neverhood { + +Module2000::Module2000(NeverhoodEngine *vm, Module *parentModule, int which) + : Module(vm, parentModule) { + + if (which < 0) { + createScene(_vm->gameState().sceneNum, -1); + } else if (which == 0) { + createScene(0, 3); + } else if (which == 1) { + createScene(0, 1); + } + +} + +Module2000::~Module2000() { + // TODO Sound1ChList_sub_407A50(0x81293110); +} + +void Module2000::createScene(int sceneNum, int which) { + debug("Module2000::createScene(%d, %d)", sceneNum, which); + _vm->gameState().sceneNum = sceneNum; + switch (_vm->gameState().sceneNum) { + case 0: + _childObject = new Scene2001(_vm, this, which); + break; + case 1: + createNavigationScene(getGlobalVar(0x98109F12) ? 0x004B7B48 : 0x004B7B00, which); + break; + case 2: + setGlobalVar(0x98109F12, 1); + setSubVar(0x2C145A98, 1, 1); + createSmackerScene(0x204B2031, true, true, false); + break; + } + SetUpdateHandler(&Module2000::updateScene); + _childObject->handleUpdate(); +} + +void Module2000::updateScene() { + if (!updateChild()) { + switch (_vm->gameState().sceneNum) { + case 0: + if (_moduleResult == 1) { + leaveModule(0); + } else { + createScene(1, 0); + } + break; + case 1: + if (_moduleResult == 0) { + if (getGlobalVar(0x98109F12)) { + createScene(1, 0); + } else { + createScene(2, -1); + } + } else if (_moduleResult == 1) { + createScene(1, 1); + } else if (_moduleResult == 2) { + createScene(0, 0); + } + break; + case 2: + createScene(1, 0); + break; + } + } +} + +// Scene2001 + +Scene2001::Scene2001(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule, true) { + + _surfaceFlag = true; + SetMessageHandler(&Scene2001::handleMessage); + + setBackground(0xA6417244); + setPalette(0xA6417244); + insertMouse433(0x17240A6C); + + _class401 = insertStaticSprite(0x0D641724, 1100); + + if (which < 0) { + insertKlayman<KmScene2001>(300, 345); + setMessageList(0x004B3538); + sendMessage(this, 0x2000, 0); + } else if (which == 1) { + insertKlayman<KmScene2001>(116, 345); + sendMessage(_klayman, 0x2000, 1); + setMessageList(0x004B3540, false); + sendMessage(this, 0x2000, 1); + } else if (which == 2) { + insertKlayman<KmScene2001>(116, 345); + sendMessage(_klayman, 0x2000, 1); + setMessageList(0x004B35F0, false); + sendMessage(this, 0x2000, 1); + } else if (which == 3) { + insertKlayman<KmScene2001>(116, 345); + sendMessage(_klayman, 0x2000, 1); + setMessageList(0x004B3550, false); + sendMessage(this, 0x2000, 1); + } else { + insertKlayman<KmScene2001>(390, 345); + setMessageList(0x004B3530); + sendMessage(this, 0x2000, 0); + _klayman->setDoDeltaX(1); + } + + _klayman->setClipRect(_class401->getDrawRect().x, 0, 640, 480); + +} + +uint32 Scene2001::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2000: + if (param.asInteger()) { + setRectList(0x004B3680); + _klayman->setKlaymanTable3(); + } else { + setRectList(0x004B3670); + _klayman->setKlaymanTable1(); + } + } + return 0; +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/module2000.h b/engines/neverhood/module2000.h new file mode 100644 index 0000000000..8e741b357e --- /dev/null +++ b/engines/neverhood/module2000.h @@ -0,0 +1,55 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef NEVERHOOD_MODULE2000_H +#define NEVERHOOD_MODULE2000_H + +#include "neverhood/neverhood.h" +#include "neverhood/module.h" +#include "neverhood/scene.h" +#include "neverhood/module1200.h" + +namespace Neverhood { + +class Module2000 : public Module { +public: + Module2000(NeverhoodEngine *vm, Module *parentModule, int which); + virtual ~Module2000(); +protected: + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void createScene(int sceneNum, int which); + void updateScene(); +}; + +// Scene2001 + +class Scene2001 : public Scene { +public: + Scene2001(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + Sprite *_class401; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_MODULE2000_H */ diff --git a/engines/neverhood/module2100.cpp b/engines/neverhood/module2100.cpp new file mode 100644 index 0000000000..66f8631b8a --- /dev/null +++ b/engines/neverhood/module2100.cpp @@ -0,0 +1,341 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "neverhood/module2100.h" +#include "neverhood/gamemodule.h" +#include "neverhood/module1200.h" + +namespace Neverhood { + +Module2100::Module2100(NeverhoodEngine *vm, Module *parentModule, int which) + : Module(vm, parentModule) { + + // TODO Music18hList_add(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() { + // TODO Music18hList_deleteGroup(0x10A10C14); +} + +void Module2100::createScene(int sceneNum, int which) { + debug("Module2100::createScene(%d, %d)", sceneNum, which); + _vm->gameState().sceneNum = sceneNum; + switch (_vm->gameState().sceneNum) { + case 0: + // TODO Music18hList_play(0x11482B95, 0, 1, 1); + _childObject = new Scene2101(_vm, this, which); + break; + } + SetUpdateHandler(&Module2100::updateScene); + _childObject->handleUpdate(); +} + +void Module2100::updateScene() { + if (!updateChild()) { + switch (_vm->gameState().sceneNum) { + case 0: + if (_moduleResult == 1) { + setGlobalVar(0x2090590C, 1); + leaveModule(0); + } else { + leaveModule(1); + } + break; + } + } +} + +// Scene2101 + +Class538::Class538(NeverhoodEngine *vm, bool flag) + : AnimatedSprite(vm, 1100), _soundResource(vm) { + + // TODO createSurface3(100, dword_4B9018); + createSurface(100, 640, 480); //TODO: Remove once the line above is done + _x = 320; + _y = 240; + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&Class538::handleMessage); + if (flag) { + setFileHash(0x0C202B9C, -1, -1); + _newHashListIndex = -2; + } else { + setVisible(false); + } +} + +uint32 Class538::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + removeCallbacks(); + break; + case 0x4808: + openDoor(); + break; + case 0x4809: + closeDoor(); + break; + } + return messageResult; +} + +void Class538::openDoor() { + setFileHash(0x0C202B9C, 0, -1); + _newHashListIndex = -2; + setVisible(true); + _soundResource.play(calcHash("fxDoorOpen32")); +} + +void Class538::closeDoor() { + setFileHash(0xC222A8D4, 0, -1); + _newHashListIndex = -2; + setVisible(true); + NextState(&Class538::hide); + _soundResource.play(calcHash("fxDoorClose32")); +} + +void Class538::hide() { + stopAnimation(); + setVisible(false); +} + +Class539::Class539(NeverhoodEngine *vm, Sprite *klayman) + : AnimatedSprite(vm, 1400), _klayman(klayman) { + + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&Class539::handleMessage); + createSurface(1200, 88, 165); + setVisible(false); +} + +uint32 Class539::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2001: + _x = _klayman->getX(); + _y = _klayman->getY() - 132; + setFileHash(0x0422255A, 0, -1); + setVisible(true); + break; + case 0x3002: + stopAnimation(); + setVisible(false); + break; + } + return messageResult; +} + +Class427::Class427(NeverhoodEngine *vm, Scene *parentScene, uint32 fileHash1, uint32 fileHash2, int surfacePriority, uint32 soundFileHash) + : StaticSprite(vm, 1100), _soundResource(vm), _parentScene(parentScene), _countdown(0), + _fileHash1(fileHash1), _fileHash2(fileHash2), _soundFileHash(soundFileHash) { + + SetUpdateHandler(&Class427::update); + SetMessageHandler(&Class427::handleMessage); + if (_soundFileHash == 0) + _soundFileHash = 0x44141000; + createSurface(1010, 61, 30); + if (_fileHash1) { + load(_fileHash1, true, true); + StaticSprite::update(); + } else + setVisible(false); +} + +void Class427::update() { + if (_countdown != 0 && (--_countdown == 0)) { + sendMessage(_parentScene, 0x1022, 1010); + if (_fileHash1) { + load(_fileHash1, true, true); + StaticSprite::update(); + } else + setVisible(false); + } +} + +uint32 Class427::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x480B: + sendMessage(_parentScene, 0x480B, 0); + setVisible(true); + sendMessage(_parentScene, 0x1022, 990); + load(_fileHash2, true, true); + StaticSprite::update(); + _countdown = 16; + _soundResource.play(_soundFileHash); + break; + } + return messageResult; +} + +Scene2101::Scene2101(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule, true) { + + _surfaceFlag = true; + SetMessageHandler(&Scene2101::handleMessage); + SetUpdateHandler(&Scene2101::update); + + setBackground(0x44242305); + setPalette(0x44242305); + insertMouse433(0x4230144A); + + insertStaticSprite(0x00502330, 1100); + _sprite1 = insertStaticSprite(0x78492010, 1100); + _class427 = insertSprite<Class427>(this, 0x72427010, 0x32423010, 200, 0); + _asTape1 = insertSprite<AsScene1201Tape>(this, 18, 1100, 412, 443, 0x9148A011); + _vm->_collisionMan->addSprite(_asTape1); + _asTape2 = insertSprite<AsScene1201Tape>(this, 11, 1100, 441, 443, 0x9148A011); + _vm->_collisionMan->addSprite(_asTape2); + + if (which < 0) { + insertKlayman<KmScene2101>(380, 438); + setMessageList(0x004B8E48); + sendMessage(this, 0x2000, 0); + _class538 = insertSprite<Class538>(false); + _value1 = 1; + _countdown1 = 0; + } else if (which == 1) { + insertKlayman<KmScene2101>(640, 438); + setMessageList(0x004B8E50); + sendMessage(this, 0x2000, 0); + _class538 = insertSprite<Class538>(true); + _value1 = 2; + _countdown1 = 48; + } else if (which == 2) { + insertKlayman<KmScene2101>(115, 438); + sendMessage(_klayman, 0x2000, 1); + setMessageList(0x004B8F58); + sendMessage(this, 0x2000, 1); + _class538 = insertSprite<Class538>(false); + _value1 = 1; + _countdown1 = 0; + } else if (which == 3) { + insertKlayman<KmScene2101>(115, 438); + sendMessage(_klayman, 0x2000, 1); + setMessageList(0x004B8EB0); + sendMessage(this, 0x2000, 1); + _class538 = insertSprite<Class538>(false); + _value1 = 1; + _countdown1 = 0; + } else { + insertKlayman<KmScene2101>(115, 438); + sendMessage(_klayman, 0x2000, 1); + setMessageList(0x004B8EA0); + sendMessage(this, 0x2000, 1); + _class538 = insertSprite<Class538>(false); + _value1 = 1; + _countdown1 = 0; + } + + _class539 = insertSprite<Class539>(_klayman); + _klayman->setClipRect(0, 0, _sprite1->getDrawRect().x2(), 480); + +} + +void Scene2101::update() { + if (_countdown1 != 0) { + if (_value1 == 2) { + if (--_countdown1 == 0) { + sendMessage(_class538, 0x4809, 0); + _value1 = 1; + } + } else { + if (_klayman->getX() > 575) + _messageListFlag = false; + if (--_countdown1 == 0) { + if (_klayman->getX() < 480) { + sendMessage(_class538, 0x4809, 0); + _value1 = 1; + } else if (_klayman->getX() >= 480 && _klayman->getX() <= 575) { + _klayman->setDoDeltaX(0); + setMessageList2(0x004B8F48); + sendMessage(_class538, 0x4809, 0); + sendMessage(_class539, 0x2001, 0); + _value1 = 1; + } + } + } + } else if (_value1 == 1 && _messageValue >= 0 && _klayman->getX() > 470 /* TODO ! && _messageList2 != 0x004B8F48*/) { + setMessageList2(0x004B8F50); + } + Scene::update(); +} + +uint32 Scene2101::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x02144CB1) { + sendEntityMessage(_klayman, 0x1014, _class427); + } else if (param.asInteger() == 0x21E64A00) { + if (_value1 == 0) { + setMessageList(0x004B8E80); + } else { + setMessageList(0x004B8EC8); + } + } else if (param.asInteger() == 0x41442820) { + messageList402220(); + } + break; + case 0x2000: + if (param.asInteger() != 0) { + setRectList(0x004B9008); + _klayman->setKlaymanTable3(); + } else { + setRectList(0x004B8FF8); + _klayman->setKlaymanTable1(); + } + break; + case 0x480B: + if (sender == _class427 && _value1 == 1) { + sendMessage(_class538, 0x4808, 0); + _value1 = 0; + _countdown1 = 90; + } + break; + case 0x4826: + if (sender == _asTape1 || sender == _asTape2) { + if (_klayman->getX() >= 228 && _klayman->getX() <= 500) { + sendEntityMessage(_klayman, 0x1014, sender); + setMessageList(0x004B8F78); + } else if (_klayman->getX() < 228) { + setMessageList2(0x004B8F00); + } + } + break; + } + return 0; +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/module2100.h b/engines/neverhood/module2100.h new file mode 100644 index 0000000000..65846d7ded --- /dev/null +++ b/engines/neverhood/module2100.h @@ -0,0 +1,95 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef NEVERHOOD_MODULE2100_H +#define NEVERHOOD_MODULE2100_H + +#include "neverhood/neverhood.h" +#include "neverhood/module.h" +#include "neverhood/scene.h" + +namespace Neverhood { + +class Module2100 : public Module { +public: + Module2100(NeverhoodEngine *vm, Module *parentModule, int which); + virtual ~Module2100(); +protected: + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void createScene(int sceneNum, int which); + void updateScene(); +}; + +// Scene1901 + +class Class538 : public AnimatedSprite { +public: + Class538(NeverhoodEngine *vm, bool flag); +protected: + SoundResource _soundResource; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void openDoor(); + void closeDoor(); + void hide(); +}; + +class Class539 : public AnimatedSprite { +public: + Class539(NeverhoodEngine *vm, Sprite *klayman); +protected: + Sprite *_klayman; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Class427 : public StaticSprite { +public: + Class427(NeverhoodEngine *vm, Scene *parentScene, uint32 fileHash1, uint32 fileHash2, int surfacePriority, uint32 soundFileHash); +protected: + Scene *_parentScene; + SoundResource _soundResource; + uint32 _soundFileHash; + uint32 _fileHash1, _fileHash2; + int16 _countdown; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + + +class Scene2101 : public Scene { +public: + Scene2101(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + Sprite *_sprite1; + Sprite *_class427; + Sprite *_asTape1; + Sprite *_asTape2; + Sprite *_class538; + Sprite *_class539; + int _countdown1; + int _value1; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_MODULE2100_H */ diff --git a/engines/neverhood/module2200.cpp b/engines/neverhood/module2200.cpp new file mode 100644 index 0000000000..ab1fac8c5e --- /dev/null +++ b/engines/neverhood/module2200.cpp @@ -0,0 +1,2731 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "neverhood/module2200.h" +#include "neverhood/module1000.h" +#include "neverhood/module1200.h" +#include "neverhood/gamemodule.h" +#include "neverhood/diskplayerscene.h" + +namespace Neverhood { + +Module2200::Module2200(NeverhoodEngine *vm, Module *parentModule, int which) + : Module(vm, parentModule) { + + debug("Create Module2200(%d)", which); + + // TODO: Music18hList_add(0x11391412, 0x601C908C); + + if (which < 0) { + createScene(_vm->gameState().sceneNum, -1); + } else { + createScene(0, 0); + } + +} + +Module2200::~Module2200() { + // TODO Sound1ChList_sub_407A50(0x11391412); +} + +void Module2200::createScene(int sceneNum, int which) { + // CHECKME if this can be used regardless of the new sceneNum + if (sceneNum == 7 && which >= 0) + _vm->gameState().which = _vm->gameState().sceneNum; + _vm->gameState().sceneNum = sceneNum; + switch (_vm->gameState().sceneNum) { + case 0: + _childObject = new Scene2201(_vm, this, which); + break; + case 1: + // TODO Music18hList_play(0x601C908C, 0, 2, 1); + _childObject = new Scene2202(_vm, this, which); + break; + case 2: + // TODO Music18hList_play(0x601C908C, 0, 2, 1); + _childObject = new Scene2203(_vm, this, which); + break; + case 3: + // TODO Music18hList_stop(0x601C908C, 0, 2); + _childObject = new DiskplayerScene(_vm, this, 3); + break; + case 4: + // TODO Music18hList_stop(0x601C908C, 0, 2); + _childObject = new Scene2205(_vm, this, which); + break; + case 5: + // TODO Music18hList_stop(0x601C908C, 0, 2); + _childObject = new Scene2206(_vm, this, which); + break; + case 6: + _childObject = new Scene2207(_vm, this, which); + break; + case 7: + _childObject = new Scene2208(_vm, this, which); + break; + case 8: + _childObject = new Scene2208(_vm, this, which); + break; + case 9: + _childObject = new HallOfRecordsScene(_vm, this, which, 0x004B7180); + break; + case 10: + _childObject = new HallOfRecordsScene(_vm, this, which, 0x004B7198); + break; + case 11: + _childObject = new HallOfRecordsScene(_vm, this, which, 0x004B71B0); + break; + case 12: + _childObject = new HallOfRecordsScene(_vm, this, which, 0x004B71C8); + break; + case 13: + _childObject = new HallOfRecordsScene(_vm, this, which, 0x004B71E0); + break; + case 14: + _childObject = new HallOfRecordsScene(_vm, this, which, 0x004B71F8); + break; + case 15: + _childObject = new HallOfRecordsScene(_vm, this, which, 0x004B7210); + break; + case 16: + _childObject = new HallOfRecordsScene(_vm, this, which, 0x004B7228); + break; + case 17: + _childObject = new HallOfRecordsScene(_vm, this, which, 0x004B7240); + break; + case 18: + _childObject = new HallOfRecordsScene(_vm, this, which, 0x004B7258); + break; + case 19: + _childObject = new HallOfRecordsScene(_vm, this, which, 0x004B7270); + break; + case 20: + _childObject = new HallOfRecordsScene(_vm, this, which, 0x004B7288); + break; + case 21: + _childObject = new HallOfRecordsScene(_vm, this, which, 0x004B72A0); + break; + case 22: + _childObject = new HallOfRecordsScene(_vm, this, which, 0x004B72B8); + break; + case 23: + _childObject = new HallOfRecordsScene(_vm, this, which, 0x004B72D0); + break; + case 24: + _childObject = new HallOfRecordsScene(_vm, this, which, 0x004B72E8); + break; + case 25: + _childObject = new HallOfRecordsScene(_vm, this, which, 0x004B7300); + break; + case 26: + _childObject = new HallOfRecordsScene(_vm, this, which, 0x004B7318); + break; + case 27: + _childObject = new HallOfRecordsScene(_vm, this, which, 0x004B7330); + break; + case 28: + _childObject = new HallOfRecordsScene(_vm, this, which, 0x004B7348); + break; + case 29: + _childObject = new HallOfRecordsScene(_vm, this, which, 0x004B7360); + break; + case 30: + _childObject = new HallOfRecordsScene(_vm, this, which, 0x004B7378); + break; + case 31: + _childObject = new HallOfRecordsScene(_vm, this, which, 0x004B7390); + break; + case 32: + _childObject = new HallOfRecordsScene(_vm, this, which, 0x004B73A8); + break; + case 33: + _childObject = new HallOfRecordsScene(_vm, this, which, 0x004B73C0); + break; + case 34: + _childObject = new HallOfRecordsScene(_vm, this, which, 0x004B73D8); + break; + case 35: + _childObject = new HallOfRecordsScene(_vm, this, which, 0x004B73F0); + break; + case 36: + _childObject = new HallOfRecordsScene(_vm, this, which, 0x004B7408); + break; + case 37: + _childObject = new HallOfRecordsScene(_vm, this, which, 0x004B7420); + break; + case 38: + _childObject = new HallOfRecordsScene(_vm, this, which, 0x004B7438); + break; + case 39: + _childObject = new HallOfRecordsScene(_vm, this, which, 0x004B7450); + break; + case 40: + _childObject = new HallOfRecordsScene(_vm, this, which, 0x004B7468); + break; + case 41: + _childObject = new Scene2242(_vm, this, which); + break; + case 42: + _childObject = new HallOfRecordsScene(_vm, this, which, 0x004B7480); + break; + case 43: + _childObject = new HallOfRecordsScene(_vm, this, which, 0x004B7498); + break; + case 44: + _childObject = new HallOfRecordsScene(_vm, this, which, 0x004B74B0); + break; + case 45: + _childObject = new HallOfRecordsScene(_vm, this, which, 0x004B74C8); + break; + case 46: + _childObject = new Scene2247(_vm, this, which); + break; + case 47: + if (!getGlobalVar(0x98109F12)) { + if (getGlobalVar(0x4D080E54)) + _childObject = new Class152(_vm, this, 0x83110287, 0x10283839); + else + _childObject = new Class152(_vm, this, 0x83412B9D, 0x12B9983C); + } else { + if (getGlobalVar(0x4D080E54)) + _childObject = new Class152(_vm, this, 0x48632087, 0x3208348E); + else + _childObject = new Class152(_vm, this, 0x08C74886, 0x74882084); + } + break; + } + SetUpdateHandler(&Module2200::updateScene); + _childObject->handleUpdate(); +} + +#define HallOfRecordsSceneLink(nextSceneNum, prevSceneNum) \ + if (_moduleResult == 1) createScene(nextSceneNum, 0); else if (_moduleResult == 2) createScene(7, 0); else createScene(prevSceneNum, 1) + +void Module2200::updateScene() { + if (!updateChild()) { + switch (_vm->gameState().sceneNum) { + case 0: + if (_moduleResult == 1) { + createScene(2, 0); + } else if (_moduleResult == 2) { + createScene(1, 0); + } else { + leaveModule(0); + } + break; + case 1: + createScene(0, 2); + break; + case 2: + if (_moduleResult == 1) { + createScene(4, 0); + } else if (_moduleResult == 2) { + createScene(3, 0); + } else { + createScene(0, 1); + } + break; + case 3: + createScene(2, 2); + break; + case 4: + if (_moduleResult == 1) { + createScene(5, 0); + } else if (_moduleResult == 2) { + createScene(4, 2); + } else { + createScene(2, 1); + } + break; + case 5: + if (_moduleResult == 1) { + createScene(46, 0); + } else if (_moduleResult == 2) { + createScene(6, 0); + } else if (_moduleResult == 3) { + createScene(8, 0); + } else { + createScene(4, 1); + } + break; + case 6: + createScene(5, 2); + break; + case 7: + createScene(_vm->gameState().which, 2); + break; + case 8: + createScene(5, 3); + break; + case 9: + HallOfRecordsSceneLink(10, 46); + break; + case 10: + HallOfRecordsSceneLink(11, 9); + break; + case 11: + HallOfRecordsSceneLink(12, 10); + break; + case 12: + HallOfRecordsSceneLink(13, 11); + break; + case 13: + HallOfRecordsSceneLink(14, 12); + break; + case 14: + HallOfRecordsSceneLink(15, 13); + break; + case 15: + HallOfRecordsSceneLink(16, 14); + break; + case 16: + HallOfRecordsSceneLink(17, 15); + break; + case 17: + HallOfRecordsSceneLink(18, 16); + break; + case 18: + HallOfRecordsSceneLink(19, 17); + break; + case 19: + HallOfRecordsSceneLink(20, 18); + break; + case 20: + HallOfRecordsSceneLink(21, 19); + break; + case 21: + HallOfRecordsSceneLink(22, 20); + break; + case 22: + HallOfRecordsSceneLink(23, 21); + break; + case 23: + HallOfRecordsSceneLink(24, 22); + break; + case 24: + HallOfRecordsSceneLink(25, 23); + break; + case 25: + HallOfRecordsSceneLink(26, 24); + break; + case 26: + HallOfRecordsSceneLink(27, 25); + break; + case 27: + HallOfRecordsSceneLink(28, 26); + break; + case 28: + HallOfRecordsSceneLink(29, 27); + break; + case 29: + HallOfRecordsSceneLink(30, 28); + break; + case 30: + HallOfRecordsSceneLink(31, 29); + break; + case 31: + HallOfRecordsSceneLink(32, 30); + break; + case 32: + HallOfRecordsSceneLink(33, 31); + break; + case 33: + HallOfRecordsSceneLink(34, 32); + break; + case 34: + HallOfRecordsSceneLink(42, 33); + break; + case 35: + HallOfRecordsSceneLink(36, 45); + break; + case 36: + HallOfRecordsSceneLink(37, 35); + break; + case 37: + HallOfRecordsSceneLink(38, 36); + break; + case 38: + HallOfRecordsSceneLink(39, 37); + break; + case 39: + HallOfRecordsSceneLink(40, 38); + break; + case 40: + HallOfRecordsSceneLink(41, 39); + break; + case 41: + HallOfRecordsSceneLink(47, 40); + break; + case 42: + HallOfRecordsSceneLink(43, 34); + break; + case 43: + HallOfRecordsSceneLink(44, 42); + break; + case 44: + HallOfRecordsSceneLink(45, 43); + break; + case 45: + HallOfRecordsSceneLink(35, 44); + break; + case 46: + HallOfRecordsSceneLink(9, 5); + break; + case 47: + createScene(41, 1); + break; + } + } +} + +#undef HallOfRecordsSceneLink + +// Scene2201 + +AsScene2201CeilingFan::AsScene2201CeilingFan(NeverhoodEngine *vm) + : AnimatedSprite(vm, 1100) { + + _x = 403; + _y = 259; + createSurface(100, 233, 96); + setFileHash(0x8600866, 0, -1); + SetUpdateHandler(&AnimatedSprite::update); +} + +AsScene2201Door::AsScene2201Door(NeverhoodEngine *vm, Klayman *klayman, Sprite *doorLightSprite, bool flag1) + : AnimatedSprite(vm, 1100), _soundResource(vm), _klayman(klayman), _doorLightSprite(doorLightSprite), + _countdown(0), _doorOpen(flag1) { + + _x = 408; + _y = 290; + createSurface(900, 63, 266); + SetUpdateHandler(&AsScene2201Door::update); + SetMessageHandler(&AsScene2201Door::handleMessage); + if (_doorOpen) { + setFileHash(0xE2CB0412, -1, -1); + _countdown = 48; + _newHashListIndex = -2; + } else { + setFileHash(0xE2CB0412, 0, -1); + _newHashListIndex = 0; + _doorLightSprite->setVisible(false); + } +} + +void AsScene2201Door::update() { + if (_countdown != 0 && _doorOpen && (--_countdown == 0)) { + stCloseDoor(); + } + AnimatedSprite::update(); +} + +uint32 AsScene2201Door::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x11001090) { + if (_doorOpen) + _doorLightSprite->setVisible(true); + } else if (param.asInteger() == 0x11283090) { + if (!_doorOpen) + _doorLightSprite->setVisible(false); + } + break; + case 0x2000: + if (_doorOpen) + _countdown = 144; + messageResult = _doorOpen ? 1 : 0; + break; + case 0x3002: + removeCallbacks(); + break; + case 0x4808: + _countdown = 144; + if (!_doorOpen) + stOpenDoor(); + break; + } + return messageResult; +} + +void AsScene2201Door::stOpenDoor() { + _doorOpen = true; + setFileHash(0xE2CB0412, 0, -1); + _newHashListIndex = -2; + _soundResource.play(calcHash("fxDoorOpen33")); +} + +void AsScene2201Door::stCloseDoor() { + _doorOpen = false; + setFileHash(0xE2CB0412, -1, -1); + _playBackwards = true; + _newHashListIndex = 0; + _soundResource.play(calcHash("fxDoorClose33")); +} + +Class444::Class444(NeverhoodEngine *vm, int pointIndex, int spriteIndex) + : StaticSprite(vm, 900) { + + _spriteResource.load2(kClass444FileHashes[spriteIndex]); + createSurface(100, 16, 16); + _drawRect.x = -(_spriteResource.getDimensions().width / 2); + _drawRect.y = -(_spriteResource.getDimensions().height / 2); + _drawRect.width = _spriteResource.getDimensions().width; + _drawRect.height = _spriteResource.getDimensions().height; + _x = kClass444Points[pointIndex].x; + _y = kClass444Points[pointIndex].y; + _needRefresh = true; +} + +Scene2201::Scene2201(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule, true), _soundFlag(false) { + + Sprite *tempSprite; + + if (!getSubVar(0x40050052, 0x60400854)) { + // TODO _vm->gameModule()->initScene2201Vars(); + } + + _surfaceFlag = true; + SetMessageHandler(&Scene2201::handleMessage); + SetUpdateHandler(&Scene2201::update); + + loadDataResource(0x04104242); + loadHitRectList(); + + setBackground(0x40008208); + setPalette(0x40008208); + insertMouse433(0x0820C408); + + _asTape = insertSprite<AsScene1201Tape>(this, 7, 1100, 459, 432, 0x9148A011); + _vm->_collisionMan->addSprite(_asTape); + + _ssDoorButton = insertSprite<Class426>(this, 0xE4A43E29, 0xE4A43E29, 100, 0); + + for (uint32 i = 0; i < 9; i++) { + if ((int16)getSubVar(0x484498D0, i) >= 0) { + insertSprite<Class444>(i, (int16)getSubVar(0x484498D0, i)); + } + } + + _rect1.y1 = 0; + _rect1.x2 = 640; + _rect2.x2 = 640; + _rect2.y2 = 480; + + if (!getGlobalVar(0x404290D5)) { + insertStaticSprite(0x00026027, 900); + } + + tempSprite = insertStaticSprite(0x030326A0, 1100); + _rect1.x1 = tempSprite->getDrawRect().x; + + insertStaticSprite(0x811DA061, 1100); + + tempSprite = insertStaticSprite(0x11180022, 1100); + _rect2.x1 = tempSprite->getDrawRect().x; + + tempSprite = insertStaticSprite(0x0D411130, 1100); + _rect1.y2 = tempSprite->getDrawRect().y2(); + _rect2.y1 = tempSprite->getDrawRect().y2(); + + _doorLightSprite = insertStaticSprite(0xA4062212, 900); + + if (which < 0) { + insertKlayman<KmScene2201>(300, 427, &_rect1, 2); + setMessageList(0x004B8118); + _asDoor = insertSprite<AsScene2201Door>(_klayman, _doorLightSprite, false); + } else if (which == 1) { + insertKlayman<KmScene2201>(412, 393, &_rect1, 2); + setMessageList(0x004B8130); + _asDoor = insertSprite<AsScene2201Door>(_klayman, _doorLightSprite, false); + } else if (which == 2) { + if (getGlobalVar(0xC0418A02)) { + insertKlayman<KmScene2201>(379, 427, &_rect1, 2); + _klayman->setDoDeltaX(1); + } else { + insertKlayman<KmScene2201>(261, 427, &_rect1, 2); + } + setMessageList(0x004B8178); + _asDoor = insertSprite<AsScene2201Door>(_klayman, _doorLightSprite, false); + } else { + NPoint pt = _dataResource.getPoint(0x0304D8DC); + insertKlayman<KmScene2201>(pt.x, pt.y, &_rect1, 2); + setMessageList(0x004B8120); + _asDoor = insertSprite<AsScene2201Door>(_klayman, _doorLightSprite, true); + } + + insertSprite<AsScene2201CeilingFan>(); + + // TODO Sound1ChList_addSoundResource(0x04106220, 0x81212040, true); + +} + +Scene2201::~Scene2201() { + setGlobalVar(0xC0418A02, _klayman->isDoDeltaX() ? 1 : 0); + // TODO Sound1ChList_sub_407AF0(0x04106220); +} + +void Scene2201::update() { + Scene::update(); + if (!_soundFlag) { + // TODO Sound1ChList_playLooping(0x81212040); + _soundFlag = true; + } +} + +uint32 Scene2201::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x402064D8) { + sendEntityMessage(_klayman, 0x1014, _ssDoorButton); + } else if (param.asInteger() == 0x35803198) { + if (sendMessage(_asDoor, 0x2000, 0)) { + setMessageList(0x004B81A0); + } else { + setMessageList(0x004B81B8); + } + } else if (param.asInteger() == 0x51445010) { + if (getGlobalVar(0x404290D5)) { + setMessageList(0x004B8108); + } else { + setMessageList(0x004B8150); + } + } else if (param.asInteger() == 0x1D203082) { + setMessageList(0x004B8180); + } else if (param.asInteger() == 0x00049091) { + if (getGlobalVar(0x404290D5)) { + setMessageList(0x004B8138); + } else { + setMessageList(0x004B8108); + } + } + break; + case 0x480B: + if (sender == _ssDoorButton) { + sendMessage(_asDoor, 0x4808, 0); + } + break; + case 0x4826: + if (sender == _asTape) { + sendEntityMessage(_klayman, 0x1014, _asTape); + setMessageList(0x004B81C8); + } + break; + } + return 0; +} + +static const NPoint kSsScene2202PuzzleTilePoints[] = { + {196, 105}, + {323, 102}, + {445, 106}, + {192, 216}, + {319, 220}, + {446, 216}, + {188, 320}, + {319, 319}, + {443, 322} +}; + +static const uint32 kSsScene2202PuzzleTileFileHashes1[] = { + 0xA500800C, + 0x2182910C, + 0x2323980C, + 0x23049084, + 0x21008080, + 0x2303900C, + 0x6120980C, + 0x2504D808 +}; + +static const uint32 kSsScene2202PuzzleTileFileHashes2[] = { + 0x0AAD8080, + 0x0A290291, + 0x0A2BA398, + 0x822B8490, + 0x86298080, + 0x0A2B8390, + 0x0A69A098, + 0x0E2D84D8 +}; + +SsScene2202PuzzleTile::SsScene2202PuzzleTile(NeverhoodEngine *vm, Scene *parentScene, int16 tileIndex, int16 value) + : StaticSprite(vm, 900), _soundResource1(vm), _soundResource2(vm), _parentScene(parentScene), + _value(value), _tileIndex(tileIndex), _isMoving(false) { + + SetUpdateHandler(&SsScene2202PuzzleTile::update); + SetMessageHandler(&SsScene2202PuzzleTile::handleMessage); + _spriteResource.load2(kSsScene2202PuzzleTileFileHashes2[_value]); + if (_tileIndex >= 0 && _tileIndex <= 2) { + createSurface(100, 128, 128); + } else if (_tileIndex >= 3 && _tileIndex <= 5) { + createSurface(300, 128, 128); + } else { + createSurface(500, 128, 128); + } + _drawRect.x = -(_spriteResource.getDimensions().width / 2); + _drawRect.y = -(_spriteResource.getDimensions().height / 2); + _drawRect.width = _spriteResource.getDimensions().width; + _drawRect.height = _spriteResource.getDimensions().height; + _deltaRect = _drawRect; + _x = kSsScene2202PuzzleTilePoints[_tileIndex].x; + _y = kSsScene2202PuzzleTilePoints[_tileIndex].y; + processDelta(); + _needRefresh = true; + StaticSprite::update(); + _soundResource1.load(0x40958621); + _soundResource2.load(0x51108241); +} + +void SsScene2202PuzzleTile::update() { + handleSpriteUpdate(); + StaticSprite::update(); +} + +uint32 SsScene2202PuzzleTile::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + if (!_isMoving && !getGlobalVar(0x404290D5)) { + sendMessage(_parentScene, 0x2000, _tileIndex); + } + messageResult = 1; + break; + case 0x2001: + _isMoving = true; + moveTile(param.asInteger()); + break; + } + return messageResult; +} + +void SsScene2202PuzzleTile::suMoveTileX() { + + bool done = false; + + if (_counterDirection) { + if (_counter > 2) + _counter -= 2; + } else { + if (_counter < 20) + _counter += 2; + } + + for (int16 i = 0; i < _counter; i++) { + _x += _xIncr; + _errValue += _yDelta; + if (_errValue >= _xDelta) { + _errValue -= _xDelta; + _y += _yIncr; + } + if (_x == _newX && _y == _newY) { + done = true; + break; + } + if (_x == _xFlagPos) + _counterDirection = true; + } + + if (done) { + stopMoving(); + } + + processDelta(); + +} + +void SsScene2202PuzzleTile::suMoveTileY() { + + bool done = false; + + if (_counterDirection) { + if (_counter > 2) + _counter -= 2; + } else { + if (_counter < 20) + _counter += 2; + } + + for (int16 i = 0; i < _counter; i++) { + _y += _yIncr; + _errValue += _xDelta; + if (_errValue >= _yDelta) { + _errValue -= _yDelta; + _x += _xIncr; + } + if (_x == _newX && _y == _newY) { + done = true; + break; + } + if (_x == _xFlagPos) + _counterDirection = true; + } + + if (done) { + stopMoving(); + } + + processDelta(); + +} + +void SsScene2202PuzzleTile::moveTile(int16 newTileIndex) { + + _spriteResource.load2(kSsScene2202PuzzleTileFileHashes1[_value]); + _drawRect.x = -(_spriteResource.getDimensions().width / 2); + _drawRect.y = -(_spriteResource.getDimensions().height / 2); + _drawRect.width = _spriteResource.getDimensions().width; + _drawRect.height = _spriteResource.getDimensions().height; + _needRefresh = true; + + setSubVar(0x484498D0, _tileIndex, (uint32)-1); + setSubVar(0x484498D0, newTileIndex, (uint32)_value); + + _tileIndex = newTileIndex; + + _errValue = 0; + _counterDirection = false; + _counter = 0; + + _newX = kSsScene2202PuzzleTilePoints[newTileIndex].x; + _newY = kSsScene2202PuzzleTilePoints[newTileIndex].y; + + if (_x == _newX && _y == _newY) + return; + + if (_x <= _newX) { + if (_y <= _newY) { + _xDelta = _newX - _x; + _yDelta = _newY - _y; + _xIncr = 1; + _yIncr = 1; + } else { + _xDelta = _newX - _x; + _yDelta = _y - _newY; + _xIncr = 1; + _yIncr = -1; + } + } else { + if (_y <= _newY) { + _xDelta = _x - _newX; + _yDelta = _newY - _y; + _xIncr = -1; + _yIncr = 1; + } else { + _xDelta = _x - _newX; + _yDelta = _y - _newY; + _xIncr = -1; + _yIncr = -1; + } + } + + if (_xDelta > _yDelta) { + SetSpriteCallback(&SsScene2202PuzzleTile::suMoveTileX); + if (_xIncr > 0) { + if (_newX - _x >= 180) + _xFlagPos = _newX - 90; + else + _xFlagPos = _x + _newX / 2; + } else { + if (_x - _newX >= 180) + _xFlagPos = _x + 90; + else + _xFlagPos = _x / 2 + _newX; + } + _soundResource1.play(); + } else { + SetSpriteCallback(&SsScene2202PuzzleTile::suMoveTileY); + if (_yIncr > 0) { + if (_newY - _y >= 180) + _xFlagPos = _newY - 90; + else + _xFlagPos = _y + _newY / 2; + } else { + if (_y - _newY >= 180) + _xFlagPos = _y + 90; + else + _xFlagPos = _y / 2 + _newY; + } + _soundResource2.play(); + } + +} + +void SsScene2202PuzzleTile::stopMoving() { + _spriteResource.load2(kSsScene2202PuzzleTileFileHashes2[_value]); + _drawRect.x = -(_spriteResource.getDimensions().width / 2); + _drawRect.y = -(_spriteResource.getDimensions().height / 2); + _drawRect.width = _spriteResource.getDimensions().width; + _drawRect.height = _spriteResource.getDimensions().height; + _needRefresh = true; + SetSpriteCallback(NULL); + _isMoving = false; + sendMessage(_parentScene, 0x2002, _tileIndex); +} + +Scene2202::Scene2202(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule, true), _soundResource1(vm), _soundResource2(vm), + _isSolved(false), _leaveScene(false), _isTileMoving(false), _movingTileSprite(NULL), _doneMovingTileSprite(NULL) { + + // TODO initScene2201Vars(); + SetMessageHandler(&Scene2202::handleMessage); + SetUpdateHandler(&Scene2202::update); + + _surfaceFlag = true; + + setBackground(0x08100A0C); + setPalette(0x08100A0C); + addEntity(_palette); + insertMouse435(0x00A08089, 20, 620); + + //DEBUG! + for (uint32 index = 0; index < 9; index++) + setSubVar(0x484498D0, index, 7 - index); + + for (uint32 index = 0; index < 9; index++) { + int16 value = (int16)getSubVar(0x484498D0, index); + if (value >= 0) { + Sprite *puzzleTileSprite = insertSprite<SsScene2202PuzzleTile>(this, index, value); + _vm->_collisionMan->addSprite(puzzleTileSprite); + } + } + + insertStaticSprite(0x55C043B8, 200); + insertStaticSprite(0x85500158, 400); + insertStaticSprite(0x25547028, 600); + + _soundResource1.load(0x68E25540); + _soundResource2.load(0x40400457); + + // TODO Sound1ChList_addSoundResource(0x60400854, 0x8101A241, true); + // TODO Sound1ChList_playLooping(0x8101A241); + +} + +Scene2202::~Scene2202() { + // TODO Sound1ChList_sub_407AF0(0x60400854); +} + +void Scene2202::update() { + Scene::update(); + + if (_leaveScene && !_soundResource2.isPlaying()) { + leaveScene(0); + } + + if (_isSolved && !_soundResource1.isPlaying()) { + _soundResource2.play(); + _isSolved = false; + _leaveScene = true; + } + + if (_movingTileSprite && !_isTileMoving) { + int16 value = getFreeTileIndex(_movingTileIndex); + if (value != -1) { + setSurfacePriority(_movingTileSprite->getSurface(), 700); + sendMessage(_movingTileSprite, 0x2001, value); + _movingTileSprite = NULL; + _isTileMoving = true; + } + } + + if (_doneMovingTileSprite) { + setSurfacePriority(_doneMovingTileSprite->getSurface(), _surfacePriority); + _doneMovingTileSprite = NULL; + if (testIsSolved()) { + _soundResource1.play(); + setGlobalVar(0x404290D5, 1); + _isSolved = true; + } + } + +} + +uint32 Scene2202::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x0001: + // TODO Debug stuff + if (param.asPoint().x <= 20 || param.asPoint().x >= 620) { + leaveScene(0); + } + break; + case 0x000D: + // TODO Debug stuff + break; + case 0x2000: + _movingTileIndex = (int16)param.asInteger(); + _movingTileSprite = (Sprite*)sender; + break; + case 0x2002: + _isTileMoving = false; + _doneMovingTileSprite = (Sprite*)sender; + if (param.asInteger() <= 2) { + _surfacePriority = 100; + } else if (param.asInteger() >= 3 && param.asInteger() <= 5) { + _surfacePriority = 300; + } else { + _surfacePriority = 500; + } + break; + } + return 0; +} + +int16 Scene2202::getFreeTileIndex(int16 index) { + if (index >= 3 && (int16)getSubVar(0x484498D0, index - 3) == -1) { + return index - 3; + } else if (index <= 5 && (int16)getSubVar(0x484498D0, index + 3) == -1) { + return index + 3; + } else if (index != 0 && index != 3 && index != 6 && (int16)getSubVar(0x484498D0, index - 1) == -1) { + return index - 1; + } else if (index != 2 && index != 5 && index != 8 && (int16)getSubVar(0x484498D0, index + 1) == -1) { + return index + 1; + } else + return -1; +} + +bool Scene2202::testIsSolved() { + return + getSubVar(0x484498D0, 0) == 0 && + getSubVar(0x484498D0, 2) == 2 && + getSubVar(0x484498D0, 3) == 3 && + getSubVar(0x484498D0, 4) == 4 && + getSubVar(0x484498D0, 5) == 5 && + getSubVar(0x484498D0, 6) == 6 && + getSubVar(0x484498D0, 8) == 7; +} + +static const uint32 kClass545FileHashes[] = { + 0x2450D850, + 0x0C9CE8D0, + 0x2C58A152 +}; + +Class545::Class545(NeverhoodEngine *vm, Scene *parentScene, int index, int surfacePriority, int16 x, int16 y) + : AnimatedSprite(vm, kClass545FileHashes[index], surfacePriority, x, y), _parentScene(parentScene), _index(index) { + + if (!getSubVar(0x0090EA95, _index) && !getSubVar(0x08D0AB11, _index)) { + SetMessageHandler(&Class545::handleMessage); + } else { + setVisible(false); + SetMessageHandler(NULL); + } +} + +uint32 Class545::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + sendMessage(_parentScene, 0x4826, 0); + messageResult = 1; + break; + case 0x4806: + setSubVar(0x0090EA95, _index, 1); + setVisible(false); + SetMessageHandler(NULL); + } + return messageResult; +} + +static const uint32 kAsScene2203DoorFileHashes[] = { + 0x7868AE10, + 0x1A488110 +}; + +AsScene2203Door::AsScene2203Door(NeverhoodEngine *vm, Scene *parentScene, uint index) + : AnimatedSprite(vm, 1100), _soundResource(vm), _parentScene(parentScene), + _index(index) { + + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene2203Door::handleMessage); + _x = 320; + _y = 240; + createSurface1(kAsScene2203DoorFileHashes[_index], 900); + if (getGlobalVar(0x9A500914) == _index) { + setFileHash(kAsScene2203DoorFileHashes[_index], -1, -1); + _newHashListIndex = -2; + } else { + setFileHash(kAsScene2203DoorFileHashes[_index], 0, -1); + _newHashListIndex = 0; + } +} + +uint32 AsScene2203Door::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + if (_index == getGlobalVar(0x9A500914)) + sendMessage(_parentScene, 0x2002, 0); + else + sendMessage(_parentScene, 0x2001, 0); + messageResult = 1; + break; + case 0x2000: + _otherDoor = (Sprite*)param.asEntity(); + break; + case 0x3002: + if (_index == getGlobalVar(0x9A500914)) + sendMessage(_parentScene, 0x4808, 0); + stopAnimation(); + break; + case 0x4808: + setGlobalVar(0x9A500914, _index); + sendMessage(_otherDoor, 0x4809, 0); + openDoor(); + break; + case 0x4809: + closeDoor(); + sendMessage(_parentScene, 0x2003, 0); + break; + } + return messageResult; +} + +void AsScene2203Door::openDoor() { + _soundResource.play(0x341014C4); + setFileHash(kAsScene2203DoorFileHashes[_index], 1, -1); +} + +void AsScene2203Door::closeDoor() { + setFileHash(kAsScene2203DoorFileHashes[_index], -1, -1); + _playBackwards = true; + _newHashListIndex = 0; +} + +Scene2203::Scene2203(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule, true) { + + if (getGlobalVar(0xC0780812) && !getGlobalVar(0x13382860)) + setGlobalVar(0x13382860, 1); + + SetMessageHandler(&Scene2203::handleMessage); + _surfaceFlag = true; + + setBackground(0x82C80334); + setPalette(0x82C80334); + insertMouse433(0x80330824); + + setHitRects(0x004B8320); + + if (getGlobalVar(0x13382860) == 1) { + _class545 = insertSprite<Class545>(this, 2, 1100, 282, 432); + _vm->_collisionMan->addSprite(_class545); + } + + _asTape = insertSprite<AsScene1201Tape>(this, 1, 1100, 435, 432, 0x9148A011); + _vm->_collisionMan->addSprite(_asTape); + + _asLeftDoor = insertSprite<AsScene2203Door>(this, 0); + _asRightDoor = insertSprite<AsScene2203Door>(this, 1); + + _ssSmallLeftDoor = insertStaticSprite(0x542CC072, 1100); + _ssSmallRightDoor = insertStaticSprite(0x0A2C0432, 1100); + + _leftDoorClipRect.set(_ssSmallLeftDoor->getDrawRect().x, 0, 640, 480); + _rightDoorClipRect.set(0, 0, _ssSmallRightDoor->getDrawRect().x2(), 480); + + sendEntityMessage(_asLeftDoor, 0x2000, _asRightDoor); + sendEntityMessage(_asRightDoor, 0x2000, _asLeftDoor); + + _vm->_collisionMan->addSprite(_asLeftDoor); + _vm->_collisionMan->addSprite(_asRightDoor); + + if (which < 0) { + insertKlayman<KmScene2203>(200, 427); + setMessageList(0x004B8340); + } else if (which == 1) { + insertKlayman<KmScene2203>(640, 427); + setMessageList(0x004B8350); + } else if (which == 2) { + if (getGlobalVar(0xC0418A02)) { + insertKlayman<KmScene2203>(362, 427); + _klayman->setDoDeltaX(1); + } else { + insertKlayman<KmScene2203>(202, 427); + } + setMessageList(0x004B8358); + } else { + insertKlayman<KmScene2203>(0, 427); + setMessageList(0x004B8348); + } + + if (getGlobalVar(0x9A500914)) { + _ssSmallLeftDoor->setVisible(false); + _klayman->setClipRect(_rightDoorClipRect); + } else { + _ssSmallRightDoor->setVisible(false); + _klayman->setClipRect(_leftDoorClipRect); + } + + setRectList(0x004B8420); + +} + +Scene2203::~Scene2203() { + setGlobalVar(0xC0418A02, _klayman->isDoDeltaX() ? 1 : 0); +} + +uint32 Scene2203::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2001: + sendEntityMessage(_klayman, 0x1014, sender); + if (sender == _asLeftDoor) { + setMessageList2(0x004B83B0); + } else { + setMessageList2(0x004B83C8); + } + break; + case 0x2002: + if (sender == _asLeftDoor) { + setMessageList2(0x004B8370); + } else { + setMessageList2(0x004B8360); + } + break; + case 0x2003: + if (sender == _asLeftDoor) { + _ssSmallLeftDoor->setVisible(false); + } else { + _ssSmallRightDoor->setVisible(false); + } + break; + case 0x4808: + if (sender == _asLeftDoor) { + _ssSmallLeftDoor->setVisible(true); + _klayman->setClipRect(_leftDoorClipRect); + } else { + _ssSmallRightDoor->setVisible(true); + _klayman->setClipRect(_rightDoorClipRect); + } + break; + case 0x4826: + if (sender == _asTape) { + sendEntityMessage(_klayman, 0x1014, _asTape); + setMessageList(0x004B83E0); + } else if (sender == _class545) { + sendEntityMessage(_klayman, 0x1014, _class545); + setMessageList(0x004B83F0); + } + break; + } + return messageResult; +} + +SsScene2205DoorFrame::SsScene2205DoorFrame(NeverhoodEngine *vm) + : StaticSprite(vm, 900) { + + SetMessageHandler(&SsScene2205DoorFrame::handleMessage); + _spriteResource.load2(getGlobalVar(0x4D080E54) ? 0x24306227 : 0xD90032A0); + createSurface(1100, 45, 206); + _drawRect.x = 0; + _drawRect.y = 0; + _drawRect.width = _spriteResource.getDimensions().width; + _drawRect.height = _spriteResource.getDimensions().height; + _x = _spriteResource.getPosition().x; + _y = _spriteResource.getPosition().y; + _needRefresh = true; + StaticSprite::update(); +} + +uint32 SsScene2205DoorFrame::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2000: + _spriteResource.load2(getGlobalVar(0x4D080E54) ? 0x24306227 : 0xD90032A0); + _drawRect.x = 0; + _drawRect.y = 0; + _drawRect.width = _spriteResource.getDimensions().width; + _drawRect.height = _spriteResource.getDimensions().height; + _x = _spriteResource.getPosition().x; + _y = _spriteResource.getPosition().y; + _needRefresh = true; + StaticSprite::update(); + } + return messageResult; +} + +Scene2205::Scene2205(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule, true) { + + SetMessageHandler(&Scene2205::handleMessage); + SetUpdateHandler(&Scene2205::update); + + setHitRects(0x004B0620); + _surfaceFlag = true; + + if (getGlobalVar(0x4D080E54)) { + _isLightOn = true; + setBackground(0x0008028D); + setPalette(0x0008028D); + addEntity(_palette); + insertMouse433(0x80289008); + _ssLightSwitch = insertSprite<Class426>(this, 0x2D339030, 0x2D309030, 100, 0); + } else { + _isLightOn = false; + setBackground(0xD00A028D); + setPalette(0xD00A028D); + addEntity(_palette); + insertMouse433(0xA0289D08); + _ssLightSwitch = insertSprite<Class426>(this, 0x2D339030, 0xDAC86E84, 100, 0); + } + + _palette->addBasePalette(0xD00A028D, 0, 256, 0); + + _ssDoorFrame = insertSprite<SsScene2205DoorFrame>(); + + if (which < 0) { + insertKlayman<KmScene2205>(320, 417); + setMessageList(0x004B0658); + if (!getGlobalVar(0x4D080E54)) { + _palette->addPalette(0x68033B1C, 0, 65, 0); + } + _isKlaymanInLight = false; + } else if (which == 1) { + insertKlayman<KmScene2205>(640, 417); + setMessageList(0x004B0648); + if (!getGlobalVar(0x4D080E54)) { + _palette->addPalette(0x68033B1C, 0, 65, 0); + } + _isKlaymanInLight = false; + } else { + insertKlayman<KmScene2205>(0, 417); + setMessageList(0x004B0640); + _isKlaymanInLight = true; + } + + _klayman->setClipRect(_ssDoorFrame->getDrawRect().x, 0, 640, 480); + + loadDataResource(0x00144822); + _klayman->setSoundFlag(true); + +} + +void Scene2205::update() { + Scene::update(); + + if (!_isLightOn && getGlobalVar(0x4D080E54)) { + _palette->addPalette(0x0008028D, 0, 256, 0); + changeBackground(0x0008028D); + _ssLightSwitch->setFileHashes(0x2D339030, 0x2D309030); + sendMessage(_ssDoorFrame, 0x2000, 0); + changeMouseCursor(0x80289008); + _isLightOn = true; + } else if (_isLightOn && !getGlobalVar(0x4D080E54)) { + _palette->addPalette(0xD00A028D, 0, 256, 0); + changeBackground(0xD00A028D); + _ssLightSwitch->setFileHashes(0x2D339030, 0xDAC86E84); + sendMessage(_ssDoorFrame, 0x2000, 0); + changeMouseCursor(0xA0289D08); + _isKlaymanInLight = true; + if (_klayman->getX() > 85) { + _palette->addPalette(0x68033B1C, 0, 65, 0); + _isKlaymanInLight = false; + } + _isLightOn = false; + } + + if (!getGlobalVar(0x4D080E54)) { + if (_isKlaymanInLight && _klayman->getX() > 85) { + _palette->addBasePalette(0x68033B1C, 0, 65, 0); + _palette->startFadeToPalette(12); + _isKlaymanInLight = false; + } else if (!_isKlaymanInLight && _klayman->getX() <= 85) { + _palette->addBasePalette(0xD00A028D, 0, 65, 0); + _palette->startFadeToPalette(12); + _isKlaymanInLight = true; + } + } + +} + +uint32 Scene2205::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x6449569A) { + setMessageList(0x004B0690); + } else if (param.asInteger() == 0x2841369C) { + setMessageList(0x004B0630); + } else if (param.asInteger() == 0x402064D8) { + sendEntityMessage(_klayman, 0x1014, _ssLightSwitch); + } + break; + case 0x480B: + setGlobalVar(0x4D080E54, getGlobalVar(0x4D080E54) ? 0 : 1); + break; + } + return 0; +} + +static const int16 kScene2206XPositions[] = { + 384, + 480, + 572 +}; + +static const uint32 kScene2206MessageIds1[] = { + 0x004B8998, + 0x004B89B8, + 0x004B89D8 +}; + +static const uint32 kScene2206MessageIds2[] = { + 0x004B89F8, + 0x004B8A20, + 0x004B8A48 +}; + +static const int16 kClass603XDeltas1[] = { + -24, -28, -18, 6, 9, -8 +}; + +static const int16 kClass603XDeltas2[] = { + -8, 7, 11, 26, 13, 14 +}; + +Class603::Class603(NeverhoodEngine *vm, uint32 fileHash) + : StaticSprite(vm, fileHash, 200), _soundResource(vm) { + + if (getGlobalVar(0x18890C91)) + _x -= 63; + SetUpdateHandler(&Class603::update); + SetMessageHandler(&Class603::handleMessage); + SetSpriteCallback(NULL); +} + +void Class603::update() { + handleSpriteUpdate(); + StaticSprite::update(); +} + +uint32 Class603::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x4808: + _index = 0; + SetMessageHandler(NULL); + SetSpriteCallback(&Class603::spriteUpdate481E60); + _soundResource.play(0x032746E0); + break; + case 0x4809: + _index = 0; + SetMessageHandler(NULL); + SetSpriteCallback(&Class603::spriteUpdate481E90); + _soundResource.play(0x002642C0); + break; + } + return messageResult; +} + +void Class603::spriteUpdate481E60() { + if (_index < 6) { + _x += kClass603XDeltas1[_index]; + _index++; + } else { + SetMessageHandler(&Class603::handleMessage); + SetSpriteCallback(NULL); + } +} + +void Class603::spriteUpdate481E90() { + if (_index < 6) { + _x += kClass603XDeltas2[_index]; + _index++; + } else { + SetMessageHandler(&Class603::handleMessage); + SetSpriteCallback(NULL); + } +} + +Class604::Class604(NeverhoodEngine *vm, uint32 fileHash) + : StaticSprite(vm, fileHash, 50) { + + SetUpdateHandler(&Class604::update); + SetMessageHandler(&Class604::handleMessage); + SetSpriteCallback(NULL); +} + +void Class604::update() { + handleSpriteUpdate(); + StaticSprite::update(); +} + +uint32 Class604::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x4803: + SetMessageHandler(NULL); + SetSpriteCallback(&Class604::spriteUpdate482020); + _yDelta = 0; + break; + } + return messageResult; +} + +void Class604::spriteUpdate482020() { + _yDelta++; + _y += _yDelta; +} + +Class607::Class607(NeverhoodEngine *vm, Scene *parentScene, int surfacePriority, uint32 fileHash) + : StaticSprite(vm, fileHash, surfacePriority), _parentScene(parentScene) { + + if (getGlobalVar(0x45080C38)) { + setVisible(false); + SetMessageHandler(NULL); + } else { + SetMessageHandler(&Class607::handleMessage); + } + _deltaRect = _drawRect; + processDelta(); +} + +uint32 Class607::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + sendMessage(_parentScene, 0x4826, 0); + messageResult = 1; + break; + case 0x4806: + setGlobalVar(0x45080C38, 1); + setVisible(false); + SetMessageHandler(NULL); + break; + } + return messageResult; +} + +Scene2206::Scene2206(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule, true), _soundResource(vm) { + + uint32 fileHash; + + SetUpdateHandler(&Scene::update); + SetMessageHandler(&Scene2206::handleMessage); + _surfaceFlag = true; + + if (getGlobalVar(0x4D080E54)) { + fileHash = 0x41983216; + _sprite1 = insertStaticSprite(0x2201266A, 100); + _sprite2 = insertStaticSprite(0x3406A333, 300); + _sprite3 = insertStaticSprite(0x24A223A2, 100); + _sprite4 = insertSprite<Class603>(0x26133023); + _sprite4->setClipRect(_sprite2->getDrawRect().x, 0, 640, 480); + setRectList(0x004B8AF8); + _sprite5 = insertSprite<SsCommonButtonSprite>(this, 0x0E038022, 100, 0); + insertMouse433(0x83212411); + _class607 = insertSprite<Class607>(this, 1100, /*464, 433, */0x5E00E262); + _class604 = insertSprite<Class604>(0x085E25E0); + } else { + fileHash = 0xE0102A45; + _sprite1 = insertStaticSprite(0x1C1106B8, 100); + _sprite2 = insertStaticSprite(0x020462E0, 300); + _sprite3 = insertStaticSprite(0x900626A2, 100); + _sprite4 = insertSprite<Class603>(0x544822A8); + _sprite4->setClipRect(_sprite2->getDrawRect().x, 0, 640, 480); + setRectList(0x004B8B58); + _sprite5 = insertSprite<SsCommonButtonSprite>(this, 0x16882608, 100, 0); + insertMouse433(0x02A41E09); + _class607 = insertSprite<Class607>(this, 1100, /*464, 433, */0x52032563); + _class604 = insertSprite<Class604>(0x317831A0); + } + + _class604->setClipRect(_sprite2->getDrawRect().x, 0, _sprite3->getDrawRect().x2(), _sprite1->getDrawRect().y2()); + + setBackground(fileHash); + + setPalette(fileHash); + addEntity(_palette); + + _palette->addBasePalette(fileHash, 0, 256, 0); + + if (!getGlobalVar(0x4D080E54)) { + _palette->addPalette(0x0263D144, 0, 65, 0); + } + + _vm->_collisionMan->addSprite(_class607); + + if (which < 0) { + insertKlayman<KmScene2206>(200, 430); + setMessageList(0x004B88A8); + } else if (which == 1) { + insertKlayman<KmScene2206>(640, 430); + setMessageList(0x004B88B8); + } else if (which == 2) { + insertKlayman<KmScene2206>(205, 396); + setMessageList(0x004B88C8); + _palette->addPalette(getGlobalVar(0x4D080E54) ? 0xB103B604 : 0x0263D144, 0, 65, 0); + sub4819D0(); + _soundResource.play(0x53B8284A); + } else if (which == 3) { + insertKlayman<KmScene2206>(kScene2206XPositions[getGlobalVar(0x48A68852)], 430); + if (getGlobalVar(0xC0418A02)) + _klayman->setDoDeltaX(1); + setMessageList(0x004B8A70); + } else { + insertKlayman<KmScene2206>(0, 430); + setMessageList(0x004B88B0); + } + + _klayman->setSoundFlag(true); + _klayman->setKlaymanTable2(); + +} + +Scene2206::~Scene2206() { + setGlobalVar(0xC0418A02, _klayman->isDoDeltaX() ? 1 : 0); +} + +uint32 Scene2206::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x800C6694) { + sub481B00(); + } else if (param.asInteger() == 0x402064D8) { + sendEntityMessage(_klayman, 0x1014, _sprite5); + } else if (param.asInteger() == 0x11C40840) { + if (getGlobalVar(0x18890C91)) + setMessageList(0x004B8948); + else + setMessageList(0x004B8970); + } + break; + case 0x4803: + sendMessage(_class604, 0x4803, 0); + break; + case 0x480B: + if (sender == _sprite5) { + setGlobalVar(0x18890C91, getGlobalVar(0x18890C91) ? 0 : 1); + if (getGlobalVar(0x18890C91)) + sendMessage(_sprite4, 0x4808, 0); + else + sendMessage(_sprite4, 0x4809, 0); + } + break; + case 0x4826: + sendEntityMessage(_klayman, 0x1014, _class607); + setMessageList(0x004B8988); + break; + case 0x482A: + sub4819D0(); + break; + case 0x482B: + sub481950(); + break; + } + return messageResult; +} + +void Scene2206::sub481950() { + if (getGlobalVar(0x4D080E54)) { + _palette->addBasePalette(0x41983216, 0, 65, 0); + _palette->startFadeToPalette(12); + } + setSurfacePriority(_sprite1->getSurface(), 100); + setSurfacePriority(_sprite2->getSurface(), 300); + setSurfacePriority(_sprite3->getSurface(), 100); + setSurfacePriority(_sprite4->getSurface(), 200); + _klayman->setClipRect(0, 0, 640, 480); +} + +void Scene2206::sub4819D0() { + if (!getGlobalVar(0x4D080E54)) { + _palette->addBasePalette(0xB103B604, 0, 65, 0); + _palette->startFadeToPalette(12); + } + setSurfacePriority(_sprite1->getSurface(), 1100); + setSurfacePriority(_sprite2->getSurface(), 1300); + setSurfacePriority(_sprite3->getSurface(), 1100); + setSurfacePriority(_sprite4->getSurface(), 1200); + _klayman->setClipRect(_sprite2->getDrawRect().x, 0, _sprite3->getDrawRect().x2(), _sprite1->getDrawRect().y2()); +} + +void Scene2206::sub481B00() { + setGlobalVar(0x48A68852, (_mouseClickPos.x - 354) / 96); + if (getGlobalVar(0x48A68852) > 2) + setGlobalVar(0x48A68852, 2); + setGlobalVar(0x49C40058, (_mouseClickPos.y - 183) / 7); + setGlobalVar(0xC8C28808, calcHash("stLineagex")); + setGlobalVar(0x4CE79018, 0); + if (ABS(kScene2206XPositions[getGlobalVar(0x48A68852)] - _klayman->getX()) >= 144) { + setMessageList2(kScene2206MessageIds1[getGlobalVar(0x48A68852)]); + } else { + setMessageList2(kScene2206MessageIds2[getGlobalVar(0x48A68852)]); + } +} + +static const uint32 kScene2207FileHashes[] = { + 0x33B1E12E, + 0x33D1E12E, + 0x3311E12E, + 0x3291E12E, + 0x3191E12E, + 0x3791E12E, + 0x3B91E12E, + 0x2391E12E, + 0x1391E12E, + 0x3BB1E12E, + 0x23B1E12E, + 0x13B1E12E +}; + +AsScene2207Elevator::AsScene2207Elevator(NeverhoodEngine *vm, Scene *parentScene) + : AnimatedSprite(vm, 900), _parentScene(parentScene), _soundResource(vm), + _pointIndex(0), _destPointIndex(0), _destPointIndexDelta(0) { + + NPoint pt; + + _dataResource.load(0x00524846); + _pointArray = _dataResource.getPointArray(0x005B02B7); + pt = _dataResource.getPoint(0x403A82B1); + _x = pt.x; + _y = pt.y; + createSurface(1100, 129, 103); + setFileHash(getGlobalVar(0x4D080E54) ? 0xC858CC19 : 0x294B3377, 0, 0); + SetUpdateHandler(&AsScene2207Elevator::update); + SetSpriteCallback(&AsScene2207Elevator::suSetPosition); + SetMessageHandler(&AsScene2207Elevator::handleMessage); + _newHashListIndex = 0; +} + +AsScene2207Elevator::~AsScene2207Elevator() { + // TODO Sound1ChList_sub_407AF0(0x02700413); +} + +void AsScene2207Elevator::update() { + + if (_destPointIndex + _destPointIndexDelta > _pointIndex) { + _pointIndex++; + setFileHash(getGlobalVar(0x4D080E54) ? 0xC858CC19 : 0x294B3377, _pointIndex, _pointIndex); + _newHashListIndex = _pointIndex; + if (_destPointIndex + _destPointIndexDelta == _pointIndex) { + if (_destPointIndexDelta != 0) { + _destPointIndexDelta = 0; + } else { + // TODO Sound1ChList_deleteSoundByHash(0xD3B02847); + _soundResource.play(0x53B8284A); + } + } + } + + if (_destPointIndex + _destPointIndexDelta < _pointIndex) { + _pointIndex--; + if (_pointIndex == 0) + sendMessage(_parentScene, 0x2003, 0); + setFileHash(getGlobalVar(0x4D080E54) ? 0xC858CC19 : 0x294B3377, _pointIndex, _pointIndex); + _newHashListIndex = _pointIndex; + if (_destPointIndex + _destPointIndexDelta == _pointIndex) { + if (_destPointIndexDelta != 0) { + _destPointIndexDelta = 0; + } else { + // TODO Sound1ChList_deleteSoundByHash(0xD3B02847); + _soundResource.play(0x53B8284A); + } + } + } + + if (_pointIndex > 20 && _surface->getPriority() != 900) { + sendMessage(_parentScene, 0x2002, 900); + } else if (_pointIndex < 20 && _surface->getPriority() != 1100) { + sendMessage(_parentScene, 0x2002, 1100); + } + + AnimatedSprite::update(); + + if (_destPointIndex + _destPointIndexDelta == _pointIndex && _isMoving) { + sendMessage(_parentScene, 0x2004, 0); + _isMoving = false; + } + +} + +void AsScene2207Elevator::suSetPosition() { + _x = (*_pointArray)[_pointIndex].x; + _y = (*_pointArray)[_pointIndex].y - 60; + processDelta(); +} + +uint32 AsScene2207Elevator::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2000: + moveToY(param.asInteger()); + break; + } + return messageResult; +} + +void AsScene2207Elevator::moveToY(int16 y) { + int16 minDistance = 480; + + if (!_pointArray || _pointArray->size() == 0) + return; + + for (uint i = 0; i < _pointArray->size(); i++) { + int16 distance = ABS(y - (*_pointArray)[i].y); + if (distance < minDistance) { + minDistance = distance; + _destPointIndex = i; + } + } + + if (_destPointIndex != _pointIndex) { + if (_destPointIndex == 0 || _destPointIndex == (int)_pointArray->size() - 1) { + _destPointIndexDelta = 0; + } else if (_destPointIndex < _pointIndex) { + _destPointIndexDelta = -2; + } else { + _destPointIndexDelta = 2; + } + // TODO Sound1ChList_addSoundResource(0x02700413, 0xD3B02847, true); + // TODO Sound1ChList_playLooping(0xD3B02847); + } + + _isMoving = true; + +} + +AsScene2207Lever::AsScene2207Lever(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y, int doDeltaX) + : AnimatedSprite(vm, 1100), _soundResource(vm), _parentScene(parentScene) { + + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene2207Lever::handleMessage); + createSurface(1010, 71, 73); + setDoDeltaX(doDeltaX); + setFileHash(0x80880090, 0, -1); + _newHashListIndex = 0; + _x = x; + _y = y; +} + +uint32 AsScene2207Lever::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + sendMessage(_parentScene, 0x4826, 0); + messageResult = 1; + break; + case 0x3002: + removeCallbacks(); + stopAnimation(); + break; + case 0x4807: + stLeverUp(); + break; + case 0x480F: + stLeverDown(); + break; + case 0x482A: + sendMessage(_parentScene, 0x1022, 990); + break; + case 0x482B: + sendMessage(_parentScene, 0x1022, 1010); + break; + } + return messageResult; +} + +void AsScene2207Lever::stLeverDown() { + setFileHash(0x80880090, 1, -1); + FinalizeState(&AsScene2207Lever::stLeverDownEvent); + _soundResource.play(0x40581882); +} + +void AsScene2207Lever::stLeverDownEvent() { + sendMessage(_parentScene, 0x480F, 0); +} + +void AsScene2207Lever::stLeverUp() { + setFileHash(0x80880090, 6, -1); + FinalizeState(&AsScene2207Lever::stLeverUpEvent); + _playBackwards = true; + _soundResource.play(0x40581882); +} + +void AsScene2207Lever::stLeverUpEvent() { + sendMessage(_parentScene, 0x4807, 0); +} + +AsScene2207WallRobotAnimation::AsScene2207WallRobotAnimation(NeverhoodEngine *vm, Scene *parentScene) + : AnimatedSprite(vm, 1200), _soundResource1(vm), _soundResource2(vm), + _soundResource3(vm), _soundResource4(vm), _idle(true) { + + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene2207WallRobotAnimation::handleMessage); + createSurface1(0xCCFD6090, 100); + _x = 309; + _y = 320; + setFileHash(0xCCFD6090, 0, -1); + _newHashListIndex = 0; + _soundResource2.load(0x40330872); + _soundResource3.load(0x72A2914A); + _soundResource4.load(0xD4226080); +} + +AsScene2207WallRobotAnimation::~AsScene2207WallRobotAnimation() { + // TODO Sound1ChList_sub_407AF0(0x80D00820); +} + +uint32 AsScene2207WallRobotAnimation::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (!_idle) { + if (param.asInteger() == 0x3423093) { + // TODO Sound1ChList_addSoundResource(0x80D00820, 0x12121943, true); + // TODO Sound1ChList_playLooping(0x12121943); + } else if (param.asInteger() == 0x834AB011) { + _soundResource1.stop(); + _soundResource2.stop(); + _soundResource3.stop(); + _soundResource4.stop(); + // TODO Sound1ChList_deleteSoundByHash(0x12121943); + } else if (param.asInteger() == 0x3A980501) { + _soundResource2.play(); + } else if (param.asInteger() == 0x2A2AD498) { + _soundResource3.play(); + } else if (param.asInteger() == 0xC4980008) { + _soundResource4.play(); + } else if (param.asInteger() == 0x06B84228) { + _soundResource1.play(0xE0702146); + } + } + break; + case 0x2006: + stStartAnimation(); + break; + case 0x2007: + stStopAnimation(); + break; + case 0x3002: + removeCallbacks(); + break; + } + return messageResult; +} + +void AsScene2207WallRobotAnimation::stStartAnimation() { + if (!_idle) { + NextState(NULL); + } else { + setFileHash(0xCCFD6090, 0, -1); + _idle = false; + setVisible(true); + } +} + +void AsScene2207WallRobotAnimation::stStopAnimation() { + NextState(&AsScene2207WallRobotAnimation::cbStopAnimation); +} + +void AsScene2207WallRobotAnimation::cbStopAnimation() { + stopAnimation(); + _soundResource1.stop(); + _soundResource2.stop(); + _soundResource3.stop(); + _soundResource4.stop(); + // TODO Sound1ChList_deleteSoundByHash(0x12121943); + _idle = true; + setVisible(false); +} + +AsScene2207WallCannonAnimation::AsScene2207WallCannonAnimation(NeverhoodEngine *vm) + : AnimatedSprite(vm, 1200), _idle(true) { + + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene2207WallCannonAnimation::handleMessage); + createSurface1(0x8CAA0099, 100); + _x = 309; + _y = 320; + setFileHash(0x8CAA0099, 0, -1); + _newHashListIndex = 0; +} + +uint32 AsScene2207WallCannonAnimation::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2006: + stStartAnimation(); + break; + case 0x2007: + stStopAnimation(); + break; + case 0x3002: + removeCallbacks(); + break; + } + return messageResult; +} + +void AsScene2207WallCannonAnimation::stStartAnimation() { + if (!_idle) { + NextState(NULL); + } else { + setVisible(true); + setFileHash(0x8CAA0099, 0, -1); + _idle = false; + } +} + +void AsScene2207WallCannonAnimation::stStopAnimation() { + NextState(&AsScene2207WallCannonAnimation::cbStopAnimation); +} + +void AsScene2207WallCannonAnimation::cbStopAnimation() { + stopAnimation(); + setVisible(false); + _idle = true; +} + +SsScene2207Symbol::SsScene2207Symbol(NeverhoodEngine *vm, uint32 fileHash, int index) + : StaticSprite(vm, fileHash, 100) { + + _x = 330; + _y = 246 + index * 50; + StaticSprite::update(); +} + +Scene2207::Scene2207(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule, true), _soundResource1(vm), _soundResource2(vm), + _klaymanAtElevator(true), _elevatorSurfacePriority(0) { + + //DEBUG + setGlobalVar(0x4D080E54, 1); + + _vm->gameModule()->initScene3009Vars(); + + if (!getSubVar(0x40050052, 0x88460852)) + setSubVar(0x40050052, 0x88460852, 1); + + SetMessageHandler(&Scene2207::handleMessage); + SetUpdateHandler(&Scene2207::update); + _surfaceFlag = true; + + insertKlayman<KmScene2207>(0, 0); + _klayman->setRepl(64, 0); + + setMessageList(0x004B38E8); + + _asElevator = insertSprite<AsScene2207Elevator>(this); + + if (getGlobalVar(0x4D080E54)) { + + setBackground(0x88C00241); + setPalette(0x88C00241); + insertMouse433(0x00245884); + + _ssMaskPart1 = insertStaticSprite(0xE20A28A0, 1200); + _ssMaskPart2 = insertStaticSprite(0x688F62A5, 1100); + _ssMaskPart3 = insertStaticSprite(0x0043B038, 1100); + + _asTape = insertSprite<AsScene1201Tape>(this, 4, 1100, 277, 428, 0x9148A011); + _vm->_collisionMan->addSprite(_asTape); + + _asLever = insertSprite<AsScene2207Lever>(this, 527, 333, 0); + _vm->_collisionMan->addSprite(_asLever); + + _asWallRobotAnimation = insertSprite<AsScene2207WallRobotAnimation>(this); + _asWallCannonAnimation = insertSprite<AsScene2207WallCannonAnimation>(); + + _asWallRobotAnimation->setVisible(false); + _asWallCannonAnimation->setVisible(false); + + _ssButton = insertSprite<SsCommonButtonSprite>(this, 0x2C4061C4, 100, 0); + + _asLever->setClipRect(0, 0, _ssMaskPart3->getDrawRect().x2(), 480); + _klayman->setClipRect(0, _ssMaskPart1->getDrawRect().y, 640, _ssMaskPart2->getDrawRect().y2()); + _asElevator->setClipRect(0, _ssMaskPart1->getDrawRect().y, 640, _ssMaskPart2->getDrawRect().y2()); + + } else { + + setGlobalVar(0x81890D14, 1); + + setBackground(0x05C02A55); + setPalette(0x05C02A55); + insertMouse433(0x02A51054); + + _ssMaskPart1 = insertStaticSprite(0x980E46A4, 1200); + + insertSprite<SsScene2207Symbol>(kScene2207FileHashes[getSubVar(0x00504B86, 0)], 0); + insertSprite<SsScene2207Symbol>(kScene2207FileHashes[getSubVar(0x00504B86, 1)], 1); + insertSprite<SsScene2207Symbol>(kScene2207FileHashes[getSubVar(0x00504B86, 2)], 2); + + _asTape = NULL; + _asLever = NULL; + _asWallRobotAnimation = NULL; + _asWallCannonAnimation = NULL; + _ssButton = NULL; + + _klayman->setClipRect(0, _ssMaskPart1->getDrawRect().y, 640, 480); + _asElevator->setClipRect(0, _ssMaskPart1->getDrawRect().y, 640, 480); + + } + + _dataResource.load(0x00524846); + + setRectList(0x004B38B8); + + sendEntityMessage(_klayman, 0x1014, _asElevator); + sendMessage(_klayman, 0x2001, 0); + sendMessage(_asElevator, 0x2000, 480); + + _soundResource2.load(calcHash("fxFogHornSoft")); + +} + +void Scene2207::update() { + Scene::update(); + if (_elevatorSurfacePriority != 0) { + setSurfacePriority(_asElevator->getSurface(), _elevatorSurfacePriority); + _elevatorSurfacePriority = 0; + } + if (_klayman->getY() == 423) { + _klaymanAtElevator = _klayman->getX() > 459 && _klayman->getX() < 525; + } +} + +uint32 Scene2207::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x0014F275) { + if (_klaymanAtElevator) { + sendMessage(_asElevator, 0x2000, _mouseClickPos.y); + sendEntityMessage(_klayman, 0x1014, _asElevator); + sendMessage(_klayman, 0x2001, 0); + } else { + messageList402220(); + } + } else if (param.asInteger() == 0x34569073) { + if (_klaymanAtElevator) { + _messageListFlag1 = true; + sendMessage(_asElevator, 0x2000, 0); + sendEntityMessage(_klayman, 0x1014, _asElevator); + sendMessage(_klayman, 0x2001, 0); + } else { + messageList402220(); + } + } else if (param.asInteger() == 0x4054C877) { + if (_klaymanAtElevator) { + sendMessage(_asElevator, 0x2000, 480); + sendEntityMessage(_klayman, 0x1014, _asElevator); + sendMessage(_klayman, 0x2001, 0); + } else { + messageList402220(); + } + } else if (param.asInteger() == 0x0CBC6211) { + sendEntityMessage(_klayman, 0x1014, _asElevator); + sendMessage(_klayman, 0x2001, 0); + setRectList(0x004B38B8); + } else if (param.asInteger() == 0x402064D8) { + sendEntityMessage(_klayman, 0x1014, _ssButton); + } else if (param.asInteger() == 0x231DA241) { + if (_ssButton) { + setMessageList(0x004B38F0); + } else { + setMessageList(0x004B37D8); + } + } + break; + case 0x2002: + _elevatorSurfacePriority = param.asInteger(); + break; + case 0x2003: + _messageListFlag1 = false; + break; + case 0x4807: + sendMessage(_asWallRobotAnimation, 0x2007, 0); + sendMessage(_asWallCannonAnimation, 0x2007, 0); + break; + case 0x480B: + if (sender == _ssButton) { + if (getSubVar(0x14800353, 0x40119852)) { + setSubVar(0x14800353, 0x40119852, 0); + _soundResource1.play(calcHash("fx3LocksDisable")); + } else { + setSubVar(0x14800353, 0x40119852, 1); + _soundResource2.play(); + } + } + break; + case 0x480F: + sendMessage(_asWallRobotAnimation, 0x2006, 0); + sendMessage(_asWallCannonAnimation, 0x2006, 0); + _asWallRobotAnimation->setVisible(true); + _asWallCannonAnimation->setVisible(true); + break; + case 0x4826: + if (sender == _asTape) { + if (_klayman->getY() == 423) { + sendEntityMessage(_klayman, 0x1014, _asTape); + setMessageList(0x004B3958); + } + } else if (_klaymanAtElevator) { + SetMessageHandler(&Scene2207::handleMessage2); + sendMessage(_asElevator, 0x2000, 347); + sendEntityMessage(_klayman, 0x1014, _asElevator); + sendMessage(_klayman, 0x2001, 0); + } + break; + } + return messageResult; +} + +uint32 Scene2207::handleMessage2(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2002: + _elevatorSurfacePriority = param.asInteger(); + break; + case 0x2004: + SetMessageHandler(&Scene2207::handleMessage); + sendMessage(_klayman, 0x2005, 0); + sendEntityMessage(_klayman, 0x1014, _asLever); + setMessageList(0x004B3920); + setRectList(0x004B3948); + break; + } + return messageResult; +} + +static const uint32 kScene2208FileHashes1[] = { + 0x041023CB, + 0x041020CB, + 0x041026CB, + 0x04102ACB, + 0x041032CB, + 0x041002CB +}; + +static const uint32 kScene2208FileHashes2[] = { + 0x091206C9, + 0x091406C9, + 0x091806C9, + 0x090006C9, + 0x093006C9, + 0x095006C9 +}; + +Scene2208::Scene2208(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule, true), _textResource(vm) { + + SpriteResource spriteResource(_vm); + const char *textStart, *textEnd; + + if (!getGlobalVar(0xC8C28808)) + setGlobalVar(0xC8C28808, calcHash("stLineagex")); + + _textResource.load(getGlobalVar(0xC8C28808)); + + textStart = _textResource.getString(getGlobalVar(0x48A68852), textEnd); + while (textStart < textEnd) { + _strings.push_back(textStart); + textStart += strlen(textStart) + 1; + } + + _maxRowIndex = 8 + 10 * (3 - (getGlobalVar(0xC8C28808) == calcHash("stLineagex") ? 1 : 0)); + + _background = new Background(_vm, 0); + _background->createSurface(0, 640, 528); + _background->getSpriteResource().getPosition().y = 480; + addBackground(_background); + + setPalette(0x08100289); + addEntity(_palette); // Why? + + insertMouse435(0x0028D089, 40, 600); + + createFontSurface(); + + _backgroundSurface = new BaseSurface(_vm, 0, 640, 480); + spriteResource.load2(0x08100289); + _backgroundSurface->drawSpriteResourceEx(spriteResource, false, false, 0, 0); + + _topBackgroundSurface = new BaseSurface(_vm, 0, 640, 192); + spriteResource.load2(!getGlobalVar(0x4CE79018) ? kScene2208FileHashes1[getGlobalVar(0x48A68852) % 6] : getGlobalVar(0x4CE79018)); + _topBackgroundSurface->drawSpriteResourceEx(spriteResource, false, false, 0, 0); + + _bottomBackgroundSurface = new BaseSurface(_vm, 0, 640, 192); + spriteResource.load2(kScene2208FileHashes2[getGlobalVar(0x48A68852) % 6]); + _bottomBackgroundSurface->drawSpriteResourceEx(spriteResource, false, false, 0, 0); + + SetUpdateHandler(&Scene2208::update); + SetMessageHandler(&Scene2208::handleMessage); + + _visibleRowsCount = 10; + + _newRowIndex = (int16)getGlobalVar(0x49C40058); + if (_newRowIndex + _visibleRowsCount > _maxRowIndex) + _newRowIndex = _maxRowIndex - _visibleRowsCount; + if (_newRowIndex < 6) + _newRowIndex = 0; + + _rowScrollY = 0; + + _backgroundScrollY = 48 * _newRowIndex; + + _currRowIndex = _newRowIndex; + + for (int16 rowIndex = 0; rowIndex < _visibleRowsCount; rowIndex++) + drawRow(_newRowIndex + rowIndex); + + _background->getSurface()->getSysRect().y = _backgroundScrollY; + + // TODO Screen.yOffset = _backgroundScrollY; + // TODO Scene2208_sub409080 (creates background Sprites via the text, doesn't seem to be used?) + +} + +Scene2208::~Scene2208() { + delete _fontSurface; + delete _backgroundSurface; + delete _topBackgroundSurface; + delete _bottomBackgroundSurface; +} + +void Scene2208::update() { + + int16 mouseY = _vm->getMouseY(); + + if (mouseY < 48) { + if (_currRowIndex > 0) + _newRowIndex = _currRowIndex - 1; + } else if (mouseY > 432) { + if (_currRowIndex < _maxRowIndex - _visibleRowsCount) + _newRowIndex = _currRowIndex + 1; + } else { + if (_currRowIndex > _newRowIndex) + _newRowIndex = _currRowIndex; + } + + if (_currRowIndex < _newRowIndex) { + if (_rowScrollY == 0) { + drawRow(_currRowIndex + _visibleRowsCount); + } + _backgroundScrollY += 4; + _rowScrollY += 4; + if (_rowScrollY == 48) { + _rowScrollY = 0; + _currRowIndex++; + } + _background->getSurface()->getSysRect().y = _backgroundScrollY; + } else if (_currRowIndex > _newRowIndex || _rowScrollY > 0) { + if (_rowScrollY == 0) { + drawRow(_currRowIndex - 1); + _currRowIndex--; + } + _backgroundScrollY -= 4; + if (_rowScrollY == 0) + _rowScrollY = 48; + _rowScrollY -= 4; + _background->getSurface()->getSysRect().y = _backgroundScrollY; + } + + // TODO Screen.yOffset = _backgroundScrollY; + Scene::update(); + +} + +uint32 Scene2208::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x0001: + if (param.asPoint().x <= 40 || param.asPoint().x >= 600) { + leaveScene(0); + } + break; + } + return messageResult; +} + +void Scene2208::createFontSurface() { + DataResource fontData(_vm); + SpriteResource spriteResource(_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")); + spriteResource.load2(0x0800090C); + _fontSurface = new FontSurface(_vm, tracking, numRows, firstChar, charWidth, charHeight); + _fontSurface->drawSpriteResourceEx(spriteResource, false, false, 0, 0); +} + +void Scene2208::drawRow(int16 rowIndex) { + NDrawRect sourceRect; + int16 y = (rowIndex * 48) % 528; + if (rowIndex < 4) { + sourceRect.x = 0; + sourceRect.y = y; + sourceRect.width = 640; + sourceRect.height = 48; + _background->getSurface()->copyFrom(_topBackgroundSurface->getSurface(), 0, y, sourceRect, true); + } else if (rowIndex > _maxRowIndex - 5) { + sourceRect.x = 0; + sourceRect.y = (rowIndex - _maxRowIndex + 4) * 48; + sourceRect.width = 640; + sourceRect.height = 48; + _background->getSurface()->copyFrom(_bottomBackgroundSurface->getSurface(), 0, y, sourceRect, true); + } else { + rowIndex -= 4; + sourceRect.x = 0; + sourceRect.y = (rowIndex * 48) % 480; + sourceRect.width = 640; + sourceRect.height = 48; + _background->getSurface()->copyFrom(_backgroundSurface->getSurface(), 0, y, sourceRect, true); + if (rowIndex < (int)_strings.size()) { + const char *text = _strings[rowIndex]; + // TODO/CHECKME: Use temporary string up to '{' character (see above) + _fontSurface->drawString(_background->getSurface(), 95, y, (const byte*)text); + } + } +} + +static const int16 kScene2242XPositions[] = { + 68, + 158 +}; + +static const uint32 kScene2242MessageListIds2[] = { + 0x004B3CB8, + 0x004B3CD8 +}; + +static const uint32 kScene2242MessageListIds1[] = { + 0x004B3CF8, + 0x004B3D20 +}; + +Scene2242::Scene2242(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule, true), _isKlaymanInLight(false) { + + _surfaceFlag = true; + SetMessageHandler(&Scene2242::handleMessage); + SetUpdateHandler(&Scene2242::update); + + if (getGlobalVar(0x4D080E54)) { + setBackground(0x11840E24); + setPalette(0x11840E24); + insertMouse433(0x40E20110); + setRectList(0x004B3DC8); + } else { + setBackground(0x25848E24); + setPalette(0x25848E24); + addEntity(_palette); + _palette->copyBasePalette(0, 256, 0); + _palette->addPalette(0x68033B1C, 0, 65, 0); + insertMouse433(0x48E20250); + setRectList(0x004B3E18); + } + + _asTape = insertSprite<AsScene1201Tape>(this, 10, 1100, 464, 435, 0x9148A011); + _vm->_collisionMan->addSprite(_asTape); + + if (which < 0) { + insertKlayman<KmScene2242>(200, 430); + setMessageList(0x004B3C18); + } else if (which == 1) { + insertKlayman<KmScene2242>(530, 430); + setMessageList(0x004B3D60); + } else if (which == 2) { + insertKlayman<KmScene2242>(kScene2242XPositions[!getGlobalVar(0x48A68852) ? 0 : 1], 430); + setMessageList(0x004B3D48); + if (getGlobalVar(0xC0418A02)) + _klayman->setDoDeltaX(1); + } else { + insertKlayman<KmScene2242>(0, 430); + setMessageList(0x004B3C20); + } + + _klayman->setSoundFlag(true); + +} + +Scene2242::~Scene2242() { + setGlobalVar(0xC0418A02, _klayman->isDoDeltaX() ? 1 : 0); +} + +void Scene2242::update() { + if (!getGlobalVar(0x4D080E54)) { + if (_isKlaymanInLight && _klayman->getX() < 440) { + _palette->addBasePalette(0x68033B1C, 0, 65, 0); + _palette->startFadeToPalette(12); + _isKlaymanInLight = false; + } else if (!_isKlaymanInLight && _klayman->getX() >= 440) { + _palette->addBasePalette(0x25848E24, 0, 65, 0); + _palette->startFadeToPalette(12); + _isKlaymanInLight = true; + } + } + Scene::update(); +} + +uint32 Scene2242::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x800C6694) { + readClickedColumn(); + } + break; + case 0x4826: + if (sender == _asTape) { + sendEntityMessage(_klayman, 0x1014, _asTape); + setMessageList(0x004B3D50); + } + break; + } + return messageResult; +} + +void Scene2242::readClickedColumn() { + int index; + if (_mouseClickPos.x < 108) { + setGlobalVar(0xC8C28808, 0x04290188); + setGlobalVar(0x48A68852, 42); + setGlobalVar(0x4CE79018, calcHash("bgRecPanelStart1")); + index = 0; + } else { + setGlobalVar(0xC8C28808, 0x04290188); + setGlobalVar(0x48A68852, 43); + setGlobalVar(0x4CE79018, calcHash("bgRecPanelStart2")); + index = 1; + } + setGlobalVar(0x49C40058, (_mouseClickPos.y - 100) / 7); + if (ABS(_klayman->getX() - kScene2242XPositions[index]) < 133) { + setMessageList2(kScene2242MessageListIds1[index]); + } else { + setMessageList2(kScene2242MessageListIds2[index]); + } +} + +static const int16 kHallOfRecordsKlaymanXPos[] = { + 68, + 157, + 246, + 335, + 424, + 513, + 602 +}; + +static const uint32 kHallOfRecordsSceneMessageListIds2[] = { + 0x004B2978, + 0x004B2998, + 0x004B29B8, + 0x004B29D8, + 0x004B29F8, + 0x004B2A18, + 0x004B2A38 +}; + +static const uint32 kHallOfRecordsSceneMessageListIds1[] = { + 0x004B2A58, + 0x004B2A80, + 0x004B2AA8, + 0x004B2AD0, + 0x004B2AF8, + 0x004B2B20, + 0x004B2B48 +}; + +HallOfRecordsScene::HallOfRecordsScene(NeverhoodEngine *vm, Module *parentModule, int which, uint32 sceneInfo140Id) + : Scene(vm, parentModule, true) { + + _sceneInfo140 = _vm->_staticData->getSceneInfo140Item(sceneInfo140Id); + + _surfaceFlag = true; + SetMessageHandler(&HallOfRecordsScene::handleMessage); + SetUpdateHandler(&Scene::update); + + if (!getGlobalVar(0x4D080E54) && _sceneInfo140->bgFilename2) { + setRectList(0x004B2BF8); + setBackground(_sceneInfo140->bgFilename2); + setPalette(_sceneInfo140->bgFilename2); + insertMouse433(0x14320138); + } else { + setRectList(0x004B2BB8); + setBackground(_sceneInfo140->bgFilename1); + setPalette(_sceneInfo140->bgFilename1); + insertMouse433(0x63A40028); + } + + if (which < 0) { + insertKlayman<KmHallOfRecords>(200, 430); + setMessageList(0x004B2900); + } else if (which == 1) { + insertKlayman<KmHallOfRecords>(640, 430); + setMessageList(0x004B2910); + } else if (which == 2) { + insertKlayman<KmHallOfRecords>(kHallOfRecordsKlaymanXPos[getGlobalVar(0x48A68852) - _sceneInfo140->xPosIndex], 430); + setMessageList(0x004B2B70); + if (getGlobalVar(0xC0418A02)) + _klayman->setDoDeltaX(1); + } else { + insertKlayman<KmHallOfRecords>(0, 430); + setMessageList(0x004B2908); + } + + _klayman->setSoundFlag(true); + _klayman->setKlaymanTable2(); + +} + +HallOfRecordsScene::~HallOfRecordsScene() { + setGlobalVar(0xC0418A02, _klayman->isDoDeltaX() ? 1 : 0); +} + +uint32 HallOfRecordsScene::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x800C6694) { + readClickedColumn(); + } + break; + } + return messageResult; +} + +void HallOfRecordsScene::readClickedColumn() { + int16 index = (_mouseClickPos.x - 23) / 89; + if (index >= _sceneInfo140->count) { + setMessageList2(0x004B2920); + } else { + setGlobalVar(0x48A68852, _sceneInfo140->xPosIndex + index); + setGlobalVar(0x49C40058, (_mouseClickPos.y - 100) / 7); + setGlobalVar(0xC8C28808, _sceneInfo140->txFilename); + if (index == 0 && _sceneInfo140->bgFilename3) { + setGlobalVar(0x4CE79018, _sceneInfo140->bgFilename3); + } else { + setGlobalVar(0x4CE79018, 0); + } + if (ABS(_klayman->getX() - kHallOfRecordsKlaymanXPos[index]) < 133) { + setMessageList2(kHallOfRecordsSceneMessageListIds1[index]); + } else { + setMessageList2(kHallOfRecordsSceneMessageListIds2[index]); + } + } +} + +static const int16 kScene2247XPositions[] = { + 513, + 602 +}; + +static const uint32 kScene2247MessageListIds2[] = { + 0x004B54A0, + 0x004B54C0 +}; + +static const uint32 kScene2247MessageListIds1[] = { + 0x004B54E0, + 0x004B5508 +}; + +Scene2247::Scene2247(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule, true) { + + //DEBUG + setGlobalVar(0x4D080E54, 1); + + _surfaceFlag = true; + SetMessageHandler(&Scene2247::handleMessage); + SetUpdateHandler(&Scene::update); + + if (getGlobalVar(0x4D080E54)) { + setRectList(0x004B5588); + setBackground(0x40339414); + setPalette(0x40339414); + insertMouse433(0x3941040B); + } else { + setRectList(0x004B55C8); + setBackground(0x071963E5); + setPalette(0x071963E5); + insertMouse433(0x14320138); + } + + if (which < 0) { + insertKlayman<KmScene2247>(200, 430); + setMessageList(0x004B5428); + } else if (which == 1) { + insertKlayman<KmScene2247>(640, 430); + setMessageList(0x004B5438); + } else if (which == 2) { + insertKlayman<KmScene2247>(kScene2247XPositions[getGlobalVar(0xC8C28808) == 0x0008E486 ? 0 : 1], 430); + if (getGlobalVar(0xC0418A02)) + _klayman->setDoDeltaX(1); + setMessageList(0x004B5530); + } else { + insertKlayman<KmScene2247>(0, 430); + setMessageList(0x004B5430); + } + + _klayman->setSoundFlag(true); + +} + +Scene2247::~Scene2247() { + setGlobalVar(0xC0418A02, _klayman->isDoDeltaX() ? 1 : 0); +} + +uint32 Scene2247::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + // TODO: Debug stuff + case 0x100D: + if (param.asInteger() == 0x800C6694) { + readClickedColumn(); + } + break; + } + return messageResult; +} + +void Scene2247::readClickedColumn() { + int index; + if (_mouseClickPos.x < 553) { + setGlobalVar(0xC8C28808, 0x0008E486); + setGlobalVar(0x4CE79018, calcHash("bgFatherHeader")); + index = 0; + } else { + setGlobalVar(0xC8C28808, 0x03086004); + setGlobalVar(0x4CE79018, calcHash("bgQuaterHeader")); + index = 1; + } + setGlobalVar(0x48A68852, 0); + setGlobalVar(0x49C40058, (_mouseClickPos.y - 100) / 7); + if (ABS(_klayman->getX() - kScene2247XPositions[index]) < 133) { + setMessageList2(kScene2247MessageListIds1[index]); + } else { + setMessageList2(kScene2247MessageListIds2[index]); + } +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/module2200.h b/engines/neverhood/module2200.h new file mode 100644 index 0000000000..76a59509f2 --- /dev/null +++ b/engines/neverhood/module2200.h @@ -0,0 +1,403 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef NEVERHOOD_MODULE2200_H +#define NEVERHOOD_MODULE2200_H + +#include "neverhood/neverhood.h" +#include "neverhood/module.h" +#include "neverhood/scene.h" +#include "neverhood/module1000.h" +#include "neverhood/graphics.h" + +namespace Neverhood { + +// Module2200 + +class Module2200 : public Module { +public: + Module2200(NeverhoodEngine *vm, Module *parentModule, int which); + virtual ~Module2200(); +protected: + void createScene(int sceneNum, int which); + void updateScene(); +}; + +// Scene2201 + +static const NPoint kClass444Points[] = { + {305, 305}, + {321, 305}, + {336, 305}, + {305, 319}, + {321, 319}, + {336, 319}, + {305, 332}, + {321, 332}, + {336, 333} +}; + +static const uint32 kClass444FileHashes[] = { + 0x88134A44, + 0xAA124340, + 0xB8124602, + 0xA902464C, + 0x890A4244, + 0xA8124642, + 0xB812C204, + 0x381A4A4C +}; + +class AsScene2201CeilingFan : public AnimatedSprite { +public: + AsScene2201CeilingFan(NeverhoodEngine *vm); +}; + +class AsScene2201Door : public AnimatedSprite { +public: + AsScene2201Door(NeverhoodEngine *vm, Klayman *klayman, Sprite *doorLightSprite, bool flag1); +protected: + SoundResource _soundResource; + Klayman *_klayman; + Sprite *_doorLightSprite; + bool _doorOpen; + int _countdown; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void stOpenDoor(); + void stCloseDoor(); +}; + +class Class444 : public StaticSprite { +public: + Class444(NeverhoodEngine *vm, int pointIndex, int spriteIndex); +}; + +class Scene2201 : public Scene { +public: + Scene2201(NeverhoodEngine *vm, Module *parentModule, int which); + ~Scene2201(); +protected: + NRect _rect1; + NRect _rect2; + Sprite *_doorLightSprite; + Sprite *_asDoor; + Sprite *_ssDoorButton; + Sprite *_asTape; + bool _soundFlag; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class SsScene2202PuzzleTile : public StaticSprite { +public: + SsScene2202PuzzleTile(NeverhoodEngine *vm, Scene *parentScene, int16 tileIndex, int16 value); +protected: + Scene *_parentScene; + int16 _value; + int16 _tileIndex; + int16 _newX, _newY; + int16 _xDelta, _yDelta; + int16 _xIncr; + int16 _yIncr; + int16 _errValue; + int16 _counter; + int16 _xFlagPos; + bool _counterDirection; + bool _isMoving; + SoundResource _soundResource1; + SoundResource _soundResource2; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void suMoveTileX(); + void suMoveTileY(); + void moveTile(int16 newTileIndex); + void stopMoving(); +}; + +class Scene2202 : public Scene { +public: + Scene2202(NeverhoodEngine *vm, Module *parentModule, int which); + ~Scene2202(); +protected: + SoundResource _soundResource1; + SoundResource _soundResource2; + Sprite *_movingTileSprite; + Sprite *_doneMovingTileSprite; + bool _isTileMoving; + int16 _movingTileIndex; + int _surfacePriority; + bool _leaveScene; + bool _isSolved; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + int16 getFreeTileIndex(int16 index); + bool testIsSolved(); +}; + +class Class545 : public AnimatedSprite { +public: + Class545(NeverhoodEngine *vm, Scene *parentScene, int index, int surfacePriority, int16 x, int16 y); +protected: + Scene *_parentScene; + int _index; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class AsScene2203Door : public AnimatedSprite { +public: + AsScene2203Door(NeverhoodEngine *vm, Scene *parentScene, uint index); +protected: + Scene *_parentScene; + SoundResource _soundResource; + Sprite *_otherDoor; + uint _index; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void openDoor(); + void closeDoor(); +}; + +class Scene2203 : public Scene { +public: + Scene2203(NeverhoodEngine *vm, Module *parentModule, int which); + ~Scene2203(); +protected: + Sprite *_asLeftDoor; + Sprite *_asRightDoor; + Sprite *_ssSmallLeftDoor; + Sprite *_ssSmallRightDoor; + Sprite *_asTape; + Sprite *_class545; + NRect _leftDoorClipRect; + NRect _rightDoorClipRect; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class SsScene2205DoorFrame : public StaticSprite { +public: + SsScene2205DoorFrame(NeverhoodEngine *vm); +protected: + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene2205 : public Scene { +public: + Scene2205(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + Class426 *_ssLightSwitch; + Sprite *_ssDoorFrame; + bool _isKlaymanInLight; + bool _isLightOn; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Class603 : public StaticSprite { +public: + Class603(NeverhoodEngine *vm, uint32 fileHash); +protected: + int _index; + SoundResource _soundResource; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void spriteUpdate481E60(); + void spriteUpdate481E90(); +}; + +class Class604 : public StaticSprite { +public: + Class604(NeverhoodEngine *vm, uint32 fileHash); +protected: + int16 _yDelta; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void spriteUpdate482020(); +}; + +class Class607 : public StaticSprite { +public: + Class607(NeverhoodEngine *vm, Scene *parentScene, int surfacePriority, uint32 fileHash); +protected: + Scene *_parentScene; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene2206 : public Scene { +public: + Scene2206(NeverhoodEngine *vm, Module *parentModule, int which); + ~Scene2206(); +protected: + Sprite *_sprite1; + Sprite *_sprite2; + Sprite *_sprite3; + Sprite *_sprite4; + Sprite *_sprite5; + Sprite *_class604; + Sprite *_class607; + SoundResource _soundResource; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void sub481950(); + void sub4819D0(); + void sub481B00(); +}; + +class AsScene2207Elevator : public AnimatedSprite { +public: + AsScene2207Elevator(NeverhoodEngine *vm, Scene *parentScene); + ~AsScene2207Elevator(); +protected: + Scene *_parentScene; + SoundResource _soundResource; + NPointArray *_pointArray; + int16 _pointIndex; + int16 _destPointIndex, _destPointIndexDelta; + bool _isMoving; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void suSetPosition(); + void moveToY(int16 y); +}; + +class AsScene2207Lever : public AnimatedSprite { +public: + AsScene2207Lever(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y, int doDeltaX); +protected: + Scene *_parentScene; + SoundResource _soundResource; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void stLeverDown(); + void stLeverDownEvent(); + void stLeverUp(); + void stLeverUpEvent(); +}; + +class AsScene2207WallRobotAnimation : public AnimatedSprite { +public: + AsScene2207WallRobotAnimation(NeverhoodEngine *vm, Scene *parentScene); + ~AsScene2207WallRobotAnimation(); +protected: + SoundResource _soundResource1; + SoundResource _soundResource2; + SoundResource _soundResource3; + SoundResource _soundResource4; + bool _idle; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void stStartAnimation(); + void stStopAnimation(); + void cbStopAnimation(); +}; + +class AsScene2207WallCannonAnimation : public AnimatedSprite { +public: + AsScene2207WallCannonAnimation(NeverhoodEngine *vm); +protected: + bool _idle; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void stStartAnimation(); + void stStopAnimation(); + void cbStopAnimation(); +}; + +class SsScene2207Symbol : public StaticSprite { +public: + SsScene2207Symbol(NeverhoodEngine *vm, uint32 fileHash, int index); +}; + +class Scene2207 : public Scene { +public: + Scene2207(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + SoundResource _soundResource1; + SoundResource _soundResource2; + Sprite *_asElevator; + Sprite *_ssMaskPart1; + Sprite *_ssMaskPart2; + Sprite *_ssMaskPart3; + Sprite *_asTape; + Sprite *_asLever; + Sprite *_asWallRobotAnimation; + Sprite *_asWallCannonAnimation; + Sprite *_ssButton; + int _elevatorSurfacePriority; + bool _klaymanAtElevator; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 handleMessage2(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene2208 : public Scene { +public: + Scene2208(NeverhoodEngine *vm, Module *parentModule, int which); + ~Scene2208(); +protected: + FontSurface *_fontSurface; + BaseSurface *_backgroundSurface; + BaseSurface *_topBackgroundSurface; + BaseSurface *_bottomBackgroundSurface; + TextResource _textResource; + int16 _backgroundScrollY; + int16 _newRowIndex; + int16 _currRowIndex; + int16 _rowScrollY; + int16 _maxRowIndex; + int16 _visibleRowsCount; + Common::Array<const char*> _strings; // TODO: Move to TextResource + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void createFontSurface(); + void drawRow(int16 rowIndex); +}; + +class Scene2242 : public Scene { +public: + Scene2242(NeverhoodEngine *vm, Module *parentModule, int which); + ~Scene2242(); +protected: + Sprite *_asTape; + bool _isKlaymanInLight; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void readClickedColumn(); +}; + +class HallOfRecordsScene : public Scene { +public: + HallOfRecordsScene(NeverhoodEngine *vm, Module *parentModule, int which, uint32 sceneInfo140Id); + ~HallOfRecordsScene(); +protected: + SceneInfo140 *_sceneInfo140; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void readClickedColumn(); +}; + +class Scene2247 : public Scene { +public: + Scene2247(NeverhoodEngine *vm, Module *parentModule, int which); + ~Scene2247(); +protected: + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void readClickedColumn(); +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_MODULE2200_H */ diff --git a/engines/neverhood/module2300.cpp b/engines/neverhood/module2300.cpp new file mode 100644 index 0000000000..1513f7ba40 --- /dev/null +++ b/engines/neverhood/module2300.cpp @@ -0,0 +1,181 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "neverhood/module2300.h" + +namespace Neverhood { + +Module2300::Module2300(NeverhoodEngine *vm, Module *parentModule, int which) + : Module(vm, parentModule), _volume(0) { + + // TODO Sound1ChList_addSoundResources(0x1A214010, dword_4B6938, true); + // TODO Sound1ChList_setSoundValuesMulti(dword_4B6938, true, 50, 600, 10, 10, 150); + + _flag = getGlobalVar(0x10938830) == 0; + + if (_flag) { + // TODO Sound1ChList_setVolume(0x90F0D1C3, 0); + // TODO Sound1ChList_playLooping(0x90F0D1C3); + } else { + // TODO Sound1ChList_setSoundValues(0x90F0D1C3, false, 0, 0, 0, 0); + } + + // TODO Sound1ChList_sub_407C70(0x1A214010, 0x48498E46, 0x50399F64, 0); + // TODO Sound1ChList_sub_407C70(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() { + // TODO Sound1ChList_sub_407A50(0x1A214010); +} + +void Module2300::createScene(int sceneNum, int which) { + debug("Module2300::createScene(%d, %d)", sceneNum, which); + _vm->gameState().sceneNum = sceneNum; + switch (_vm->gameState().sceneNum) { + case 0: + createNavigationScene(0x004B67B8, which); + break; + case 1: + createNavigationScene(0x004B67E8, which); + if (_flag) { + _volume = 15; + // TODO Sound1ChList_setVolume(0x90F0D1C3, 15); + } + break; + case 2: + createNavigationScene(0x004B6878, which); + break; + case 3: + if (getGlobalVar(0x10938830)) { + createNavigationScene(0x004B68F0, which); + } else { + // TODO Sound1ChList_setVolume(0x90F0D1C3, _volume); + createNavigationScene(0x004B68A8, which); + if (_flag) { + _volume = 87; + // TODO Sound1ChList_setVolume(0x90F0D1C3, 87); + } + } + break; + case 4: + // TODO Sound1ChList_sub_4080B0(true); + createSmackerScene(0x20080A0B, true, true, false); + break; + } + SetUpdateHandler(&Module2300::updateScene); + _childObject->handleUpdate(); +} + +void Module2300::updateScene() { + if (!updateChild()) { + switch (_vm->gameState().sceneNum) { + case 0: + if (_moduleResult == 1) { + createScene(1, 4); + } else { + leaveModule(0); + } + break; + case 1: + if (_moduleResult == 1) { + createScene(0, 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(3); + } else { + createScene(1, 5); + } + break; + case 3: + if (_moduleResult == 1) { + leaveModule(2); + } else { + createScene(1, 1); + } + break; + case 4: + // TODO Sound1ChList_sub_4080B0(false); + createScene(1, 2); + break; + } + } else { + switch (_vm->gameState().sceneNum) { + case 1: +#if 0 // TODO + NavigationScene *navigationScene = (NavigationScene*)_childObject; + if (_flag && navigationScene->getSoundFlag1() && navigationScene->getNavigationIndex() == 4 && + navigationScene->getSmackerPlayer() && navigationScene->getSmackerPlayer()->getFrameNumber() % 2) { + _volume++; + Sound1ChList_setVolume(0x90F0D1C3, _volume); + } +#endif +#if 0 // TODO + if (navigationScene->getSoundFlag1() && navigationScene->getNavigationIndex() == 0 && + navigationScene->getSmackerPlayer() && navigationScene->getSmackerPlayer()->getFrameNumber() == 50) { + Sound1ChList_sub_407C70(0x1A214010, 0x48498E46, 0x50399F64); + Sound1ChList_setVolume(0x48498E46, 70); + Sound1ChList_setVolume(0x50399F64, 70); + } +#endif + break; + case 3: +#if 0 // TODO + NavigationScene *navigationScene = (NavigationScene*)_childObject; + if (_flag && navigationScene->getSoundFlag1() && navigationScene->getSmackerPlayer() && + navigationScene->getSmackerPlayer()->getFrameNumber() % 2) { + _volume--; + Sound1ChList_setVolume(0x90F0D1C3, _volume); + } +#endif + break; + } + } +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/module2300.h b/engines/neverhood/module2300.h new file mode 100644 index 0000000000..722255978c --- /dev/null +++ b/engines/neverhood/module2300.h @@ -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 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef NEVERHOOD_MODULE2300_H +#define NEVERHOOD_MODULE2300_H + +#include "neverhood/neverhood.h" +#include "neverhood/module.h" +#include "neverhood/scene.h" + +namespace Neverhood { + +// Module2300 + +class Module2300 : public Module { +public: + Module2300(NeverhoodEngine *vm, Module *parentModule, int which); + virtual ~Module2300(); +protected: + bool _flag; + int _volume; + void createScene(int sceneNum, int which); + void updateScene(); +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_MODULE2300_H */ diff --git a/engines/neverhood/module2600.cpp b/engines/neverhood/module2600.cpp new file mode 100644 index 0000000000..5823e4dcb2 --- /dev/null +++ b/engines/neverhood/module2600.cpp @@ -0,0 +1,333 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "neverhood/module2600.h" + +namespace Neverhood { + +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); + } + + // TODO Sound1ChList_addSoundResources(0x40271018, dword_4B87E8, true); + // TODO Sound1ChList_setSoundValuesMulti(dword_4B87E8, true, 50, 600, 5, 150); + // TODO Sound1ChList_sub_407C70(0x40271018, 0x41861371, 0x43A2507F); + +} + +Module2600::~Module2600() { + // TODO Sound1ChList_sub_407A50(0x40271018); +} + +void Module2600::createScene(int sceneNum, int which) { + debug("Module2600::createScene(%d, %d)", sceneNum, which); + _vm->gameState().sceneNum = sceneNum; + switch (_vm->gameState().sceneNum) { + case 0: + createNavigationScene(0x004B8608, which); + break; + case 1: + createNavigationScene(0x004B8638, which); + break; + case 2: + createNavigationScene(0x004B86C8, which); + break; + case 3: + if (getGlobalVar(0x0A310817)) { + createNavigationScene(0x004B8758, which); + } else { + createNavigationScene(0x004B86F8, which); + } + break; + case 4: + createNavigationScene(0x004B87B8, which); + break; + case 6: + createNavigationScene(0x004B8698, which); + break; + case 7: + // TODO Sound1ChList_sub_407A50(0x40271018); + createSmackerScene(0x30090001, true, true, false); + break; + case 8: + _childObject = new Scene2609(_vm, this, which); + break; + case 1002: + if (getGlobalVar(0x40040831) == 1) { + createSmackerScene(0x018C0404, true, true, false); + } else if (getGlobalVar(0x40040831) == 2) { + createSmackerScene(0x018C0407, true, true, false); + } else { + createSmackerScene(0x818C0405, true, true, false); + } + if (getGlobalVar(0x40040831) >= 2) { + setGlobalVar(0x40040831, 0); + } else { + incGlobalVar(0x40040831, +1); + } + break; + case 1003: + createSmackerScene(0x001C0007, true, true, false); + break; + case 1006: + if (getGlobalVar(0x4E0BE910)) { + createSmackerScene(0x049A1181, true, true, false); + } else { + createSmackerScene(0x04981181, true, true, false); + } + break; + case 1008: + if (getGlobalVar(0x4E0BE910)) { + createSmackerScene(0x42B80941, true, true, false); + } else { + createSmackerScene(0x42980941, true, true, false); + } + break; + } + SetUpdateHandler(&Module2600::updateScene); + _childObject->handleUpdate(); +} + +void Module2600::updateScene() { + if (!updateChild()) { + switch (_vm->gameState().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) { + createScene(1002, -1); + } + break; + case 3: + if (_moduleResult == 0) { + if (getGlobalVar(0x0A310817)) { + createScene(4, 0); + } else { + createScene(1003, -1); + } + } else if (_moduleResult == 2) { + createScene(1, 1); + } else if (_moduleResult == 3) { + if (getGlobalVar(0x0A310817)) { + createScene(4, 0); + } else { + setGlobalVar(0x0A310817, 1); + createScene(7, -1); + } + } + break; + case 4: + if (_moduleResult == 0) { + leaveModule(1); + } else { + createScene(3, 1); + } + break; + case 6: + if (_moduleResult == 0) { + 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; + } + } +} + +SsScene2609Button::SsScene2609Button(NeverhoodEngine *vm, Scene *parentScene) + : StaticSprite(vm, 1400), _soundResource1(vm), _soundResource2(vm), + _soundResource3(vm), _soundResource4(vm), _parentScene(parentScene), + _countdown(0) { + + _spriteResource.load2(0x825A6923); + createSurface(400, _spriteResource.getDimensions().width, _spriteResource.getDimensions().height); + if (!getGlobalVar(0x4E0BE910)) + setVisible(false); + + _drawRect.set(0, 0, _spriteResource.getDimensions().width, _spriteResource.getDimensions().height); + _deltaRect = _drawRect; + _x = _spriteResource.getPosition().x; + _y = _spriteResource.getPosition().y; + processDelta(); + _needRefresh = true; + + _soundResource1.load(0x10267160); + _soundResource2.load(0x7027FD64); + _soundResource3.load(0x44043000); + _soundResource4.load(0x44045000); + + SetUpdateHandler(&SsScene2609Button::update); + SetMessageHandler(&SsScene2609Button::handleMessage); + +} + +void SsScene2609Button::update() { + StaticSprite::update(); + if (_countdown != 0 && (--_countdown == 0)) { + if (getGlobalVar(0x4E0BE910)) { + setGlobalVar(0x4E0BE910, 0); + sendMessage(_parentScene, 0x2001, 0); + } else { + setGlobalVar(0x4E0BE910, 1); + sendMessage(_parentScene, 0x2002, 0); + } + } +} + +uint32 SsScene2609Button::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + if (_countdown == 0) { + sendMessage(_parentScene, 0x2000, 0); + if (getGlobalVar(0x4E0BE910)) { + setVisible(false); + _soundResource4.play(); + _soundResource2.play(); + _countdown = 12; + } else { + setVisible(true); + _soundResource3.play(); + _soundResource1.play(); + _countdown = 96; + } + } + messageResult = 1; + break; + } + return messageResult; +} + +AsScene2609Water::AsScene2609Water(NeverhoodEngine *vm) + : AnimatedSprite(vm, 1000) { + + _x = 240; + _y = 420; + setDoDeltaX(1); + createSurface1(0x9C210C90, 1200); + setClipRect(260, 260, 400, 368); + // TODO Sound1ChList_addSoundResource(0xDC2769B0, true); + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene2609Water::handleMessage); + if (getGlobalVar(0x4E0BE910)) + sendMessage(this, 0x2002, 0); +} + +uint32 AsScene2609Water::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2001: + stopAnimation(); + setVisible(false); + // TODO Sound1ChList_stop(0xDC2769B0); + break; + case 0x2002: + setFileHash(0x9C210C90, 0, -1); + setVisible(true); + // TODO Sound1ChList_playLooping(0xDC2769B0); + break; + } + return messageResult; +} + +Scene2609::Scene2609(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule, true), _isBusy(false) { + + _surfaceFlag = true; + setBackground(0x51409A16); + setPalette(0x51409A16); + _asWater = insertSprite<AsScene2609Water>(); + _ssButton = insertSprite<SsScene2609Button>(this); + _vm->_collisionMan->addSprite(_ssButton); + insertMouse435(0x09A1251C, 20, 620); + insertStaticSprite(0x02138002, 1200); + insertStaticSprite(0x825E2827, 1200); + SetMessageHandler(&Scene2609::handleMessage); + SetUpdateHandler(&Scene::update); +} + +uint32 Scene2609::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x0001: + if ((param.asPoint().x <= 20 || param.asPoint().x >= 620) && !_isBusy) + leaveScene(0); + break; + case 0x2000: + _isBusy = true; + break; + case 0x2001: + _isBusy = false; + sendMessage(_asWater, 0x2001, 0); + break; + case 0x2002: + _isBusy = false; + sendMessage(_asWater, 0x2002, 0); + break; + } + return 0; +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/module2600.h b/engines/neverhood/module2600.h new file mode 100644 index 0000000000..d6580a6c7a --- /dev/null +++ b/engines/neverhood/module2600.h @@ -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 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef NEVERHOOD_MODULE2600_H +#define NEVERHOOD_MODULE2600_H + +#include "neverhood/neverhood.h" +#include "neverhood/module.h" +#include "neverhood/scene.h" + +namespace Neverhood { + +// Module2600 + +class Module2600 : public Module { +public: + Module2600(NeverhoodEngine *vm, Module *parentModule, int which); + virtual ~Module2600(); +protected: + void createScene(int sceneNum, int which); + void updateScene(); +}; + +class SsScene2609Button : public StaticSprite { +public: + SsScene2609Button(NeverhoodEngine *vm, Scene *parentScene); +protected: + Scene *_parentScene; + SoundResource _soundResource1; + SoundResource _soundResource2; + SoundResource _soundResource3; + SoundResource _soundResource4; + int _countdown; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class AsScene2609Water : public AnimatedSprite { +public: + AsScene2609Water(NeverhoodEngine *vm); +protected: + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene2609 : public Scene { +public: + Scene2609(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + bool _isBusy; + Sprite *_asWater; + Sprite *_ssButton; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_MODULE2600_H */ diff --git a/engines/neverhood/module2700.cpp b/engines/neverhood/module2700.cpp new file mode 100644 index 0000000000..096e33546e --- /dev/null +++ b/engines/neverhood/module2700.cpp @@ -0,0 +1,1038 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "neverhood/module2700.h" +#include "neverhood/gamemodule.h" +#include "neverhood/module1000.h" + +namespace Neverhood { + +static const NRect kScene2710ClipRect = NRect(0, 0, 626, 480); + +static const uint32 kScene2710StaticSprites[] = { + 0x0D2016C0, + 0 +}; + +static const NRect kScene2711ClipRect = NRect(0, 0, 521, 480); + +static const uint32 kScene2711FileHashes1[] = { + 0, + 0x100801A1, + 0x201081A0, + 0x006800A4, + 0x40390120, + 0x000001B1, + 0x001000A1, + 0 +}; + +static const uint32 kScene2711FileHashes2[] = { + 0, + 0x40403308, + 0x71403168, + 0x80423928, + 0x224131A8, + 0x50401328, + 0x70423328, + 0 +}; + +static const uint32 kScene2711FileHashes3[] = { + 0, + 0x1088A021, + 0x108120E5, + 0x18A02321, + 0x148221A9, + 0x10082061, + 0x188820E1, + 0 +}; + +static const NRect kScene2724ClipRect = NRect(0, 141, 640, 480); + +static const uint32 kScene2724StaticSprites[] = { + 0xC20D00A5, + 0 +}; + +static const NRect kScene2725ClipRect = NRect(0, 0, 640, 413); + +static const uint32 kScene2725StaticSprites[] = { + 0xC20E00A5, + 0 +}; + +Module2700::Module2700(NeverhoodEngine *vm, Module *parentModule, int which) + : Module(vm, parentModule), _soundResource1(vm), _soundResource2(vm), + _soundResource3(vm), _soundResource4(vm), _soundIndex(0), _flag1(false) { + + // TODO Music18hList_add(0x42212411, 0x04020210); + // TODO Music18hList_play(0x04020210, 24, 2, 1); + SetMessageHandler(&Module2700::handleMessage); + + + if (which < 0) { + which = _vm->gameState().which; + if (_vm->gameState().sceneNum == 0 || _vm->gameState().sceneNum == 30 || _vm->gameState().sceneNum == 31) + which = -1; + createScene(_vm->gameState().sceneNum, which); + } else { + createScene(0, 0); + } + + _soundResource1.load(0x00880CCC); + _soundResource2.load(0x00880CC0); + _soundResource3.load(0x00880CCC); + _soundResource4.load(0x00880CC0); + +} + +Module2700::~Module2700() { + // TODO Sound1ChList_sub_407A50(0x42212411); +} + +void Module2700::createScene(int sceneNum, int which) { + debug("Module2700::createScene(%d, %d)", sceneNum, which); + if (sceneNum != 30 && sceneNum != 31) + _vm->gameState().which = which; + _vm->gameState().sceneNum = sceneNum; + switch (_vm->gameState().sceneNum) { + case 0: + _childObject = new Scene2701(_vm, this, which); + break; + case 1: + _childObject = new Scene2702(_vm, this, which); + break; + case 2: + if (which == 6 || which == 7) + createScene2703(which, 0x004B1710); + else if (which == 4 || which == 5) + createScene2703(which, 0x004B1738); + else if (which == 2 || which == 3) + createScene2703(which, 0x004B1760); + else + createScene2703(which, 0x004B1788); + break; + case 3: + createScene2704(which, 0x004B17B0, 150); + break; + case 4: + createScene2704(which, 0x004B17D8, 150); + break; + case 5: + if (which >= 4) { + _childObject = new Scene2706(_vm, this, which); + } else if (which == 2 || which == 3) { + createScene2704(which, 0x004B1828, 150); + } else { + createScene2704(which, 0x004B1800, 150); + } + break; + case 6: + createScene2704(which, 0x004B1850, 150); + break; + case 7: + if (which == 2 || which == 3) { + createScene2704(which, 0x004B1878, 150); + } else { + createScene2704(which, 0x004B18A0, 150); + } + break; + case 8: + if (which == 2 || which == 3) { + createScene2704(which, 0x004B18C8, 150); + } else { + createScene2704(which, 0x004B18F0, 150); + } + break; + case 9: + createScene2704(which, 0x004B1918, 150, kScene2710StaticSprites, &kScene2710ClipRect); + break; + case 10: + // TODO _vm->gameModule()->initScene2808Vars2(); + _scene2711StaticSprites[0] = kScene2711FileHashes1[getSubVar(0x40005834, 2)]; + _scene2711StaticSprites[1] = kScene2711FileHashes2[getSubVar(0x40005834, 1)]; + _scene2711StaticSprites[2] = kScene2711FileHashes3[getSubVar(0x40005834, 0)]; + _scene2711StaticSprites[3] = 0x0261282E; + _scene2711StaticSprites[4] = 0x9608E5A0; + _scene2711StaticSprites[5] = 0; + createScene2704(which, 0x004B1950, 150, _scene2711StaticSprites, &kScene2711ClipRect); + break; + case 11: + createScene2704(which, 0x004B19E0, 150); + break; + case 12: + createScene2704(which, 0x004B1A08, 150); + break; + case 13: + createScene2704(which, 0x004B1A30, 150); + break; + case 14: + if (which == 4 || which == 5) { + createScene2704(which, 0x004B1A58, 150); + } else if (which == 2 || which == 3) { + createScene2704(which, 0x004B1A80, 150); + } else { + createScene2704(which, 0x004B1AA8, 150); + } + break; + case 15: + if (which == 4 || which == 5) { + createScene2704(which, 0x004B1AD0, 150); + } else if (which == 2 || which == 3) { + createScene2704(which, 0x004B1AF8, 150); + } else { + createScene2704(which, 0x004B1B20, 150); + } + break; + case 16: + if (which == 4 || which == 5) { + createScene2704(which, 0x004B1B48, 150); + } else if (which == 2 || which == 3) { + createScene2704(which, 0x004B1B70, 150); + } else { + createScene2704(which, 0x004B1B98, 150); + } + break; + case 17: + if (which == 4 || which == 5) { + createScene2704(which, 0x004B1BC0, 150); + } else if (which == 2 || which == 3) { + createScene2704(which, 0x004B1BE8, 150); + } else { + createScene2704(which, 0x004B1C10, 150); + } + break; + case 18: + if (which == 2 || which == 3) { + createScene2704(which, 0x004B1C38, 150); + } else { + createScene2704(which, 0x004B1C60, 150); + } + break; + case 19: + if (which == 2 || which == 3) { + createScene2704(which, 0x004B1CB0, 150); + } else { + createScene2704(which, 0x004B1C88, 150); + } + break; + case 20: + if (which == 2 || which == 3) { + createScene2704(which, 0x004B1CD8, 150); + } else { + createScene2704(which, 0x004B1D00, 150); + } + break; + case 21: + createScene2704(which, 0x004B1D28, 150); + break; + case 22: + createScene2704(which, 0x004B1D50, 150); + break; + case 23: + createScene2704(which, 0x004B1D78, 150, kScene2724StaticSprites, &kScene2724ClipRect); + break; + case 24: + createScene2704(which, 0x004B1DB0, 150, kScene2725StaticSprites, &kScene2725ClipRect); + break; + case 25: + createScene2704(which, 0x004B1DE8, 150); + break; + case 26: + createScene2704(which, 0x004B1E10, 150); + break; + case 27: + createScene2704(which, 0x004B1E38, 150); + break; + case 28: + createScene2704(which, 0x004B1E60, 150); + break; + case 30: + _childObject = new Class152(_vm, this, 0x09507248, 0x0724C09D); + break; + case 31: +//TODO _childObject = new Scene2732(_vm, this, which); + break; + } + SetUpdateHandler(&Module2700::updateScene); + _childObject->handleUpdate(); +} + +#define SceneLinkIf(moduleResult, sceneNum, which) \ + if (_moduleResult == moduleResult) { createScene(sceneNum, which); break; } + +void Module2700::updateScene() { + if (!updateChild()) { + + debug("sceneNum = %d; _moduleResult = %d", _vm->gameState().sceneNum, _moduleResult); + + switch (_vm->gameState().sceneNum) { + case 0: + SceneLinkIf(1, 1, 0); + leaveModule(0); + break; + case 1: + SceneLinkIf(1, 14, 1); + SceneLinkIf(2, 2, 2); + SceneLinkIf(3, 14, 3); + SceneLinkIf(4, 2, 6); + SceneLinkIf(5, 2, 4); + createScene(0, 1); + break; + case 2: + SceneLinkIf(1, 5, 0); + SceneLinkIf(2, 1, 2); + SceneLinkIf(3, 5, 2); + SceneLinkIf(4, 1, 5); + SceneLinkIf(5, 5, 4); + SceneLinkIf(6, 1, 4); + SceneLinkIf(7, 11, 0); + createScene(3, 0); + break; + case 3: + createScene(2, 0); + break; + case 4: + SceneLinkIf(1, 7, 2); + createScene(5, 5); + break; + case 5: + SceneLinkIf(1, 6, 0); + SceneLinkIf(2, 2, 3); + SceneLinkIf(3, 8, 2); + SceneLinkIf(4, 2, 5); + SceneLinkIf(5, 4, 0); + SceneLinkIf(6, 7, 0); + createScene(2, 1); + break; + case 6: + SceneLinkIf(1, 8, 0); + createScene(5, 1); + break; + case 7: + SceneLinkIf(1, 8, 3); + SceneLinkIf(2, 4, 1); + SceneLinkIf(3, 9, 0); + createScene(5, 6); + break; + case 8: + SceneLinkIf(1, 10, 0); + SceneLinkIf(2, 5, 3); + SceneLinkIf(3, 7, 1); + createScene(6, 1); + break; + case 9: + SceneLinkIf(1, 10, 1); + createScene(7, 3); + break; + case 10: + SceneLinkIf(1, 9, 1); + createScene(8, 1); + break; + case 11: + SceneLinkIf(1, 12, 0); + createScene(2, 7); + break; + case 12: + SceneLinkIf(1, 13, 0); + createScene(11, 1); + break; + case 13: + SceneLinkIf(1, 30, 0); + createScene(12, 1); + break; + case 14: + SceneLinkIf(1, 1, 1); + SceneLinkIf(2, 15, 3); + SceneLinkIf(3, 1, 3); + SceneLinkIf(4, 15, 5); + SceneLinkIf(5, 22, 0); + createScene(15, 1); + break; + case 15: + SceneLinkIf(1, 14, 0); + SceneLinkIf(2, 16, 3); + SceneLinkIf(3, 14, 2); + SceneLinkIf(4, 16, 5); + SceneLinkIf(5, 14, 4); + createScene(16, 1); + break; + case 16: + SceneLinkIf(1, 15, 0); + SceneLinkIf(2, 17, 3); + SceneLinkIf(3, 15, 2); + SceneLinkIf(4, 17, 5); + SceneLinkIf(5, 15, 4); + createScene(17, 1); + break; + case 17: + SceneLinkIf(1, 16, 0); + SceneLinkIf(2, 18, 3); + SceneLinkIf(3, 16, 2); + SceneLinkIf(4, 20, 1); + SceneLinkIf(5, 16, 4); + createScene(18, 1); + break; + case 18: + SceneLinkIf(1, 17, 0); + SceneLinkIf(2, 19, 2); + SceneLinkIf(3, 17, 2); + createScene(19, 0); + break; + case 19: + SceneLinkIf(1, 20, 2); + SceneLinkIf(2, 18, 2); + SceneLinkIf(3, 20, 0); + createScene(18, 0); + break; + case 20: + SceneLinkIf(1, 17, 4); + SceneLinkIf(2, 19, 1); + SceneLinkIf(3, 21, 0); + createScene(19, 3); + break; + case 21: + // TODO? GameState_sub_469C50(&field_52, 0); + // TODO MusicMan_stopAll (if field_52 above = 1) + // TODO Music18hList_delete(_musicFileHash); + // TODO Music18hList_play(0x04020210, 0, 2, 1); + // TODO Sound1ChList_sub_407AF0(0x42212411); + createScene(20, 3); + break; + case 22: + SceneLinkIf(1, 23, 0); + createScene(14, 5); + break; + case 23: + SceneLinkIf(1, 24, 0); + createScene(22, 1); + break; + case 24: + SceneLinkIf(1, 25, 0); + createScene(23, 1); + break; + case 25: + SceneLinkIf(1, 26, 0); + createScene(24, 1); + break; + case 26: + SceneLinkIf(1, 27, 0); + createScene(25, 1); + break; + case 27: + SceneLinkIf(1, 28, 0); + createScene(26, 1); + break; + case 28: + SceneLinkIf(1, 31, 0); + createScene(27, 1); + break; + case 29: + createScene(13, 1); + break; + case 30: + createScene(28, 1); + break; + } + } else { + switch (_vm->gameState().sceneNum) { + case 21: + if (!_flag1) { + // TODO Music18hList_stop(0x04020210, 0, 1); + // TODO _vm->gameModule()->initScene2801Vars(); + _musicFileHash = getGlobalVar(0x89A82A15); + // TODO? GameState_sub_469C50(&field_52, 0); + // TODO MusicMan_create(); + // TODO Music18hList_add2(0x42212411, _musicFileHash); + // TODO Music18hList_play2(_musicFileHash, 0, /*TODO */???, 1); + // TODO Sound1ChList_addSoundResource(0x42212411, 0x44014282, true); + // TODO Sound1ChList_setSoundValues(0x44014282, true, 120, 360, 72, 0); + _flag1 = true; + } + break; + } + } +} + +void Module2700::update() { + +} + +uint32 Module2700::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Module::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x200D: + switch (_soundIndex) { + case 0: + _soundResource1.play(); + break; + case 1: + _soundResource2.play(); + break; + case 2: + _soundResource3.play(); + break; + case 3: + _soundResource4.play(); + break; + } + _soundIndex++; + if (_soundIndex >= 4) + _soundIndex = 0; + break; + } + return messageResult; +} + +void Module2700::createScene2703(int which, uint32 sceneInfoId, const uint32 *staticSprites, const NRect *clipRect) { + // TODO +} + +void Module2700::createScene2704(int which, uint32 sceneInfoId, int16 value, const uint32 *staticSprites, const NRect *clipRect) { + _childObject = new Scene2704(_vm, this, which, sceneInfoId, value, staticSprites, clipRect); +} + +Class437::Class437(NeverhoodEngine *vm, uint32 fileHash) + : StaticSprite(vm, 0) { + + _spriteResource.load2(fileHash); + createSurface(0, _spriteResource.getDimensions().width, _spriteResource.getDimensions().height); + _x = _spriteResource.getPosition().x; + _y = _spriteResource.getPosition().y; + _drawRect.set(0, 0, _spriteResource.getDimensions().width, _spriteResource.getDimensions().height); + _needRefresh = true; + StaticSprite::update(); +} + +Scene2701::Scene2701(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule, true) { + + NRect clipRect; + SceneInfo2700 *sceneInfo = _vm->_staticData->getSceneInfo2700(0x004B2240); + setGlobalVar(0x21E60190, 1); + + _surfaceFlag = true; + + setBackground(sceneInfo->bgFilename); + setPalette(sceneInfo->bgFilename); + + _palette->addPalette(calcHash("paPodFloor"), 65, 31, 65); + _palette->addPalette(calcHash("paKlayFloor"), 0, 65, 0); + + insertMouse433(0x08B08180); + + _sprite1 = insertStaticSprite(0x1E086325, 1200); + + clipRect.set(0, 0, 640, _sprite1->getDrawRect().x2()); + + if (sceneInfo->class437Filename) { + _class437 = insertSprite<Class437>(sceneInfo->class437Filename); + _class521 = insertSprite<Class521>(this, 320, 240); +//TODO _class517 = insertSprite<Class517>(_class521, _class437->getSurface(), 4); +//TODO _class520 = insertSprite<Class520>(_class521, _class437->getSurface(), 4); +//TODO _class519 = insertSprite<Class519>(_class521, _class437->getSurface(), 4); + } else { + _class437 = NULL; + _class521 = insertSprite<Class521>(this, 320, 240); + } + +//TODO _class518 = insertSprite<Class518>(_class521); + + _which1 = sceneInfo->which1; + _which2 = sceneInfo->which2; + + _dataResource.load(sceneInfo->dataResourceFilename); + _trackPoints = _dataResource.getPointArray(sceneInfo->pointListName); + _class521->setPathPoints(_trackPoints); + + if (which == _which2) { + NPoint testPoint = (*_trackPoints)[_trackPoints->size() - 1]; + sendMessage(_class521, 0x2002, _trackPoints->size() - 1); + if (testPoint.x < 0 || testPoint.x >= 640 || testPoint.y < 0 || testPoint.y >= 480) + sendMessage(_class521, 0x2007, 150); + } else { + NPoint testPoint = (*_trackPoints)[0]; + sendMessage(_class521, 0x2002, 0); + if (testPoint.x < 0 || testPoint.x >= 640 || testPoint.y < 0 || testPoint.y >= 480) + sendMessage(_class521, 0x2008, 150); + } + + _class521->setClipRect(clipRect); + // TODO _class518->setClipRect(clipRect); + + if (which == 1) { + SetMessageHandler(&Scene2701::handleMessage42F500); + } else { + sendMessage(_class521, 0x2009, 0); + SetMessageHandler(&Scene2701::handleMessage42F600); + } + +} + +uint32 Scene2701::handleMessage42F500(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x0001: + sendPointMessage(_class521, 0x2004, param.asPoint()); + break; + case 0x2005: + if (_which1 >= 0) + SetMessageHandler(&Scene2701::handleMessage42F600); + break; + case 0x2006: + if (_which2 >= 0) + leaveScene(_which2); + break; + case 0x200D: + sendMessage(_parentModule, 0x200D, 0); + break; + } + return 0; +} + +uint32 Scene2701::handleMessage42F600(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x0001: + if (param.asPoint().x >= 385) { + leaveScene(0); + } else { + sendPointMessage(_class521, 0x2004, param.asPoint()); + SetMessageHandler(&Scene2701::handleMessage42F500); + } + break; + case 0x200D: + sendMessage(_parentModule, 0x200D, 0); + break; + } + return 0; +} + +static const uint32 kScene2702Infos[2][3] = { + {0x004B5F68, 0x004B5F8C, 0x004B5FB0}, + {0x004B5FD8, 0x004B5FFC, 0x004B6020} +}; + + +Scene2702::Scene2702(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule, true), _flag1(true), _newTrackIndex(-1), _count(3) { + + for (int i = 0; i < 2; i++) + for (int j = 0; j < 3; j++) + _sceneInfos[i][j] = _vm->_staticData->getSceneInfo2700(kScene2702Infos[i][j]); + + _surfaceFlag = true; + SetMessageHandler(&Scene2702::handleMessage); + SetUpdateHandler(&Scene2702::update); + + setBackground(0x18808B00); + setPalette(0x18808B00); + + _palette->addPalette(calcHash("paPodFloor"), 65, 31, 65); + _palette->addPalette(calcHash("paKlayFloor"), 0, 65, 0); + addEntity(_palette); + + insertMouse433(0x08B04180); + + _class437 = insertSprite<Class437>(0x12002035); + _class521 = insertSprite<Class521>(this, 320, 240); + //TODO _class517 = insertSprite<Class517>(_class521, _class437->getSurface(), 4); + //TODO insertSprite<Class518>(_class521); + //TODO _class520 = insertSprite<Class520>(_class521, _class437->getSurface(), 4); + //TODO _class519 = insertSprite<Class519>(_class521, _class437->getSurface(), 4); + + _dataResource.load(0x04310014); + + if (which == 1) { + _currSceneInfos = _sceneInfos[1]; + _currTrackIndex = 1; + } else if (which == 2) { + _currSceneInfos = _sceneInfos[1]; + _currTrackIndex = 2; + _palette->addPalette(calcHash("paPodShade"), 65, 31, 65); + _palette->addPalette(calcHash("paKlayShade"), 0, 65, 0); + _flag1 = false; + } else if (which == 3) { + _currSceneInfos = _sceneInfos[0]; + _currTrackIndex = 0; + } else if (which == 4) { + _currSceneInfos = _sceneInfos[0]; + _currTrackIndex = 2; + _palette->addPalette(calcHash("paPodShade"), 65, 31, 65); + _palette->addPalette(calcHash("paKlayShade"), 0, 65, 0); + _flag1 = false; + } else if (which == 5) { + _currSceneInfos = _sceneInfos[0]; + _currTrackIndex = 1; + _palette->addPalette(calcHash("paPodShade"), 65, 31, 65); + _palette->addPalette(calcHash("paKlayShade"), 0, 65, 0); + _flag1 = false; + } else { + _currSceneInfos = _sceneInfos[1]; + _currTrackIndex = 0; + } + + _trackPoints = _dataResource.getPointArray(_currSceneInfos[_currTrackIndex]->pointListName); + _class521->setPathPoints(_trackPoints); + + if (which == _currSceneInfos[_currTrackIndex]->which2) { + sendMessage(_class521, 0x2002, _trackPoints->size() - 1); + sendMessage(_class521, 0x2007, 150); + } else { + sendMessage(_class521, 0x2002, 0); + sendMessage(_class521, 0x2008, 150); + } + + _palette->copyBasePalette(0, 256, 0); + +} + +void Scene2702::update() { + Scene::update(); + if (_flag1 && _class521->getX() > 422) { + debug("fade #1"); + _palette->addBasePalette(calcHash("paPodShade"), 65, 31, 65); + _palette->addBasePalette(calcHash("paKlayShade"), 0, 65, 0); + _palette->startFadeToPalette(12); + _flag1 = false; + } else if (!_flag1 && _class521->getX() <= 422) { + debug("fade #2"); + _palette->addBasePalette(calcHash("paPodFloor"), 65, 31, 65); + _palette->addBasePalette(calcHash("paKlayFloor"), 0, 65, 0); + _palette->startFadeToPalette(12); + _flag1 = true; + } +} + +uint32 Scene2702::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x0001: + findClosestTrack(param.asPoint()); + break; + case 0x2005: + if (_newTrackIndex >= 0) { + if (_currSceneInfos[_currTrackIndex]->which1 < 0) + changeTrack(); + } else if (_currSceneInfos[_currTrackIndex]->which1 >= 0) + leaveScene(_currSceneInfos[_currTrackIndex]->which1); + break; + case 0x2006: + if (_newTrackIndex >= 0) { + if (_currSceneInfos[_currTrackIndex]->which2 < 0) + changeTrack(); + } else if (_currSceneInfos[_currTrackIndex]->which2 >= 0) + leaveScene(_currSceneInfos[_currTrackIndex]->which2); + break; + case 0x200D: + sendMessage(_parentModule, 0x200D, 0); + break; + } + return 0; +} + +void Scene2702::findClosestTrack(NPoint pt) { + int minMatchTrackIndex = -1; + int minMatchDistance = 640; + // Find the track which contains a point closest to pt + for (int infoIndex = 0; infoIndex < _count; infoIndex++) { + NPointArray *pointList = _dataResource.getPointArray(_currSceneInfos[infoIndex]->pointListName); + 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 = infoIndex; + minMatchDistance = distance; + } + } + } + if (minMatchTrackIndex >= 0 && minMatchTrackIndex != _currTrackIndex) { + _newTrackIndex = minMatchTrackIndex; + _newTrackDestX = pt.x; + if (_currSceneInfos == _sceneInfos[0]) { + if (_currTrackIndex == 0) + sendMessage(_class521, 0x2003, _trackPoints->size() - 1); + else + sendMessage(_class521, 0x2003, 0); + } else if (_currTrackIndex == 2) { + sendMessage(_class521, 0x2003, 0); + } else { + sendMessage(_class521, 0x2003, _trackPoints->size() - 1); + } + } else { + _newTrackIndex = -1; + sendMessage(_class521, 0x2004, pt.x); + } +} + +void Scene2702::changeTrack() { + _currTrackIndex = _newTrackIndex; + _trackPoints = _dataResource.getPointArray(_currSceneInfos[_currTrackIndex]->pointListName); + _class521->setPathPoints(_trackPoints); + if (_currSceneInfos == _sceneInfos[0]) { + if (_currTrackIndex == 0) + sendMessage(_class521, 0x2002, _trackPoints->size() - 1); + else + sendMessage(_class521, 0x2002, 0); + } else if (_currTrackIndex == 2) { + sendMessage(_class521, 0x2002, 0); + } else { + sendMessage(_class521, 0x2002, _trackPoints->size() - 1); + } + sendMessage(_class521, 0x2004, _newTrackDestX); + _newTrackIndex = -1; +} + +Scene2704::Scene2704(NeverhoodEngine *vm, Module *parentModule, int which, uint32 sceneInfoId, int16 value, + const uint32 *staticSprites, const NRect *clipRect) + : Scene(vm, parentModule, true) { + + SceneInfo2700 *sceneInfo = _vm->_staticData->getSceneInfo2700(sceneInfoId); + + _surfaceFlag = true; + SetMessageHandler(&Scene2704::handleMessage); + SetUpdateHandler(&Scene2704::update); + + setBackground(sceneInfo->bgFilename); + setPalette(sceneInfo->bgFilename); + + if (sceneInfo->exPaletteFilename1) + _palette->addPalette(sceneInfo->exPaletteFilename1, 0, 65, 0); + + if (sceneInfo->exPaletteFilename2) + _palette->addPalette(sceneInfo->exPaletteFilename2, 65, 31, 65); + + while (staticSprites && *staticSprites) + insertStaticSprite(*staticSprites++, 1100); + + insertMouse433(sceneInfo->mouseCursorFilename); + + if (sceneInfo->class437Filename) { + _class437 = insertSprite<Class437>(sceneInfo->class437Filename); + _class521 = insertSprite<Class521>(this, 320, 240); +//TODO _class517 = insertSprite<Class517>(_class521, _class437->getSurface(), 4); +//TODO _class520 = insertSprite<Class520>(_class521, _class437->getSurface(), 4); +//TODO _class519 = insertSprite<Class519>(_class521, _class437->getSurface(), 4); + } else { + _class437 = NULL; +//TODO _class517 = NULL; + _class521 = insertSprite<Class521>(this, 320, 240); + } + +//TODO _class518 = insertSprite<Class518>(_class521); + + _which1 = sceneInfo->which1; + _which2 = sceneInfo->which2; + + _dataResource.load(sceneInfo->dataResourceFilename); + _trackPoints = _dataResource.getPointArray(sceneInfo->pointListName); + _class521->setPathPoints(_trackPoints); + + if (sceneInfo->rectListName) { + _rectList = _dataResource.getRectArray(sceneInfo->rectListName); + // TODO _class521->setPathRects(_rectList); + } + + if (which == _which2) { + NPoint testPoint = (*_trackPoints)[_trackPoints->size() - 1]; + sendMessage(_class521, 0x2002, _trackPoints->size() - 1); + if (testPoint.x > 0 && testPoint.x < 640 && testPoint.y > 0 && testPoint.y < 480) + sendMessage(_class521, 0x2009, 0); + else + sendMessage(_class521, 0x2007, 0); + } else { + NPoint testPoint = (*_trackPoints)[0]; + sendMessage(_class521, 0x2002, 0); + if (testPoint.x > 0 && testPoint.x < 640 && testPoint.y > 0 && testPoint.y < 480) + sendMessage(_class521, 0x2009, 0); + else + sendMessage(_class521, 0x2008, 0); + } + + if (clipRect) { + _class521->getClipRect() = *clipRect; +#if 0 + if (_class517) + _class517->getClipRect() = *clipRect; + if (_class520) + _class520->getClipRect() = *clipRect; + if (_class519) + _class519->getClipRect() = *clipRect; + if (_class518) + _class518->getClipRect() = *clipRect; +#endif + } + +} + +void Scene2704::update() { + Scene::update(); + if (_mouseClicked) { + sendPointMessage(_class521, 0x2004, _mouseClickPos); + _mouseClicked = false; + } +} + +uint32 Scene2704::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2005: + if (_which1 >= 0) + leaveScene(_which1); + break; + case 0x2006: + if (_which2 >= 0) + leaveScene(_which2); + break; + case 0x200D: + sendMessage(_parentModule, 0x200D, 0); + break; + } + return 0; +} + +static const int kSceneInfo2706Count = 3; +static const struct { const char *pointListName; int which1, which2; } kSceneInfo2706[] = { + {"me06slotSlotPath2", 4, -1}, + {"me06slotSlotPath3", -1, 6}, + {"me06slotSlotPath4", -1, 5} +}; + +Scene2706::Scene2706(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule, true), _newTrackIndex(-1) { + + _surfaceFlag = true; + SetMessageHandler(&Scene2706::handleMessage); + + setBackground(0x18808B88); + setPalette(0x18808B88); + + _palette->addPalette(calcHash("paPodShade"), 65, 31, 65); + _palette->addPalette(calcHash("paKlayShade"), 0, 65, 0); + + insertMouse433(0x08B8C180); + + _class437 = insertSprite<Class437>(0x18808B88); + _class521 = insertSprite<Class521>(this, 320, 240); +//TODO _class517 = insertSprite<Class517>(_class521, _class437->getSurface(), 4); +//TODO _class518 = insertSprite<Class518>(_class521); +//TODO _class520 = insertSprite<Class520>(_class521, _class437->getSurface(), 4); +//TODO _class519 = insertSprite<Class519>(_class521, _class437->getSurface(), 4); + + _dataResource.load(0x06000162); + + if (which == 5) + _currTrackIndex = 2; + else if (which == 6) + _currTrackIndex = 1; + else + _currTrackIndex = 0; + + _trackPoints = _dataResource.getPointArray(calcHash(kSceneInfo2706[_currTrackIndex].pointListName)); + _class521->setPathPoints(_trackPoints); + + if (which == kSceneInfo2706[_currTrackIndex].which2) { + sendMessage(_class521, 0x2002, _trackPoints->size() - 1); + if (which == 5) + sendMessage(_class521, 0x2007, 50); + else + sendMessage(_class521, 0x2007, 150); + } else { + sendMessage(_class521, 0x2002, 0); + if (which == 5) + sendMessage(_class521, 0x2008, 50); + else + sendMessage(_class521, 0x2008, 150); + } + +} + +uint32 Scene2706::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x0001: + findClosestTrack(param.asPoint()); + break; + case 0x2005: + if (_newTrackIndex >= 0) { + if (kSceneInfo2706[_currTrackIndex].which1 < 0) + changeTrack(); + } else if (kSceneInfo2706[_currTrackIndex].which1 >= 0) + leaveScene(kSceneInfo2706[_currTrackIndex].which1); + break; + case 0x2006: + if (_newTrackIndex >= 0) { + if (kSceneInfo2706[_currTrackIndex].which2 < 0) + changeTrack(); + } else if (kSceneInfo2706[_currTrackIndex].which2 >= 0) + leaveScene(kSceneInfo2706[_currTrackIndex].which2); + break; + case 0x200D: + sendMessage(_parentModule, 0x200D, 0); + break; + } + return 0; +} + +void Scene2706::findClosestTrack(NPoint pt) { + int minMatchTrackIndex = -1; + int minMatchDistance = 640; + // Find the track which contains a point closest to pt + for (int infoIndex = 0; infoIndex < kSceneInfo2706Count; infoIndex++) { + NPointArray *pointList = _dataResource.getPointArray(calcHash(kSceneInfo2706[infoIndex].pointListName)); + 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 = infoIndex; + minMatchDistance = distance; + } + } + } + if (minMatchTrackIndex >= 0 && minMatchTrackIndex != _currTrackIndex) { + _newTrackIndex = minMatchTrackIndex; + _newTrackDestX = pt.x; + if (_currTrackIndex == 0) + sendMessage(_class521, 0x2003, _trackPoints->size() - 1); + else + sendMessage(_class521, 0x2003, 0); + } else { + _newTrackIndex = -1; + sendMessage(_class521, 0x2004, pt.x); + } +} + +void Scene2706::changeTrack() { + _currTrackIndex = _newTrackIndex; + _trackPoints = _dataResource.getPointArray(calcHash(kSceneInfo2706[_currTrackIndex].pointListName)); + _class521->setPathPoints(_trackPoints); + if (_currTrackIndex == 0) + sendMessage(_class521, 0x2002, _trackPoints->size() - 1); + else + sendMessage(_class521, 0x2002, 0); + sendMessage(_class521, 0x2004, _newTrackDestX); + _newTrackIndex = -1; +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/module2700.h b/engines/neverhood/module2700.h new file mode 100644 index 0000000000..f1ef3f5727 --- /dev/null +++ b/engines/neverhood/module2700.h @@ -0,0 +1,134 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef NEVERHOOD_MODULE2700_H +#define NEVERHOOD_MODULE2700_H + +#include "neverhood/neverhood.h" +#include "neverhood/module.h" +#include "neverhood/scene.h" +#include "neverhood/module1600.h" + +namespace Neverhood { + +// Module2700 + +class Module2700 : public Module { +public: + Module2700(NeverhoodEngine *vm, Module *parentModule, int which); + virtual ~Module2700(); +protected: + SoundResource _soundResource1; + SoundResource _soundResource2; + SoundResource _soundResource3; + SoundResource _soundResource4; + int _soundIndex; + bool _flag1; + uint32 _scene2711StaticSprites[6]; + uint32 _musicFileHash; + void createScene(int sceneNum, int which); + void updateScene(); + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void createScene2703(int which, uint32 sceneInfoId, const uint32 *staticSprites = NULL, const NRect *clipRect = NULL); + void createScene2704(int which, uint32 sceneInfoId, int16 value, const uint32 *staticSprites = NULL, const NRect *clipRect = NULL); +}; + +class Class437 : public StaticSprite { +public: + Class437(NeverhoodEngine *vm, uint32 fileHash); +}; + +class Scene2701 : public Scene { +public: + Scene2701(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + Class521 *_class521; + Sprite *_class437; + Sprite *_class517; + Sprite *_class520; + Sprite *_class519; + Sprite *_class518; + Sprite *_sprite1; + int _which1, _which2; + NPointArray *_trackPoints; + uint32 handleMessage42F500(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 handleMessage42F600(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene2702 : public Scene { +public: + Scene2702(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + Class521 *_class521; + Sprite *_class437; + Sprite *_class517; + Sprite *_class520; + Sprite *_class519; + int16 _newTrackDestX; + int _currTrackIndex, _newTrackIndex; + int _count; + bool _flag1; + SceneInfo2700 *_sceneInfos[2][3]; + SceneInfo2700 **_currSceneInfos; + NPointArray *_trackPoints; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void findClosestTrack(NPoint pt); + void changeTrack(); +}; + +class Scene2704 : public Scene { +public: + Scene2704(NeverhoodEngine *vm, Module *parentModule, int which, uint32 sceneInfoId, int16 value, + const uint32 *staticSprites = NULL, const NRect *clipRect = NULL); +protected: + Class521 *_class521; + Sprite *_class437; + int _which1, _which2; + NPointArray *_trackPoints; + NRectArray *_rectList; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene2706 : public Scene { +public: + Scene2706(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + Class521 *_class521; + Sprite *_class437; + Sprite *_class517; + Sprite *_class518; + Sprite *_class520; + Sprite *_class519; + int16 _newTrackDestX; + int _currTrackIndex, _newTrackIndex; + NPointArray *_trackPoints; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void findClosestTrack(NPoint pt); + void changeTrack(); +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_MODULE2700_H */ diff --git a/engines/neverhood/module3000.cpp b/engines/neverhood/module3000.cpp new file mode 100644 index 0000000000..783e6166ea --- /dev/null +++ b/engines/neverhood/module3000.cpp @@ -0,0 +1,1617 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "neverhood/module3000.h" +#include "neverhood/gamemodule.h" +#include "neverhood/navigationscene.h" + +namespace Neverhood { + +Module3000::Module3000(NeverhoodEngine *vm, Module *parentModule, int which) + : Module(vm, parentModule), _soundVolume(0) { + + // TODO Sound1ChList_addSoundResources(0x81293110, dword_4B7FC8, true); + // TODO Sound1ChList_setSoundValuesMulti(dword_4B7FC8, 1, 50, 600, 5, 150); + // TODO Sound1ChList_setSoundValues(0x90F0D1C3, false, 20000, 30000, 20000, 30000); + // TODO Sound1ChList_sub_407C70(0x81293110, 0x48498E46, 0x50399F64, 0); + // TODO Sound1ChList_sub_407C70(0x81293110, 0x40030A51, 0xC862CA15, 0); + // TODO Sound1ChList_sub_407C70(0x81293110, 0x41861371, 0x43A2507F, 0); + + _flag = getGlobalVar(0x10938830) != 0; + + if (_flag) { + // TODO Sound1ChList_setVolume(0x90F0D1C3, 0); + // TODO Sound1ChList_playLooping(0x90F0D1C3); + } + + if (which < 0) { + createScene(_vm->gameState().sceneNum, -1); + } else if (which == 0) { + createScene(1, 0); + } else if (which == 1) { + createScene(4, 2); + } else if (which == 2) { + createScene(4, 1); + } else if (which == 3) { + createScene(5, 1); + } + +} + +Module3000::~Module3000() { + // TODO Sound1ChList_sub_407A50(0x81293110); +} + +void Module3000::createScene(int sceneNum, int which) { + static const byte kNavigationTypes05[] = {3, 0}; + static const byte kNavigationTypes06[] = {5}; + debug("Module3000::createScene(%d, %d)", sceneNum, which); + _vm->gameState().sceneNum = sceneNum; + switch (_vm->gameState().sceneNum) { + case 1: + if (!getGlobalVar(0x01BA1A52)) { + createNavigationScene(0x004B7C80, which); + } else if (getGlobalVar(0x10938830)) { + createNavigationScene(0x004B7CE0, which); + } else { + createNavigationScene(0x004B7CB0, which); + } + break; + case 2: + // TODO Sound1ChList_sub_407C70(0x81293110, 0x40030A51, 0xC862CA15, 0); + if (_flag) { + _soundVolume = 90; + // TODO Sound1ChList_setVolume(0x90F0D1C3, 90); + } + if (getGlobalVar(0x10938830)) { + createNavigationScene(0x004B7D58, which); + } else { + createNavigationScene(0x004B7D10, which); + } + break; + case 3: + if (getGlobalVar(0x09221A62)) + createNavigationScene(0x004B7E60, which); + else if (getGlobalVar(0x10938830)) + createNavigationScene(0x004B7DA0, which); + else + createNavigationScene(0x004B7E00, which); + break; + case 4: + if (getGlobalVar(0x09221A62)) + createNavigationScene(0x004B7F20, which); + else + createNavigationScene(0x004B7EC0, which); + break; + case 5: + createNavigationScene(0x004B7F80, which, kNavigationTypes05); + break; + case 6: + createNavigationScene(0x004B7FB0, which, kNavigationTypes06); + break; + case 7: + // TODO Sound1ChList_setSoundValuesMulti(dword_4B7FC8, 0, 0, 0, 0, 0); + if (!getSubVar(0x40050052, 0x089809C2)) { + setSubVar(0x40050052, 0x089809C2, 1); + createSmackerScene(0x90022001, true, true, false); + } else + createSmackerScene(0x98022001, true, true, false); + break; + case 8: + _childObject = new Scene3009(_vm, this, which); + break; + case 9: + _childObject = new Scene3010(_vm, this, 0); + break; + case 10: + _childObject = new Scene3011(_vm, this, 0); + break; + case 11: + // TODO Sound1ChList_setSoundValuesMulti(dword_4B7FC8, 0, 0, 0, 0, 0); + if (!getSubVar(0x40050052, 0x10130993)) { + setSubVar(0x40050052, 0x10130993, 1); + createSmackerScene(0x31093019, true, true, false); + } else + createSmackerScene(0x20093019, true, true, false); + break; + case 12: + _childObject = new Scene3010(_vm, this, 1); + break; + // NOTE: Newly introduced sceneNums + case 1001: + if (!getGlobalVar(0x01BA1A52)) + if (getGlobalVar(0x10938830)) + createSmackerScene(0x00940021, true, true, false); + else + createSmackerScene(0x01140021, true, true, false); + else + if (getGlobalVar(0x10938830)) + createSmackerScene(0x001011B1, true, true, false); + else + createSmackerScene(0x001021B1, true, true, false); + setGlobalVar(0x01BA1A52, getGlobalVar(0x01BA1A52) ? 0 : 1); + break; + case 1006: + createSmackerScene(0x080810C5, true, true, false); + break; + case 1008: + createSmackerScene(getGlobalVar(0xF0402B0A), true, true, false); + break; + } + SetUpdateHandler(&Module3000::updateScene); + _childObject->handleUpdate(); +} + +void Module3000::updateScene() { + if (!updateChild()) { + switch (_vm->gameState().sceneNum) { + case 1: + if (!getGlobalVar(0x01BA1A52)) { + if (_moduleResult == 0) + createScene(9, -1); + else if (_moduleResult == 1) + leaveModule(0); + } else { + if (_moduleResult == 0) + if (_navigationAreaType == 2) + createScene(2, 0); + else + createScene(1001, -1); + else if (_moduleResult == 1) + leaveModule(0); + } + break; + case 2: + // TODO Sound1ChList_sub_407C70(0x81293110, 0x41861371, 0x43A2507F, 0); + if (_flag) { + _soundVolume = 0; + // TODO Sound1ChList_setVolume(0x90F0D1C3, 0); + } + if (_moduleResult == 0) { + createScene(3, 0); + } else if (_moduleResult == 1) { + setGlobalVar(0x01BA1A52, 0); + createScene(1, 1); + } + break; + case 3: + if (_moduleResult == 1) + createScene(4, 0); + else if (_moduleResult == 3) + createScene(10, -1); + else if (getGlobalVar(0x09221A62)) + createScene(5, 0); + else + createScene(2, 1); + break; + case 4: + if (_moduleResult == 0) + leaveModule(1); + else if (_moduleResult == 1) + createScene(7, -1); + else if (_moduleResult == 2) + createScene(3, 3); + break; + case 5: + if (_moduleResult == 0) + createScene(6, 0); + else if (_moduleResult == 1) + createScene(3, 0); + break; + case 6: + if (_navigationAreaType == 4) + createScene(11, -1); + else + createScene(1006, -1); + break; + case 7: + createScene(8, -1); + break; + case 8: + _flag = getGlobalVar(0x10938830); // CHECKME + if (_moduleResult != 1) { + // TODO: Sound1ChList_setSoundValuesMulti(dword_4B7FC8, true, 0, 0, 0, 0): + createScene(4, 1); + } else if (getGlobalVar(0xF0402B0A)) { + createScene(1008, -1); + } else { + // TODO: Sound1ChList_setSoundValuesMulti(dword_4B7FC8, true, 0, 0, 0, 0); + createScene(4, 1); + } + break; + case 9: + if (_moduleResult == 0 || _moduleResult == 2) + createScene(1, 0); + else if (_moduleResult == 1) + createScene(1001, -1); + break; + case 10: + createScene(3, 3); + break; + case 11: + leaveModule(3); + break; + case 12: + createScene(1, 0); + break; + case 1001: + if (getGlobalVar(0x01BA1A52)) + createScene(1, 0); + else + createScene(12, -1); + break; + case 1006: + createScene(5, 0); + break; + case 1008: + createScene(8, -1); + break; + } + } else { + switch (_vm->gameState().sceneNum) { + case 1: +#if 0 // ALL TODO + if (navigationScene()->getSoundFlag1()) { + uint32 frameNumber = navigationScene()->getFrameNumber(); + int navigationIndex = navigationScene()->getIndex(); + if (navigationIndex == 1) { + if (frameNumber == 0) { + // TODO Sound1ChList_sub_407C70(0x81293110, 0x48498E46, 0x50399F64, 0); + // TODO Sound1ChList_setVolume(0x48498E46, 70); + // TODO Sound1ChList_setVolume(0x50399F64, 70); + } else if (frameNumber == 100) { + // TODO Sound1ChList_sub_407C70(0x81293110, 0x41861371, 0x43A2507F, 0); + } + } else if (navigationIndex == 0) { + if (frameNumber == 0) { + // TODO Sound1ChList_sub_407C70(0x81293110, 0x48498E46, 0x50399F64, 0); + // TODO Sound1ChList_setVolume(0x48498E46, 70); + // TODO Sound1ChList_setVolume(0x50399F64, 70); + } else if (frameNumber == 10) { + // TODO Sound1ChList_sub_407C70(0x81293110, 0x40030A51, 0xC862CA15, 0); + } + if (_flag && _soundVolume < 90 && frameNumber % 2) { + if (frameNumber == 0) + _soundVolume = 40; + else + _soundVolume++; + // TODO Sound1ChList_setVolume(0x90F0D1C3, _soundVolume); + } + } + } +#endif + break; + case 2: +#if 0 // ALL TODO + if (navigationScene()->getSoundFlag1()) { + uint32 frameNumber = navigationScene()->getFrameNumber(); + int navigationIndex = navigationScene()->getIndex(); + if (_flag && _soundVolume > 1 && frameNumber % 2) { + _soundVolume--; + // TODO Sound1ChList_setVolume(0x90F0D1C3, _soundVolume); + } + if (navigationIndex == 0) { + if (frameNumber == 35) { + // TODO Sound1ChList_sub_407C70(0x81293110, 0x41861371, 0x43A2507F, 0); + } + } else if (navigationIndex == 1) { + if (frameNumber == 55) { + // TODO Sound1ChList_sub_407C70(0x81293110, 0x48498E46, 0x50399F64, 0); + // TODO Sound1ChList_setVolume(0x48498E46, 70); + // TODO Sound1ChList_setVolume(0x50399F64, 70); + } + } + } +#endif + break; + case 3: +#if 0 // ALL TODO + if (navigationScene()->getSoundFlag1()) { + uint32 frameNumber = navigationScene()->getFrameNumber(); + int navigationIndex = navigationScene()->getIndex(); + if (navigationIndex == 2) { + if (frameNumber == 40) { + // TODO Sound1ChList_sub_407C70(0x81293110, 0x40030A51, 0xC862CA15, 0); + } + if (_flag && _soundVolume < 90 && frameNumber % 2) { + if (frameNumber == 0) + _soundVolume = 40; + else + _soundVolume++; + // TODO Sound1ChList_setVolume(0x90F0D1C3, _soundVolume); + } + } + } +#endif + break; + case 5: +#if 0 // ALL TODO + if (navigationScene()->getSoundFlag1() && navigationScene()->getIndex() == 0) { + // TODO Sound1ChList_sub_4080B0(false); + } +#endif + break; + } + } +} + +// Scene3009 + +static const uint32 kScene3009SmackerFileHashes[] = { + 0x1010000D, + 0x340A0049, + 0x340A0049, + 0x0282081D, + 0x0082080D, + 0x0882080D, + 0x0882080D, + 0x0282081D, + 0x004B000B, + 0x014B000B, + 0x044B000B, + 0x0282081D, + 0x0282081D, + 0x0282081D, + 0x340A0049 +}; + +static const uint32 kScene3009CannonLocationFileHashes[] = { + 0x00000000, + 0x8004001B, + 0x0004001A, + 0x1048404B, + 0x50200109, + 0x12032109, + 0x10201109, + 0x000A2030, + 0x000A0028, + 0x000A0028, + 0x000A0028, + 0x040A1069, + 0x040A1069, + 0x040A1069, + 0x240A1101 +}; + +static const uint32 kSsScene3009SymbolEdgesFileHashes[] = { + 0x618827A0, + 0xB1A92322 +}; + +static const uint32 kSsScene3009TargetLineFileHashes[] = { + 0x4011018C, + 0x15086623 +}; + +static const NPoint kAsScene3009SymbolPoints[] = { + {289, 338}, + {285, 375}, + {284, 419}, + {456, 372}, + {498, 372}, + {541, 372} +}; + +static const uint32 kAsScene3009SymbolFileHashes[] = { + 0x24542582, + 0x1CD61D96 +}; + +static const uint32 kSsScene3009SymbolArrowFileHashes1[] = { + 0x24016060, + 0x21216221, + 0x486160A0, + 0x42216422, + 0x90A16120, + 0x84216824, + 0x08017029, + 0x08217029, + 0x10014032, + 0x10214032, + 0x20012004, + 0x20212004 +}; + +static const uint32 kSsScene3009SymbolArrowFileHashes2[] = { + 0x40092024, + 0x01636002, + 0x8071E028, + 0x02A56064, + 0x00806031, + 0x052960A8, + 0x0A116130, + 0x0A316130, + 0x14216200, + 0x14016200, + 0x28416460, + 0x28616460 +}; + +SsScene3009FireCannonButton::SsScene3009FireCannonButton(NeverhoodEngine *vm, Scene3009 *parentScene) + : StaticSprite(vm, 1400), _soundResource(vm), _parentScene(parentScene), + _flag1(false) { + + _spriteResource.load2(0x120B24B0); + createSurface(400, _spriteResource.getDimensions().width, _spriteResource.getDimensions().height); + _x = _spriteResource.getPosition().x; + _y = _spriteResource.getPosition().y; + _drawRect.x = 0; + _drawRect.y = 0; + _drawRect.width = _spriteResource.getDimensions().width; + _drawRect.height = _spriteResource.getDimensions().height; + _deltaRect.x = 0; + _deltaRect.y = 0; + _deltaRect.width = _spriteResource.getDimensions().width; + _deltaRect.height = _spriteResource.getDimensions().height; + setVisible(false); + processDelta(); + _needRefresh = true; + SetUpdateHandler(&SsScene3009FireCannonButton::update); + SetMessageHandler(&SsScene3009FireCannonButton::handleMessage); + _soundResource.load(0x3901B44F); +} + +void SsScene3009FireCannonButton::update() { + StaticSprite::update(); + if (_flag1 && !_soundResource.isPlaying()) { + sendMessage(_parentScene, 0x2000, 0); + setVisible(false); + } +} + +uint32 SsScene3009FireCannonButton::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + if (!_flag1 && !_parentScene->sub462E90()) { + _flag1 = true; + setVisible(true); + _soundResource.play(); + } + messageResult = 1; + break; + } + return messageResult; +} + +SsScene3009SymbolEdges::SsScene3009SymbolEdges(NeverhoodEngine *vm, int index) + : StaticSprite(vm, 1400), _blinkCountdown(0) { + + _spriteResource.load2(kSsScene3009SymbolEdgesFileHashes[index]); + createSurface(600, _spriteResource.getDimensions().width, _spriteResource.getDimensions().height); + _x = _spriteResource.getPosition().x; + _y = _spriteResource.getPosition().y; + _drawRect.x = 0; + _drawRect.y = 0; + _drawRect.width = _spriteResource.getDimensions().width; + _drawRect.height = _spriteResource.getDimensions().height; + _needRefresh = true; + if (getGlobalVar(0x0C0288F4)) { + hide(); + } else { + startBlinking(); + } + SetUpdateHandler(&SsScene3009SymbolEdges::update); +} + +void SsScene3009SymbolEdges::update() { + if (_blinkCountdown != 0 && (--_blinkCountdown == 0)) { + if (_blinkToggle) { + setVisible(true); + } else { + setVisible(false); + } + StaticSprite::update(); + _blinkCountdown = 3; + _blinkToggle = !_blinkToggle; + } +} + +void SsScene3009SymbolEdges::show() { + setVisible(true); + StaticSprite::update(); + _blinkCountdown = 0; +} + +void SsScene3009SymbolEdges::hide() { + setVisible(false); + StaticSprite::update(); + _blinkCountdown = 0; +} + +void SsScene3009SymbolEdges::startBlinking() { + setVisible(true); + StaticSprite::update(); + _blinkCountdown = 3; + _blinkToggle = true; +} + +SsScene3009TargetLine::SsScene3009TargetLine(NeverhoodEngine *vm, int index) + : StaticSprite(vm, 1400) { + + _spriteResource.load2(kSsScene3009TargetLineFileHashes[index]); + createSurface(600, _spriteResource.getDimensions().width, _spriteResource.getDimensions().height); + _x = _spriteResource.getPosition().x; + _y = _spriteResource.getPosition().y; + _drawRect.x = 0; + _drawRect.y = 0; + _drawRect.width = _spriteResource.getDimensions().width; + _drawRect.height = _spriteResource.getDimensions().height; + setVisible(false); + _needRefresh = true; +} + +void SsScene3009TargetLine::show() { + setVisible(true); + StaticSprite::update(); +} + +SsScene3009SymbolArrow::SsScene3009SymbolArrow(NeverhoodEngine *vm, Sprite *asSymbol, int index) + : StaticSprite(vm, 1400), _soundResource(vm), _asSymbol(asSymbol), + _index(index), _enabled(true), _countdown(0) { + + _incrDecr = _index % 2; + + _spriteResource.load2(kSsScene3009SymbolArrowFileHashes2[_index]); + createSurface(1200, 33, 31); + _x = _spriteResource.getPosition().x; + _y = _spriteResource.getPosition().y; + _drawRect.x = 0; + _drawRect.y = 0; + _drawRect.width = 33; + _drawRect.height = 31; + _deltaRect = _drawRect; + processDelta(); + _needRefresh = true; + SetUpdateHandler(&SsScene3009SymbolArrow::update); + SetMessageHandler(&SsScene3009SymbolArrow::handleMessage); + _soundResource.load(0x2C852206); +} + +void SsScene3009SymbolArrow::hide() { + _enabled = false; + setVisible(false); +} + +void SsScene3009SymbolArrow::update() { + StaticSprite::update(); + if (_countdown != 0 && (--_countdown == 0)) { + _spriteResource.load2(kSsScene3009SymbolArrowFileHashes2[_index]); + _needRefresh = true; + _drawRect.x = 0; + _drawRect.y = 0; + _drawRect.width = _spriteResource.getDimensions().width; + _drawRect.height = _spriteResource.getDimensions().height; + } +} + +uint32 SsScene3009SymbolArrow::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + if (_enabled && _countdown == 0) { + _countdown = 2; + _spriteResource.load2(kSsScene3009SymbolArrowFileHashes1[_index]); + _needRefresh = true; + _drawRect.x = 0; + _drawRect.y = 0; + _drawRect.width = _spriteResource.getDimensions().width; + _drawRect.height = _spriteResource.getDimensions().height; + _soundResource.play(); + sendMessage(_asSymbol, 0x2005, _incrDecr); + } + messageResult = 1; + break; + } + return messageResult; +} + +AsScene3009VerticalIndicator::AsScene3009VerticalIndicator(NeverhoodEngine *vm, Scene3009 *parentScene, int index) + : AnimatedSprite(vm, 1000), _parentScene(parentScene), _enabled(false) { + + _x = 300; + _y = getGlobalVar(0x000809C2) ? 52 : 266; + createSurface1(0xC2463913, 1200); + _needRefresh = true; + updatePosition(); + setVisible(false); + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene3009VerticalIndicator::handleMessage); +} + +void AsScene3009VerticalIndicator::show() { + setFileHash(0xC2463913, 0, -1); + setVisible(true); + updatePosition(); + _enabled = true; +} + +uint32 AsScene3009VerticalIndicator::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + if (_enabled) { + sendMessage(_parentScene, 0x2002, 0); + } + messageResult = 1; + break; + } + return messageResult; +} + +AsScene3009HorizontalIndicator::AsScene3009HorizontalIndicator(NeverhoodEngine *vm, Scene3009 *parentScene, uint32 varValue) + : AnimatedSprite(vm, 1000), _parentScene(parentScene), _enabled(false) { + + _x = getGlobalVar(0x9040018A) ? 533 : 92; + _y = 150; + createSurface1(0xC0C12954, 1200); + _needRefresh = true; + updatePosition(); + setVisible(false); + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene3009HorizontalIndicator::handleMessage); + if (varValue == 8 || varValue == 9 || varValue == 10) { + SetSpriteCallback(&AsScene3009HorizontalIndicator::suMoveRight); + _x = 280; + } +} + +uint32 AsScene3009HorizontalIndicator::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + if (_enabled) { + sendMessage(_parentScene, 0x2004, 0); + } + messageResult = 1; + break; + } + return messageResult; +} + +void AsScene3009HorizontalIndicator::suMoveLeft() { + _x -= 6; + if (_x < 92) { + SetSpriteCallback(NULL); + _x = 92; + } +} + +void AsScene3009HorizontalIndicator::suMoveRight() { + _x += 6; + if (_x > 533) { + SetSpriteCallback(NULL); + _x = 533; + } +} + +void AsScene3009HorizontalIndicator::show() { + setFileHash(0xC0C12954, 0, -1); + setVisible(true); + updatePosition(); + _enabled = true; +} + +void AsScene3009HorizontalIndicator::stMoveLeft() { + _x = 533; + SetSpriteCallback(&AsScene3009HorizontalIndicator::suMoveLeft); +} + +void AsScene3009HorizontalIndicator::stMoveRight() { + _x = 330; + SetSpriteCallback(&AsScene3009HorizontalIndicator::suMoveRight); +} + +AsScene3009Symbol::AsScene3009Symbol(NeverhoodEngine *vm, Scene3009 *parentScene, int index) + : AnimatedSprite(vm, 1100), _parentScene(parentScene), _index(index) { + + _symbolIndex = getSubVar(0x00000914, _index); + + _x = kAsScene3009SymbolPoints[_index].x; + _y = kAsScene3009SymbolPoints[_index].y; + createSurface1(kAsScene3009SymbolFileHashes[_index / 3], 1200); + setFileHash(kAsScene3009SymbolFileHashes[_index / 3], _symbolIndex, -1); + _newHashListIndex = _symbolIndex; + _needRefresh = true; + updatePosition(); + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene3009Symbol::handleMessage); + _ssArrowPrev = _parentScene->insertSprite<SsScene3009SymbolArrow>(this, _index * 2 + 0); + _vm->_collisionMan->addSprite(_ssArrowPrev); + _ssArrowNext = _parentScene->insertSprite<SsScene3009SymbolArrow>(this, _index * 2 + 1); + _vm->_collisionMan->addSprite(_ssArrowNext); +} + +uint32 AsScene3009Symbol::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2005: + if (param.asInteger()) { + if (_symbolIndex == 11) + _symbolIndex = 0; + else + _symbolIndex++; + } else { + if (_symbolIndex == 0) + _symbolIndex = 11; + else + _symbolIndex--; + } + setFileHash(kAsScene3009SymbolFileHashes[_index / 3], _symbolIndex, -1); + _newHashListIndex = _symbolIndex; + setSubVar(0x00000914, _index, _symbolIndex); + if (_index / 3 == 0) { + sendMessage(_parentScene, 0x2001, 0); + } else { + sendMessage(_parentScene, 0x2003, 0); + } + messageResult = 1; + break; + } + return messageResult; +} + +void AsScene3009Symbol::hide() { + _ssArrowPrev->hide(); + _ssArrowNext->hide(); +} + +Scene3009::Scene3009(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule, true), _keepVideo(false), _flag2(false), + /*_flag3(false), */_flag4(false), _lockSymbolsPart1Countdown(1), _lockSymbolsPart2Countdown(1) { + + _cannonLocation = getGlobalVar(0x20580A86); + debug("_cannonLocation = %d", _cannonLocation); + + _vm->gameModule()->initScene3009Vars(); + + setGlobalVar(0xF0402B0A, 0); + _surfaceFlag = true; + + _vm->_screen->clear(); + + setBackground(0xD000420C); + setPalette(0xD000420C); + insertMouse435(0x04208D08, 20, 620); + + _ssFireCannonButton = insertSprite<SsScene3009FireCannonButton>(this); + _vm->_collisionMan->addSprite(_ssFireCannonButton); + + _asVerticalIndicator = insertSprite<AsScene3009VerticalIndicator>(this, _cannonLocation); + _vm->_collisionMan->addSprite(_asVerticalIndicator); + + _asHorizontalIndicator = insertSprite<AsScene3009HorizontalIndicator>(this, _cannonLocation); + _vm->_collisionMan->addSprite(_asHorizontalIndicator); + + if (_cannonLocation != 0 && _cannonLocation != 8 && _cannonLocation != 9 && _cannonLocation != 10) { + _keepVideo = true; + } else { + _keepVideo = false; + if (_cannonLocation != 0) { + _asHorizontalIndicator->stMoveRight(); + _flag4 = true; + } + } + + _smackerPlayer = addSmackerPlayer(new SmackerPlayer(_vm, this, kScene3009SmackerFileHashes[_cannonLocation], false, _keepVideo)); + _smackerPlayer->setDrawPos(89, 37); + _palette->usePalette(); // Use it again since the SmackerPlayer overrides the usage + + insertStaticSprite(0x8540252C, 400); + + for (int i = 0; i < 2; i++) { + _ssSymbolEdges[i] = insertSprite<SsScene3009SymbolEdges>(i); + _ssTargetLines[i] = insertSprite<SsScene3009TargetLine>(i); + } + + for (int i = 0; i < 6; i++) { + _asSymbols[i] = insertSprite<AsScene3009Symbol>(this, i); + if (i < 3) + _correctSymbols[i] = getSubVar(0x00504B86, i); + else + _correctSymbols[i] = getSubVar(0x0A4C0A9A, i - 3); + } + + SetMessageHandler(&Scene3009::handleMessage); + SetUpdateHandler(&Scene3009::update); + + // DEBUG: Set the correct code + for (int i = 0; i < 6; i++) + setSubVar(0x00000914, i, _correctSymbols[i]); + sendMessage(this, 0x2003, 0); + //setGlobalVar(0x610210B7, 1); + +} + +void Scene3009::update() { + Scene::update(); + + if (!_keepVideo && _smackerPlayer->getFrameNumber() + 1 == _smackerPlayer->getFrameCount() && _cannonLocation <= 14) { + switch (_cannonLocation) { + case 0: + case 14: + _smackerPlayer->open(0x340A0049, true); + _palette->usePalette(); + _keepVideo = true; + break; + case 8: + _smackerPlayer->open(0x0082080D, true); + _palette->usePalette(); + _keepVideo = true; + _flag4 = false; + break; + case 9: + _smackerPlayer->open(0x0282080D, true); + _palette->usePalette(); + _keepVideo = true; + _flag4 = false; + break; + case 10: + _smackerPlayer->open(0x0882080D, true); + _palette->usePalette(); + _keepVideo = true; + _flag4 = false; + break; + case 11: + case 12: + case 13: + if (_flag2) { + if (_cannonLocation == 11) + _smackerPlayer->open(0x110A000F, false); + else if (_cannonLocation == 12) + _smackerPlayer->open(0x500B004F, false); + else if (_cannonLocation == 13) + _smackerPlayer->open(0x100B010E, false); + _palette->usePalette(); + _flag2 = false; + _asHorizontalIndicator->stMoveLeft(); + } else { + playExtVideo(); + } + break; + } + } + + if (_lockSymbolsPart1Countdown != 0 && (--_lockSymbolsPart1Countdown == 0) && isSymbolsPart1Solved()) { + for (int i = 0; i < 3; i++) + _asSymbols[i]->hide(); + if (!getGlobalVar(0x0C0288F4) || getGlobalVar(0x000809C2) || getGlobalVar(0x9040018A)) { + _ssSymbolEdges[0]->show(); + _ssTargetLines[0]->show(); + _asVerticalIndicator->show(); + } + } + + if (_lockSymbolsPart2Countdown != 0 && (--_lockSymbolsPart2Countdown == 0) && isSymbolsPart2Solved()) { + for (int i = 3; i < 6; i++) + _asSymbols[i]->hide(); + if (!getGlobalVar(0x0C0288F4) || getGlobalVar(0x000809C2) || getGlobalVar(0x9040018A)) { + _ssSymbolEdges[1]->show(); + _ssTargetLines[1]->show(); + _asHorizontalIndicator->show(); + } + } + +} + +uint32 Scene3009::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x0001: + // TODO: Debug stuff + if ((param.asPoint().x <= 20 || param.asPoint().x >= 620) && !getGlobalVar(0x000809C2)) { + setGlobalVar(0x20580A86, 0); + leaveScene(0); + } + break; + case 0x000D: + // TODO: Debug stuff + break; + case 0x2000: + if (!getGlobalVar(0x000809C2)) { + if (!getGlobalVar(0x10938830)) { + _cannonLocation = 1; + setGlobalVar(0x10938830, 1); + } else { + _cannonLocation = 2; + } + } else if (!getGlobalVar(0x9040018A)) { + _cannonLocation = 3; + } else if (!getGlobalVar(0x610210B7)) { + _cannonLocation = 4; + } else if (!getGlobalVar(0x0C0288F4)) { + setGlobalVar(0x0C0288F4, 1); + _cannonLocation = 5; + } else { + _cannonLocation = 6; + } + playExtVideo(); + break; + case 0x2001: + _lockSymbolsPart1Countdown = 24; + break; + case 0x2002: + if (!getGlobalVar(0x9040018A) && !_flag4) { + if (getGlobalVar(0x000809C2)) { + _cannonLocation = 14; + setGlobalVar(0x000809C2, 0); + } else { + _cannonLocation = 7; + setGlobalVar(0x000809C2, 1); + } + playExtVideo(); + } + break; + case 0x2003: + _lockSymbolsPart2Countdown = 24; + break; + case 0x2004: + if (getGlobalVar(0x000809C2)) { + if (!getGlobalVar(0x9040018A)) { + if (!getGlobalVar(0x610210B7)) { + _cannonLocation = 8; + } else if (!getGlobalVar(0x0C0288F4)) { + _cannonLocation = 9; + } else { + _cannonLocation = 10; + } + setGlobalVar(0x9040018A, 1); + _flag4 = true; + playExtVideo(); + } else { + if (!getGlobalVar(0x610210B7)) { + _cannonLocation = 11; + _smackerPlayer->open(0x108A000F, false); + } else if (!getGlobalVar(0x0C0288F4)) { + _cannonLocation = 12; + _smackerPlayer->open(0x500B002F, false); + } else { + _cannonLocation = 13; + _smackerPlayer->open(0x100B008E, false); + } + _palette->usePalette(); + _flag2 = true; + _flag4 = true; + _keepVideo = false; + setGlobalVar(0x9040018A, 0); + } + } + break; + } + return 0; +} + +void Scene3009::playExtVideo() { + setGlobalVar(0x20580A86, _cannonLocation); + setGlobalVar(0xF0402B0A, kScene3009CannonLocationFileHashes[_cannonLocation]); + leaveScene(1); +} + +bool Scene3009::isSymbolsPart1Solved() { + for (int i = 0; i < 3; i++) + if (_correctSymbols[i] != getSubVar(0x00000914, i)) + return false; + return true; +} + +bool Scene3009::isSymbolsPart2Solved() { + for (int i = 3; i < 6; i++) + if (_correctSymbols[i] != getSubVar(0x00000914, i)) + return false; + return true; +} + +bool Scene3009::sub462E90() { + return _flag4; +} + +// Scene3010 + +static const uint32 kScene3010ButtonNameHashes[] = { + 0x304008D2, + 0x40119852, + 0x01180951 +}; + +static const uint32 kScene3010DeadBoltButtonFileHashes1[] = { + 0x301024C2, + 0x20280580, + 0x30200452 +}; + +static const uint32 kScene3010DeadBoltButtonFileHashes2[] = { + 0x50C025A8, + 0x1020A0A0, + 0x5000A7E8 +}; + +static const NPoint kAsScene3010DeadBoltPoints[] = { + {550, 307}, + {564, 415}, + {560, 514} +}; + +static const uint32 kAsScene3010DeadBoltFileHashes2[] = { + 0x181A0042, + 0x580A08F2, + 0x18420076 +}; + +static const uint32 kAsScene3010DeadBoltFileHashes1[] = { + 0x300E105A, + 0x804E0052, + 0x040E485A +}; + +SsScene3010DeadBoltButton::SsScene3010DeadBoltButton(NeverhoodEngine *vm, Scene *parentScene, int buttonIndex, int initCountdown, bool initDisabled) + : StaticSprite(vm, 900), _parentScene(parentScene), _soundResource1(vm), + _soundResource2(vm), _soundResource3(vm), _buttonLocked(false), _countdown1(0), + _countdown2(0), _buttonIndex(buttonIndex) { + + NDimensions dimensions1, dimensions2; + + _buttonEnabled = getSubVar(0x14800353, kScene3010ButtonNameHashes[_buttonIndex]) != 0; + _spriteResource.load2(kScene3010DeadBoltButtonFileHashes1[_buttonIndex]); + dimensions1 = _spriteResource.getDimensions(); + _spriteResource.load2(kScene3010DeadBoltButtonFileHashes2[_buttonIndex]); + dimensions2 = _spriteResource.getDimensions(); + createSurface(400, + MAX(dimensions1.width, dimensions2.width), + MAX(dimensions1.height, dimensions2.height)); + setSprite(kScene3010DeadBoltButtonFileHashes2[_buttonIndex]); + if (initDisabled) { + disableButton(); + } else if (_buttonEnabled) { + _countdown1 = initCountdown * 12 + 1; + } + _soundResource1.load(0xF4217243); + _soundResource2.load(0x44049000); + _soundResource3.load(0x6408107E); + SetUpdateHandler(&SsScene3010DeadBoltButton::update); + SetMessageHandler(&SsScene3010DeadBoltButton::handleMessage); +} + +void SsScene3010DeadBoltButton::update() { + + if (_countdown1 != 0 && (--_countdown1 == 0)) { + _soundResource1.play(); + setVisible(false); + setSprite(kScene3010DeadBoltButtonFileHashes1[_buttonIndex]); + } + + if (_countdown2 != 0 && (--_countdown2 == 0)) { + setVisible(true); + setSprite(kScene3010DeadBoltButtonFileHashes2[_buttonIndex]); + } + +} + +uint32 SsScene3010DeadBoltButton::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + if (!_buttonLocked && _countdown1 == 0) { + if (_buttonEnabled) { + _soundResource2.play(); + _soundResource3.play(); + setVisible(true); + _buttonLocked = true; + sendMessage(_parentScene, 0x2000, _buttonIndex); + } else { + sendMessage(_parentScene, 0x2002, _buttonIndex); + } + _needRefresh = true; + StaticSprite::update(); + } + messageResult = 1; + break; + } + return messageResult; +} + +void SsScene3010DeadBoltButton::disableButton() { + _buttonLocked = true; + setSprite(kScene3010DeadBoltButtonFileHashes1[_buttonIndex]); + setVisible(true); +} + +void SsScene3010DeadBoltButton::setSprite(uint32 fileHash) { + _spriteResource.load(fileHash); + _x = _spriteResource.getPosition().x; + _y = _spriteResource.getPosition().y; + _drawRect.x = 0; + _drawRect.y = 0; + _drawRect.width = _spriteResource.getDimensions().width; + _drawRect.height = _spriteResource.getDimensions().height; + _deltaRect = _drawRect; + processDelta(); + _needRefresh = true; + StaticSprite::update(); +} + +void SsScene3010DeadBoltButton::setCountdown(int count) { + _countdown2 = count * 18 + 1; +} + +AsScene3010DeadBolt::AsScene3010DeadBolt(NeverhoodEngine *vm, Scene *parentScene, int boltIndex, bool initUnlocked) + : AnimatedSprite(vm, 1100), _soundResource1(vm), _soundResource2(vm), _soundResource3(vm), + _parentScene(parentScene), _boltIndex(boltIndex), _soundToggle(true), _unlocked(false), _locked(false), + _countdown(0) { + + _x = kAsScene3010DeadBoltPoints[_boltIndex].x; + _y = kAsScene3010DeadBoltPoints[_boltIndex].y; + + if (getSubVar(0x14800353, kScene3010ButtonNameHashes[_boltIndex])) { + createSurface1(kAsScene3010DeadBoltFileHashes1[_boltIndex], 1200); + setFileHash(kAsScene3010DeadBoltFileHashes1[_boltIndex], 0, -1); + _soundResource1.load(0x46005BC4); + } else { + createSurface1(kAsScene3010DeadBoltFileHashes2[_boltIndex], 1200); + setFileHash(kAsScene3010DeadBoltFileHashes2[_boltIndex], 0, -1); + _soundResource1.load(0x420073DC); + _soundResource2.load(0x420073DC); + } + + setVisible(false); + stIdle(); + if (initUnlocked) + unlock(true); + + _needRefresh = true; + AnimatedSprite::updatePosition(); + +} + +void AsScene3010DeadBolt::update() { + updateAnim(); + updatePosition(); + if (_countdown != 0 && (--_countdown == 0)) { + stDisabled(); + } +} + +uint32 AsScene3010DeadBolt::hmAnimation(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + removeCallbacks(); + break; + } + return messageResult; +} + +void AsScene3010DeadBolt::stIdle() { + stopAnimation(); + SetUpdateHandler(&AsScene3010DeadBolt::update); + SetMessageHandler(&Sprite::handleMessage); + _locked = false; +} + +void AsScene3010DeadBolt::unlock(bool skipAnim) { + if (!_unlocked) { + setVisible(true); + if (skipAnim) { + setFileHash(kAsScene3010DeadBoltFileHashes1[_boltIndex], -1, 0); + _newHashListIndex = -2; + } else { + setFileHash(kAsScene3010DeadBoltFileHashes1[_boltIndex], 0, -1); + SetMessageHandler(&AsScene3010DeadBolt::hmAnimation); + FinalizeState(&AsScene3010DeadBolt::stIdleMessage); + NextState(&AsScene3010DeadBolt::stIdle); + _soundResource1.play(); + } + _unlocked = true; + _soundResource3.load(0x4010C345); + } +} + +void AsScene3010DeadBolt::stIdleMessage() { + stopAnimation(); + SetMessageHandler(&Sprite::handleMessage); + sendMessage(_parentScene, 0x2001, _boltIndex); +} + +void AsScene3010DeadBolt::lock() { + if (!_locked) { + _locked = true; + setVisible(true); + setFileHash(kAsScene3010DeadBoltFileHashes2[_boltIndex], 0, -1); + SetMessageHandler(&AsScene3010DeadBolt::hmAnimation); + FinalizeState(&AsScene3010DeadBolt::stDisabledMessage); + NextState(&AsScene3010DeadBolt::stIdle); + if (_soundToggle) { + _soundResource1.play(); + } else { + _soundResource2.play(); + } + _soundToggle = !_soundToggle; + } +} + +void AsScene3010DeadBolt::setCountdown(int count) { + _countdown = count * 18 + 1; +} + +void AsScene3010DeadBolt::stDisabled() { + setVisible(true); + setFileHash(kAsScene3010DeadBoltFileHashes1[_boltIndex], 0, -1); + SetMessageHandler(&AsScene3010DeadBolt::hmAnimation); + FinalizeState(&AsScene3010DeadBolt::stDisabledMessage); + NextState(&AsScene3010DeadBolt::stIdle); + _playBackwards = true; + _soundResource3.play(); +} + +void AsScene3010DeadBolt::stDisabledMessage() { + setVisible(false); + sendMessage(_parentScene, 0x2003, _boltIndex); +} + +Scene3010::Scene3010(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule, true), _soundResource(vm), _countdown(0), + _doorUnlocked(false), _checkUnlocked(false) { + + int initCountdown = 0; + + // DEBUG: Enable all buttons + setSubVar(0x14800353, kScene3010ButtonNameHashes[0], 1); + setSubVar(0x14800353, kScene3010ButtonNameHashes[1], 1); + setSubVar(0x14800353, kScene3010ButtonNameHashes[2], 1); + + _surfaceFlag = true; + + setBackground(0x80802626); + setPalette(0x80802626); + + for (int i = 0; i < 3; i++) { + _asDeadBolts[i] = insertSprite<AsScene3010DeadBolt>(this, i, which == 1);//CHECKME + _ssDeadBoltButtons[i] = insertSprite<SsScene3010DeadBoltButton>(this, i, initCountdown, which == 1);//CHECKME + _vm->_collisionMan->addSprite(_ssDeadBoltButtons[i]); + if (getSubVar(0x14800353, kScene3010ButtonNameHashes[i])) + initCountdown++; + _boltUnlocking[i] = false; + _boltUnlocked[i] = false; + } + + if (which == 0) { + insertMouse435(0x02622800, 20, 620); + } + + _soundResource.load(0x68E25540); + + SetMessageHandler(&Scene3010::handleMessage); + SetUpdateHandler(&Scene3010::update); + + if (which == 1) { + _checkUnlocked = true; + for (int i = 0; i < 3; i++) { + _boltUnlocked[i] = true; + _ssDeadBoltButtons[i]->setCountdown(i + 1); + _asDeadBolts[i]->setCountdown(i + 1); + } + } + +} + +void Scene3010::update() { + Scene::update(); + if (_checkUnlocked && !_boltUnlocked[0] && !_boltUnlocked[1] && !_boltUnlocked[2]) { + _countdown = 24; + _checkUnlocked = false; + } + if (_countdown != 0 && (--_countdown == 0)) { + leaveScene(_doorUnlocked ? 1 : 0); + } +} + +uint32 Scene3010::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x0001: + // TODO: Debug stuff + if ((param.asPoint().x <= 20 || param.asPoint().x >= 620) && _countdown == 0 && !_checkUnlocked) { + if (!_boltUnlocking[0] && !_boltUnlocking[1] && !_boltUnlocking[2]) { + showMouse(false); + if (!_boltUnlocked[0] && !_boltUnlocked[1] && !_boltUnlocked[2]) { + _countdown = 1; + } else { + _checkUnlocked = true; + for (int i = 0; i < 3; i++) { + _ssDeadBoltButtons[i]->setCountdown(i); + if (_boltUnlocked[i]) { + _asDeadBolts[i]->setCountdown(i); + } + } + } + } + } + break; + case 0x000D: + // TODO: Debug stuff + break; + case 0x2000: + if (!_boltUnlocked[param.asInteger()] && !_checkUnlocked && _countdown == 0) { + _asDeadBolts[param.asInteger()]->unlock(false); + _boltUnlocking[param.asInteger()] = true; + } + break; + case 0x2001: + _boltUnlocked[param.asInteger()] = true; + _boltUnlocking[param.asInteger()] = false; + if (_boltUnlocked[0] && _boltUnlocked[1] && _boltUnlocked[2]) { + if (!getGlobalVar(0x00040153)) { + setGlobalVar(0x00040153, 1); + _soundResource.play(); + _countdown = 60; + } else { + _countdown = 48; + } + _doorUnlocked = true; + } + break; + case 0x2002: + if (!_checkUnlocked && _countdown == 0) { + _asDeadBolts[param.asInteger()]->lock(); + } + break; + case 0x2003: + _boltUnlocked[param.asInteger()] = false; + break; + } + return 0; +} + +// Scene3011 + +static const uint32 kAsScene3011SymbolFileHashes[] = { + 0x00C88050, + 0x01488050, + 0x02488050, + 0x04488050, + 0x08488050, + 0x10488050, + 0x20488050, + 0x40488050, + 0x80488050, + 0x00488051, + 0x00488052, + 0x00488054, + 0x008B0000, + 0x008D0000, + 0x00810000, + 0x00990000, + 0x00A90000, + 0x00C90000, + 0x00090000, + 0x01890000, + 0x02890000, + 0x04890000, + 0x08890000, + 0x10890000 +}; + +SsScene3011Button::SsScene3011Button(NeverhoodEngine *vm, Scene *parentScene, bool flag) + : StaticSprite(vm, 1400), _parentScene(parentScene), _soundResource(vm), + _countdown(0) { + + if (flag) { + _spriteResource.load2(0x11282020); + } else { + _spriteResource.load2(0x994D0433); + } + _soundResource.load(0x44061000); + createSurface(400, _spriteResource.getDimensions().width, _spriteResource.getDimensions().height); + _x = _spriteResource.getPosition().x; + _y = _spriteResource.getPosition().y; + _drawRect.x = 0; + _drawRect.y = 0; + _drawRect.width = _spriteResource.getDimensions().width; + _drawRect.height = _spriteResource.getDimensions().height; + _deltaRect = _drawRect; + setVisible(false); + processDelta(); + _needRefresh = true; + SetUpdateHandler(&SsScene3011Button::update); + SetMessageHandler(&SsScene3011Button::handleMessage); +} + +void SsScene3011Button::update() { + StaticSprite::update(); + if (_countdown != 0 && (--_countdown == 0)) { + setVisible(false); + } +} + +uint32 SsScene3011Button::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = 0; + StaticSprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + if (_countdown == 0) { + setVisible(true); + _countdown = 4; + sendMessage(_parentScene, 0x2000, 0); + _soundResource.play(); + } + messageResult = 1; + break; + } + return messageResult; +} + +AsScene3011Symbol::AsScene3011Symbol(NeverhoodEngine *vm, int index, bool flag) + : AnimatedSprite(vm, 1000), _soundResource1(vm), _soundResource2(vm), + _index(index), _flag1(flag), _flag2(false) { + + if (flag) { + _x = 310; + _y = 200; + createSurface1(kAsScene3011SymbolFileHashes[_index], 1200); + _soundResource1.load(0x6052C60F); + _soundResource2.load(0x6890433B); + } else { + _index = 12; + _x = index * 39 + 96; + _y = 225; + createSurface(1200, 41, 48); + _soundResource1.load(0x64428609); + _soundResource2.load(0x7080023B); + } + setVisible(false); + _needRefresh = true; + SetUpdateHandler(&AnimatedSprite::update); +} + +void AsScene3011Symbol::show(bool flag) { + _flag2 = flag; + setFileHash(kAsScene3011SymbolFileHashes[_index], 0, -1); + setVisible(true); + if (flag) { + _soundResource2.play(); + } else { + _soundResource1.play(); + } +} + +void AsScene3011Symbol::hide() { + stopAnimation(); + setVisible(false); +} + +void AsScene3011Symbol::stopSound() { + if (_flag2) { + _soundResource2.stop(); + } else { + _soundResource2.stop(); + } +} + +void AsScene3011Symbol::change(int index, bool flag) { + _index = index; + _flag2 = flag; + setFileHash(kAsScene3011SymbolFileHashes[_index], 0, -1); + setVisible(true); + if (flag) { + _soundResource2.play(); + } else { + _soundResource1.play(); + } +} + +Scene3011::Scene3011(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule, true), _updateStatus(0), _buttonClicked(false), _index2(0) { + + // TODO _vm->gameModule()->initScene3011Vars(); + _index1 = getGlobalVar(0x2414C2F2); + + _surfaceFlag = true; + SetMessageHandler(&Scene3011::handleMessage); + SetUpdateHandler(&Scene3011::update); + + setBackground(0x92124A04); + setPalette(0xA4070114); + addEntity(_palette); + + insertMouse435(0x24A00929, 20, 620); + + for (int i = 0; i < 12; i++) + _asSymbols[i] = insertSprite<AsScene3011Symbol>(i, true); + + _ssButton = insertSprite<SsScene3011Button>(this, true); + _vm->_collisionMan->addSprite(_ssButton); + +} + +void Scene3011::update() { + Scene::update(); + + if (_countdown != 0 && (--_countdown == 0)) { + switch (_updateStatus) { + case 0: + if (_buttonClicked) { + if (_index1 == _index2) { + do { + _index3 = _vm->_rnd->getRandomNumber(12 - 1); + } while (_index1 == _index3); + _asSymbols[getSubVar(0x04909A50, _index3)]->show(true); + } else { + _asSymbols[getSubVar(0x04909A50, _index2)]->show(false); + } + _updateStatus = 1; + _countdown = 24; + fadeIn(); + _buttonClicked = false; + } + break; + case 1: + _updateStatus = 2; + _countdown = 24; + break; + case 2: + fadeOut(); + _updateStatus = 3; + _countdown = 24; + break; + case 3: + _updateStatus = 0; + _countdown = 1; + if (_index1 == _index2) { + _asSymbols[getSubVar(0x04909A50, _index3)]->hide(); + } else { + _asSymbols[getSubVar(0x04909A50, _index2)]->hide(); + } + _index2++; + if (_index2 >= 12) + _index2 = 0; + break; + } + } +} + +uint32 Scene3011::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x0001: + if (param.asPoint().x <= 20 || param.asPoint().x >= 620) { + leaveScene(0); + } + break; + case 0x2000: + _buttonClicked = true; + if (_countdown == 0) + _countdown = 1; + break; + } + return 0; +} + +void Scene3011::fadeIn() { + _palette->addBasePalette(0x92124A04, 0, 256, 0); + _palette->startFadeToPalette(24); +} + +void Scene3011::fadeOut() { + _palette->addBasePalette(0xA4070114, 0, 256, 0); + _palette->startFadeToPalette(24); +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/module3000.h b/engines/neverhood/module3000.h new file mode 100644 index 0000000000..2e25a0dec1 --- /dev/null +++ b/engines/neverhood/module3000.h @@ -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 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef NEVERHOOD_MODULE3000_H +#define NEVERHOOD_MODULE3000_H + +#include "neverhood/neverhood.h" +#include "neverhood/module.h" +#include "neverhood/scene.h" +#include "neverhood/module1200.h" + +namespace Neverhood { + +class Module3000 : public Module { +public: + Module3000(NeverhoodEngine *vm, Module *parentModule, int which); + virtual ~Module3000(); +protected: + int _soundVolume; + bool _flag; + void createScene(int sceneNum, int which); + void updateScene(); +}; + +// Scene3009 + +class Scene3009; + +class SsScene3009FireCannonButton : public StaticSprite { +public: + SsScene3009FireCannonButton(NeverhoodEngine *vm, Scene3009 *parentScene); +protected: + Scene3009 *_parentScene; + SoundResource _soundResource; + bool _flag1; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class SsScene3009SymbolEdges : public StaticSprite { +public: + SsScene3009SymbolEdges(NeverhoodEngine *vm, int index); + void show(); + void hide(); + void startBlinking(); +protected: + int _blinkCountdown; + bool _blinkToggle; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class SsScene3009TargetLine : public StaticSprite { +public: + SsScene3009TargetLine(NeverhoodEngine *vm, int index); + void show(); +}; + +class SsScene3009SymbolArrow : public StaticSprite { +public: + SsScene3009SymbolArrow(NeverhoodEngine *vm, Sprite *asSymbol, int index); + void hide(); +protected: + SoundResource _soundResource; + Sprite *_asSymbol; + int _index; + int _incrDecr; + bool _enabled; + int _countdown; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class AsScene3009VerticalIndicator : public AnimatedSprite { +public: + AsScene3009VerticalIndicator(NeverhoodEngine *vm, Scene3009 *parentScene, int index); + void show(); +protected: + Scene3009 *_parentScene; + bool _enabled; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class AsScene3009HorizontalIndicator : public AnimatedSprite { +public: + AsScene3009HorizontalIndicator(NeverhoodEngine *vm, Scene3009 *parentScene, uint32 varValue); + void show(); + void stMoveLeft(); + void stMoveRight(); +protected: + Scene3009 *_parentScene; + bool _enabled; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void suMoveLeft(); + void suMoveRight(); +}; + +class AsScene3009Symbol : public AnimatedSprite { +public: + AsScene3009Symbol(NeverhoodEngine *vm, Scene3009 *parentScene, int index); + void hide(); +protected: + Scene3009 *_parentScene; + int _index; + uint32 _symbolIndex; + SsScene3009SymbolArrow *_ssArrowPrev; + SsScene3009SymbolArrow *_ssArrowNext; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene3009 : public Scene { +public: + Scene3009(NeverhoodEngine *vm, Module *parentModule, int which); + bool sub462E90(); +protected: + int _lockSymbolsPart1Countdown; + int _lockSymbolsPart2Countdown; + SmackerPlayer *_smackerPlayer; + Sprite *_ssFireCannonButton; + SsScene3009SymbolEdges *_ssSymbolEdges[2]; + SsScene3009TargetLine *_ssTargetLines[2]; + AsScene3009VerticalIndicator *_asVerticalIndicator; + AsScene3009HorizontalIndicator *_asHorizontalIndicator; + AsScene3009Symbol *_asSymbols[6]; + uint32 _cannonLocation; + uint32 _correctSymbols[6]; + bool _keepVideo; + bool _flag2; + // UNUSED? bool _flag3; + bool _flag4; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void playExtVideo(); + bool isSymbolsPart1Solved(); + bool isSymbolsPart2Solved(); +}; + +// Scene3010 + +class SsScene3010DeadBoltButton : public StaticSprite { +public: + SsScene3010DeadBoltButton(NeverhoodEngine *vm, Scene *parentScene, int buttonIndex, int initCountdown, bool initDisabled); + void setCountdown(int count); +protected: + Scene *_parentScene; + SoundResource _soundResource1; + SoundResource _soundResource2; + SoundResource _soundResource3; + int _buttonIndex; + bool _buttonEnabled; + bool _buttonLocked; + int _countdown1; + int _countdown2; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void disableButton(); + void setSprite(uint32 fileHash); +}; + +class AsScene3010DeadBolt : public AnimatedSprite { +public: + AsScene3010DeadBolt(NeverhoodEngine *vm, Scene *parentScene, int boltIndex, bool initUnlocked); + void setCountdown(int count); + void lock(); + void unlock(bool skipAnim); +protected: + Scene *_parentScene; + SoundResource _soundResource1; + SoundResource _soundResource2; + SoundResource _soundResource3; + int _boltIndex; + int _countdown; + bool _soundToggle; + bool _unlocked; + bool _locked; + void update(); + uint32 hmAnimation(int messageNum, const MessageParam ¶m, Entity *sender); + void stIdle(); + void stIdleMessage(); + void stDisabled(); + void stDisabledMessage(); +}; + +class Scene3010 : public Scene { +public: + Scene3010(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + SoundResource _soundResource; + int _countdown; + bool _doorUnlocked; + bool _checkUnlocked; + SsScene3010DeadBoltButton *_ssDeadBoltButtons[3]; + AsScene3010DeadBolt *_asDeadBolts[3]; + bool _boltUnlocked[3]; + bool _boltUnlocking[3]; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +// Scene3011 + +class SsScene3011Button : public StaticSprite { +public: + SsScene3011Button(NeverhoodEngine *vm, Scene *parentScene, bool flag); +protected: + Scene *_parentScene; + SoundResource _soundResource; + int _countdown; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class AsScene3011Symbol : public AnimatedSprite { +public: + AsScene3011Symbol(NeverhoodEngine *vm, int index, bool flag); + void show(bool flag); + void hide(); + void stopSound(); + void change(int index, bool flag); + bool getFlag1() { return _flag1; } + int getIndex() { return _index; } +protected: + SoundResource _soundResource1; + SoundResource _soundResource2; + bool _flag1; + bool _flag2; + int _index; +}; + +class Scene3011 : public Scene { +public: + Scene3011(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + Sprite *_ssButton; + AsScene3011Symbol *_asSymbols[12]; + int _updateStatus; + bool _buttonClicked; + int _countdown; + int _index1; + int _index2; + int _index3; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void fadeIn(); + void fadeOut(); +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_MODULE3000_H */ diff --git a/engines/neverhood/mouse.cpp b/engines/neverhood/mouse.cpp new file mode 100644 index 0000000000..03b1478c55 --- /dev/null +++ b/engines/neverhood/mouse.cpp @@ -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 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "neverhood/mouse.h" + +namespace Neverhood { + +// TODO: Use CursorMan + +Mouse::Mouse(NeverhoodEngine *vm, uint32 fileHash, const NRect &mouseRect) + : StaticSprite(vm, 2000), _mouseType(kMouseType433), + _mouseCursorResource(vm), _frameNum(0) { + + _mouseRect = mouseRect; + init(fileHash); + if (_x >= _mouseRect.x1 && _x <= _mouseRect.x2 && + _y >= _mouseRect.y1 && _y <= _mouseRect.y2) { + _mouseCursorResource.setCursorNum(1); + } else { + _mouseCursorResource.setCursorNum(4); + } + updateCursor(); +} + +Mouse::Mouse(NeverhoodEngine *vm, uint32 fileHash, int16 x1, int16 x2) + : StaticSprite(vm, 2000), _mouseType(kMouseType435), + _mouseCursorResource(vm), _frameNum(0), _x1(x1), _x2(x2) { + + init(fileHash); + if (_x <= _x1) { + _mouseCursorResource.setCursorNum(6); + } else if (_x >= _x2) { + _mouseCursorResource.setCursorNum(5); + } else { + _mouseCursorResource.setCursorNum(4); + } + updateCursor(); +} + +Mouse::Mouse(NeverhoodEngine *vm, uint32 fileHash, int type) + : StaticSprite(vm, 2000), _mouseType(kMouseTypeNavigation), + _mouseCursorResource(vm), _type(type), _frameNum(0) { + + init(fileHash); + _mouseCursorResource.setCursorNum(0); +} + +void Mouse::init(uint32 fileHash) { + _mouseCursorResource.load(fileHash); + _x = _vm->getMouseX(); + _y = _vm->getMouseY(); + createSurface(2000, 32, 32); + SetUpdateHandler(&Mouse::update); + SetMessageHandler(&Mouse::handleMessage); + _drawRect.x = 0; + _drawRect.y = 0; + _drawRect.width = 32; + _drawRect.height = 32; + _deltaRect = _drawRect; + processDelta(); + _needRefresh = true; +} + +void Mouse::load(uint32 fileHash) { + _mouseCursorResource.load(fileHash); + _needRefresh = true; +} + +void Mouse::update() { + updateCursor(); + _frameNum++; + if (_frameNum >= 6) + _frameNum = 0; + _needRefresh = _frameNum % 2 == 0; +} + +uint32 Mouse::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + debug(7, "Mouse::handleMessage(%04X)", messageNum); + uint32 messageResult = 0; + if (messageNum != 5) { + messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2064: + _x = param.asPoint().x; + _y = param.asPoint().y; + switch (_type) { + case 1: + if (_x >= 320) + messageResult = 1; + else + messageResult = 0; + break; + case 2: + default: + if (_x < 100) + messageResult = 0; + else if (_x > 540) + messageResult = 1; + else + messageResult = 2; + break; + case 3: + if (_x < 100) + messageResult = 0; + else if (_x > 540) + messageResult = 1; + else + messageResult = 4; + break; + case 4: + if (_x < 100) + messageResult = 0; + else if (_x > 540) + messageResult = 1; + else if (_y >= 150) + messageResult = 2; + else + messageResult = 3; + break; + case 5: + if (_y >= 240) + messageResult = 4; + else + messageResult = 3; + break; + } + break; + case 0x4002: + _x = param.asPoint().x; + _y = param.asPoint().y; + updateCursorNum(); + processDelta(); + break; + } + } else { + // TODO: Debug stuff + } + return messageResult; +} + +void Mouse::updateCursor() { + + if (!_surface) + return; + + if (_doDeltaX) { + _surface->getDrawRect().x = filterX(_x - _drawRect.width - _drawRect.x + 1); + } else { + _surface->getDrawRect().x = filterX(_x + _drawRect.x); + } + + if (_doDeltaY) { + _surface->getDrawRect().y = filterY(_y - _drawRect.height - _drawRect.y + 1); + } else { + _surface->getDrawRect().y = filterY(_y + _drawRect.y); + } + + if (_needRefresh) { + _needRefresh = false; + _drawRect = _mouseCursorResource.getRect(); + _surface->drawMouseCursorResource(_mouseCursorResource, _frameNum / 2); + } + +} + +void Mouse::updateCursorNum() { + switch (_mouseType) { + case kMouseType433: + if (_x >= _mouseRect.x1 && _x <= _mouseRect.x2 && + _y >= _mouseRect.y1 && _y <= _mouseRect.y2) { + _mouseCursorResource.setCursorNum(1); + } else { + _mouseCursorResource.setCursorNum(4); + } + break; + case kMouseType435: + if (_x <= _x1) { + _mouseCursorResource.setCursorNum(6); + } else if (_x >= _x2) { + _mouseCursorResource.setCursorNum(5); + } else { + _mouseCursorResource.setCursorNum(4); + } + break; + case kMouseTypeNavigation: + switch (_type) { + case 1: + if (_x >= 320) + _mouseCursorResource.setCursorNum(5); + else + _mouseCursorResource.setCursorNum(6); + break; + case 2: + default: + if (_x < 100) + _mouseCursorResource.setCursorNum(6); + else if (_x > 540) + _mouseCursorResource.setCursorNum(5); + else + _mouseCursorResource.setCursorNum(0); + break; + case 3: + if (_x < 100) + _mouseCursorResource.setCursorNum(1); + else if (_x > 540) + _mouseCursorResource.setCursorNum(1); + break; + case 4: + if (_x < 100) + _mouseCursorResource.setCursorNum(6); + else if (_x > 540) + _mouseCursorResource.setCursorNum(5); + else if (_y >= 150) + _mouseCursorResource.setCursorNum(0); + else + _mouseCursorResource.setCursorNum(3); + break; + case 5: + if (_y >= 240) + _mouseCursorResource.setCursorNum(2); + else + _mouseCursorResource.setCursorNum(3); + break; + } + break; + } + +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/mouse.h b/engines/neverhood/mouse.h new file mode 100644 index 0000000000..0b4f6b81db --- /dev/null +++ b/engines/neverhood/mouse.h @@ -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 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef NEVERHOOD_MOUSE_H +#define NEVERHOOD_MOUSE_H + +#include "neverhood/neverhood.h" +#include "neverhood/sprite.h" +#include "neverhood/graphics.h" +#include "neverhood/resource.h" + +namespace Neverhood { + +enum MouseType { + kMouseType433, + kMouseType435, + kMouseTypeNavigation +}; + +class Mouse : public StaticSprite { +public: + Mouse(NeverhoodEngine *vm, uint32 fileHash, const NRect &mouseRect); + Mouse(NeverhoodEngine *vm, uint32 fileHash, int16 x1, int16 x2); + Mouse(NeverhoodEngine *vm, uint32 fileHash, int _type); + void load(uint32 fileHash); + void updateCursor(); +protected: + MouseType _mouseType; + MouseCursorResource _mouseCursorResource; + int _frameNum; + NRect _mouseRect; + int16 _x1; + int16 _x2; + int _type; + void init(uint32 fileHash); + void update(); + void updateCursorNum(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_MOUSE_H */ diff --git a/engines/neverhood/navigationscene.cpp b/engines/neverhood/navigationscene.cpp new file mode 100644 index 0000000000..7a3161cb47 --- /dev/null +++ b/engines/neverhood/navigationscene.cpp @@ -0,0 +1,217 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "neverhood/navigationscene.h" +#include "neverhood/mouse.h" + +namespace Neverhood { + +NavigationScene::NavigationScene(NeverhoodEngine *vm, Module *parentModule, uint32 navigationListId, int navigationIndex, const byte *itemsTypes) + : Scene(vm, parentModule, true), _itemsTypes(itemsTypes), _navigationIndex(navigationIndex), _smackerDone(false), + _soundFlag1(false), _soundFlag2(false), _smackerFileHash(0), _interactive(true), _done(false) { + + _navigationList = _vm->_staticData->getNavigationList(navigationListId); + for (NavigationList::iterator it = _navigationList->begin(); it != _navigationList->end(); it++) { + debug("%08X %08X %08X %08X %d %d %08X", (*it).fileHash, (*it).leftSmackerFileHash, (*it).rightSmackerFileHash, + (*it).middleSmackerFileHash, (*it).interactive, (*it).middleFlag, (*it).mouseCursorFileHash); + } + + if (_navigationIndex < 0) { + _navigationIndex = (int)getGlobalVar(0x4200189E); + if (_navigationIndex >= (int)_navigationList->size()) + _navigationIndex = 0; + } + setGlobalVar(0x4200189E, _navigationIndex); + + SetUpdateHandler(&NavigationScene::update); + SetMessageHandler(&NavigationScene::handleMessage); + + _smackerPlayer = new SmackerPlayer(_vm, this, (*_navigationList)[_navigationIndex].fileHash, true, true); + + addEntity(_smackerPlayer); + + addSurface(_smackerPlayer->getSurface()); + + createMouseCursor(); + + _vm->_screen->clear(); + + sendMessage(_parentModule, 0x100A, _navigationIndex); + +} + +NavigationScene::~NavigationScene() { + // TODO Sound1ChList_sub_4080B0(0); + // TODO Sound1ChList_sub_408110(0); +} + +int NavigationScene::getNavigationAreaType() { + NPoint mousePos; + mousePos.x = _mouseCursor->getX(); + mousePos.y = _mouseCursor->getY(); + return sendPointMessage(_mouseCursor, 0x2064, mousePos); +} + +void NavigationScene::update() { + if (_smackerFileHash != 0) { + showMouse(false); + _smackerPlayer->open(_smackerFileHash, false); + _vm->_screen->clear(); + _smackerDone = false; + _smackerFileHash = 0; + } else if (_smackerDone) { + if (_done) { + sendMessage(_parentModule, 0x1009, _navigationIndex); + } else { + const NavigationItem &navigationItem = (*_navigationList)[_navigationIndex]; + createMouseCursor(); + showMouse(true); + _soundFlag2 = false; + _soundFlag1 = false; + _interactive = true; + // TODO Sound1ChList_sub_4080B0(0); + // TODO Sound1ChList_sub_408110(0); + _smackerDone = false; + _smackerPlayer->open(navigationItem.fileHash, true); + _vm->_screen->clear(); + sendMessage(_parentModule, 0x100A, _navigationIndex); + } + } + Scene::update(); +} + +uint32 NavigationScene::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + switch (messageNum) { + case 0x0000: + if (_interactive) + sendMessage(_mouseCursor, 0x4002, param); + break; + case 0x0001: + if (_interactive) + handleNavigation(param.asPoint()); + break; + case 0x0009: + if (!_interactive) + _smackerDone = true; + break; + case 0x3002: + _smackerDone = true; + break; + } + return 0; +} + +void NavigationScene::createMouseCursor() { + + const NavigationItem &navigationItem = (*_navigationList)[_navigationIndex]; + uint32 mouseCursorFileHash; + int areaType; + + if (_mouseCursor) { + deleteSprite((Sprite**)&_mouseCursor); + } + + mouseCursorFileHash = navigationItem.mouseCursorFileHash; + // TODO: Check the resource... + if (mouseCursorFileHash == 0) + mouseCursorFileHash = 0x63A40028; + + if (_itemsTypes) { + areaType = _itemsTypes[_navigationIndex]; + } else if (navigationItem.middleSmackerFileHash != 0 || navigationItem.middleFlag) { + areaType = 0; + } else { + areaType = 1; + } + + insertNavigationMouse(mouseCursorFileHash, areaType); + sendPointMessage(_mouseCursor, 0x4002, _vm->getMousePos()); + +} + +void NavigationScene::handleNavigation(const NPoint &mousePos) { + + const NavigationItem &navigationItem = (*_navigationList)[_navigationIndex]; + bool oldSoundFlag1 = _soundFlag1; + bool oldSoundFlag2 = _soundFlag2; + uint32 direction = sendPointMessage(_mouseCursor, 0x2064, mousePos); + + switch (direction) { + // TODO: Merge cases 0 and 1? + case 0: + if (navigationItem.leftSmackerFileHash != 0) { + _smackerFileHash = navigationItem.leftSmackerFileHash; + _interactive = false; + _soundFlag1 = false; + _soundFlag2 = true; + do { + _navigationIndex--; + if (_navigationIndex < 0) + _navigationIndex = _navigationList->size() - 1; + } while (!(*_navigationList)[_navigationIndex].interactive); + setGlobalVar(0x4200189E, _navigationIndex); + } else { + sendMessage(_parentModule, 0x1009, _navigationIndex); + } + break; + case 1: + if (navigationItem.rightSmackerFileHash != 0) { + _smackerFileHash = navigationItem.rightSmackerFileHash; + _interactive = false; + _soundFlag1 = false; + _soundFlag2 = true; + do { + _navigationIndex++; + if (_navigationIndex >= (int)_navigationList->size()) + _navigationIndex = 0; + } while (!(*_navigationList)[_navigationIndex].interactive); + setGlobalVar(0x4200189E, _navigationIndex); + } else { + sendMessage(_parentModule, 0x1009, _navigationIndex); + } + break; + case 2: + case 3: + case 4: + if (navigationItem.middleFlag) { + sendMessage(_parentModule, 0x1009, _navigationIndex); + } else if (navigationItem.middleSmackerFileHash != 0) { + _smackerFileHash = navigationItem.middleSmackerFileHash; + _interactive = false; + _soundFlag1 = true; + _soundFlag2 = false; + _done = true; + } + break; + } + + if (oldSoundFlag2 != _soundFlag2) { + // TODO Sound1ChList_sub_408110(_soundFlag2); + } + + if (oldSoundFlag1 != _soundFlag1) { + // TODO Sound1ChList_sub_4080B0(_soundFlag1); + } + +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/navigationscene.h b/engines/neverhood/navigationscene.h new file mode 100644 index 0000000000..c37a7fc178 --- /dev/null +++ b/engines/neverhood/navigationscene.h @@ -0,0 +1,56 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef NEVERHOOD_NAVIGATIONSCENE_H +#define NEVERHOOD_NAVIGATIONSCENE_H + +#include "neverhood/neverhood.h" +#include "neverhood/resourceman.h" +#include "neverhood/scene.h" + +namespace Neverhood { + +class NavigationScene : public Scene { +public: + NavigationScene(NeverhoodEngine *vm, Module *parentModule, uint32 navigationListId, int navigationIndex, const byte *itemsTypes); + virtual ~NavigationScene(); + int getNavigationAreaType(); +protected: + SmackerPlayer *_smackerPlayer; + bool _smackerDone; + NavigationList *_navigationList; + int _navigationIndex; + uint32 _smackerFileHash; + bool _interactive; + bool _soundFlag1; + bool _soundFlag2; + bool _done; + const byte *_itemsTypes; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void createMouseCursor(); + void handleNavigation(const NPoint &mousePos); +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_NAVIGATIONSCENE_H */ diff --git a/engines/neverhood/neverhood.cpp b/engines/neverhood/neverhood.cpp new file mode 100644 index 0000000000..f1f4bab9cd --- /dev/null +++ b/engines/neverhood/neverhood.cpp @@ -0,0 +1,248 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/file.h" +#include "common/config-manager.h" +#include "base/plugins.h" +#include "base/version.h" +#include "graphics/cursorman.h" +#include "engines/util.h" +#include "neverhood/neverhood.h" +#include "neverhood/blbarchive.h" +#include "neverhood/collisionman.h" +#include "neverhood/gamemodule.h" +#include "neverhood/gamevars.h" +#include "neverhood/graphics.h" +#include "neverhood/resourceman.h" +#include "neverhood/resource.h" +#include "neverhood/screen.h" +#include "neverhood/staticdata.h" + +namespace Neverhood { + +NeverhoodEngine::NeverhoodEngine(OSystem *syst, const NeverhoodGameDescription *gameDesc) : Engine(syst), _gameDescription(gameDesc) { + // Setup mixer + if (!_mixer->isReady()) { + warning("Sound initialization failed."); + } + + _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume")); + _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume")); + + _rnd = new Common::RandomSource("neverhood"); + +} + +NeverhoodEngine::~NeverhoodEngine() { + delete _rnd; +} + +Common::Error NeverhoodEngine::run() { + initGraphics(640, 480, true); + + _isSaveAllowed = false; + + _mouseX = 0; + _mouseY = 0; + + _gameState.sceneNum = 0; + _gameState.field2 = 0; + + _staticData = new StaticData(); + _staticData->load("neverhood.dat"); + + _gameVars = new GameVars(); + + _screen = new Screen(this); + + _res = new ResourceMan(); + _res->addArchive("a.blb"); + _res->addArchive("c.blb"); + _res->addArchive("hd.blb"); + _res->addArchive("i.blb"); + _res->addArchive("m.blb"); + _res->addArchive("s.blb"); + _res->addArchive("t.blb"); + + CursorMan.showMouse(true); + { + // DEBUG: Dummy cursor + byte buffer[2*2]; + memset(buffer, 255, 4); + CursorMan.replaceCursor(buffer, 2, 2, 0, 0, 0); + } + +#if 0 + // TODO: This should probably be implemented as debug command later + dumpAllResources(); +#endif + +#if 1 + + _collisionMan = new CollisionMan(this); + _gameModule = new GameModule(this); + + _gameModule->startup(); + + // Preliminary main loop, needs some more work but works for testing + while (!shouldQuit()) { + Common::Event event; + Common::EventManager *eventMan = _system->getEventManager(); + + while (eventMan->pollEvent(event)) { + switch (event.type) { + case Common::EVENT_KEYDOWN: + _keyState = event.kbd.keycode; + break; + case Common::EVENT_KEYUP: + _keyState = Common::KEYCODE_INVALID; + break; + case Common::EVENT_MOUSEMOVE: + _mouseX = event.mouse.x; + _mouseY = event.mouse.y; + _gameModule->handleMouseMove(event.mouse.x, event.mouse.y); + break; + case Common::EVENT_LBUTTONDOWN: + case Common::EVENT_RBUTTONDOWN: + _gameModule->handleMouseDown(event.mouse.x, event.mouse.y); + break; + case Common::EVENT_QUIT: + _system->quit(); + break; + default: + break; + } + } + + //debug("millis %d", _system->getMillis()); + _gameModule->handleUpdate(); + _gameModule->draw(); + _screen->wait(); + _screen->update(); + + debug(0, "---------------------------------------"); + + } + + delete _gameModule; + delete _collisionMan; +#endif + + + delete _res; + delete _screen; + + delete _gameVars; + delete _staticData; + + debug("Ok."); + + return Common::kNoError; +} + +NPoint NeverhoodEngine::getMousePos() { + NPoint pt; + pt.x = _mouseX; + pt.y = _mouseY; + return pt; +} + +void writeTga(const char *filename, byte *pixels, byte *palette, int16 width, int16 height) { + byte identsize = 0; + byte colourmaptype = 1; + byte imagetype = 1; + uint16 colourmapstart = 0; + uint16 colourmaplength = 256; + byte colourmapbits = 24; + uint16 xstart = 0; + uint16 ystart = 0; + byte bits = 8; + byte descriptor = 0x20; + Common::DumpFile tga; + tga.open(filename); + tga.writeByte(identsize); + tga.writeByte(colourmaptype); + tga.writeByte(imagetype); + tga.writeUint16LE(colourmapstart); + tga.writeUint16LE(colourmaplength); + tga.writeByte(colourmapbits); + tga.writeUint16LE(xstart); + tga.writeUint16LE(ystart); + tga.writeUint16LE(width); + tga.writeUint16LE(height); + tga.writeByte(bits); + tga.writeByte(descriptor); + tga.write(palette, 768); + tga.write(pixels, width * height); + tga.close(); +} + +void NeverhoodEngine::dumpAllResources() { + + PaletteResource paletteResource(this); + byte *vgaPalette = new byte[768]; + paletteResource.load(0x4086520E); + byte *srcpalette = paletteResource.palette(); + for (int i = 0; i < 256; i++) { + vgaPalette[i * 3 + 2] = srcpalette[i * 4 + 0]; + vgaPalette[i * 3 + 1] = srcpalette[i * 4 + 1]; + vgaPalette[i * 3 + 0] = srcpalette[i * 4 + 2]; + } + +#if 0 + for (int i = 0; i < 768; i++) + vgaPalette[i] <<= 2; +#endif + + uint entriesCount = _res->getEntryCount(); + debug("%d entries", entriesCount); + + for (uint i = 0; i < entriesCount; i++) { + const ResourceFileEntry &entry = _res->getEntry(i); + int type = _res->getResourceTypeByHash(entry.fileHash); + debug("hash: %08X; type: %d", entry.fileHash, type); + if (type == 4) { + AnimResource anim(this); + anim.load(entry.fileHash); + for (uint frameIndex = 0; frameIndex < anim.getFrameCount(); frameIndex++) { + const AnimFrameInfo &frameInfo = anim.getFrameInfo(frameIndex); + int16 width = (frameInfo.rect.width + 3) & 0xFFFC; + byte *pixels = new byte[width * frameInfo.rect.height]; + memset(pixels, 0, width * frameInfo.rect.height); + anim.draw(frameIndex, pixels, width, false, false); + Common::String filename = + frameInfo.frameHash != 0 + ? Common::String::format("%08X_%03d_%08X.tga", entry.fileHash, frameIndex, frameInfo.frameHash) + : Common::String::format("%08X_%03d.tga", entry.fileHash, frameIndex); + writeTga(filename.c_str(), pixels, vgaPalette, width, frameInfo.rect.height); + delete[] pixels; + } + static int n = 0; + //if (n++ == 25) break; + } + } + + delete[] vgaPalette; + +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/neverhood.h b/engines/neverhood/neverhood.h new file mode 100644 index 0000000000..adc46198d6 --- /dev/null +++ b/engines/neverhood/neverhood.h @@ -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 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef NEVERHOOD_H +#define NEVERHOOD_H + +#include "common/scummsys.h" +#include "common/events.h" +#include "common/keyboard.h" +#include "common/random.h" +#include "common/savefile.h" +#include "common/system.h" +#include "audio/mixer.h" +#include "engines/engine.h" + +namespace Neverhood { + +enum NeverhoodGameFeatures { +}; + +struct NeverhoodGameDescription; + +class CollisionMan; +class GameModule; +class GameVars; +class ResourceMan; +class Screen; +class StaticData; +struct NPoint; + +struct GameState { + int sceneNum; + int which; + int field2; +}; + +class NeverhoodEngine : public ::Engine { +protected: + + Common::Error run(); + +public: + NeverhoodEngine(OSystem *syst, const NeverhoodGameDescription *gameDesc); + virtual ~NeverhoodEngine(); + + // Detection related functions + const NeverhoodGameDescription *_gameDescription; + const char *getGameId() const; + uint32 getFeatures() const; + uint16 getVersion() const; + Common::Platform getPlatform() const; + bool hasFeature(EngineFeature f) const; + + Common::RandomSource *_rnd; + + int16 _mouseX, _mouseY; + Common::KeyCode _keyState; + uint16 _buttonState; + + GameState _gameState; + GameVars *_gameVars; + Screen *_screen; + ResourceMan *_res; + GameModule *_gameModule; + StaticData *_staticData; + CollisionMan *_collisionMan; + +public: + + /* Save/load */ + + enum kReadSaveHeaderError { + kRSHENoError = 0, + kRSHEInvalidType = 1, + kRSHEInvalidVersion = 2, + kRSHEIoError = 3 + }; + + struct SaveHeader { + Common::String description; + uint32 version; + byte gameID; + uint32 flags; + Graphics::Surface *thumbnail; + }; + + bool _isSaveAllowed; + + bool canLoadGameStateCurrently() { return _isSaveAllowed; } + bool canSaveGameStateCurrently() { return _isSaveAllowed; } + +#if 0 // Not used yet but let's keep it for later when it is + Common::Error loadGameState(int slot); + Common::Error saveGameState(int slot, const Common::String &description); + void savegame(const char *filename, const char *description); + void loadgame(const char *filename); + const char *getSavegameFilename(int num); + static Common::String getSavegameFilename(const Common::String &target, int num); + static kReadSaveHeaderError readSaveHeader(Common::SeekableReadStream *in, bool loadThumbnail, SaveHeader &header); +#endif + + GameState& gameState() { return _gameState; } + GameModule *gameModule() { return _gameModule; } + int16 getMouseX() const { return _mouseX; } + int16 getMouseY() const { return _mouseY; } + NPoint getMousePos(); + + void dumpAllResources(); + +public: + +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_H */ diff --git a/engines/neverhood/palette.cpp b/engines/neverhood/palette.cpp new file mode 100644 index 0000000000..481e0e5058 --- /dev/null +++ b/engines/neverhood/palette.cpp @@ -0,0 +1,177 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "neverhood/palette.h" +#include "neverhood/resource.h" +#include "neverhood/screen.h" + +namespace Neverhood { + +// Palette + +Palette::Palette(NeverhoodEngine *vm) : Entity(vm, 0) { + init(); + memset(_palette, 0, 1024); + SetUpdateHandler(&Palette::update); +} + +Palette::Palette(NeverhoodEngine *vm, byte *palette) : Entity(vm, 0) { + init(); + memcpy(_palette, palette, 1024); + SetUpdateHandler(&Palette::update); +} + +Palette::Palette(NeverhoodEngine *vm, const char *filename) : Entity(vm, 0) { + PaletteResource paletteResource(_vm); + init(); + paletteResource.load(calcHash(filename)); + paletteResource.copyPalette(_palette); + SetUpdateHandler(&Palette::update); +} + +Palette::Palette(NeverhoodEngine *vm, uint32 fileHash) : Entity(vm, 0) { + PaletteResource paletteResource(_vm); + init(); + paletteResource.load(fileHash); + paletteResource.copyPalette(_palette); + SetUpdateHandler(&Palette::update); +} + +Palette::~Palette() { + _vm->_screen->unsetPaletteData(_palette); + delete[] _palette; + delete[] _basePalette; +} + +void Palette::init() { + _status = 0; + _palette = new byte[1024]; + _basePalette = new byte[1024]; +} + +void Palette::usePalette() { + _vm->_screen->setPaletteData(_palette); +} + +void Palette::addPalette(const char *filename, int toIndex, int count, int fromIndex) { + addPalette(calcHash(filename), toIndex, count, fromIndex); +} + +void Palette::addPalette(uint32 fileHash, int toIndex, int count, int fromIndex) { + PaletteResource paletteResource(_vm); + if (toIndex + count > 256) + count = 256 - toIndex; + paletteResource.load(fileHash); + memcpy(_palette + toIndex * 4, paletteResource.palette() + fromIndex * 4, count * 4); + _vm->_screen->testPalette(_palette); +} + +void Palette::addBasePalette(uint32 fileHash, int toIndex, int count, int fromIndex) { + PaletteResource paletteResource(_vm); + if (toIndex + count > 256) + count = 256 - toIndex; + paletteResource.load(fileHash); + memcpy(_basePalette + toIndex * 4, paletteResource.palette() + fromIndex * 4, count * 4); +} + +void Palette::copyPalette(const byte *palette, int toIndex, int count, int fromIndex) { + if (toIndex + count > 256) + count = 256 - toIndex; + memcpy(_palette + toIndex * 4, palette + fromIndex * 4, count * 4); + _vm->_screen->testPalette(_palette); +} + +void Palette::copyBasePalette(int toIndex, int count, int fromIndex) { + if (toIndex + count > 256) + count = 256 - toIndex; + memcpy(_basePalette + toIndex * 4, _palette + fromIndex * 4, count * 4); +} + +void Palette::startFadeToBlack(int counter) { + debug(2, "Palette::startFadeToBlack(%d)", counter); + if (counter == 0) + counter = 1; + _fadeToR = 0; + _fadeToG = 0; + _fadeToB = 0; + _palCounter = counter; + _fadeStep = 255 / counter; + _status = 1; +} + +void Palette::startFadeToWhite(int counter) { + debug(2, "Palette::startFadeToWhite(%d)", counter); + if (counter == 0) + counter = 1; + _fadeToR = 255; + _fadeToG = 255; + _fadeToB = 255; + _palCounter = counter; + _fadeStep = 255 / counter; + _status = 1; +} + +void Palette::startFadeToPalette(int counter) { + debug(2, "Palette::startFadeToPalette(%d)", counter); + if (counter == 0) + counter = 1; + _palCounter = counter; + _fadeStep = 255 / counter; + _status = 2; +} + +void Palette::update() { + debug(2, "Palette::update() _status = %d", _status); + if (_status == 1) { + if (_palCounter > 1) { + for (int i = 0; i < 256; i++) { + fadeColor(_palette + i * 4, _fadeToR, _fadeToG, _fadeToB); + } + _vm->_screen->testPalette(_palette); + _palCounter--; + } else { + memset(_palette, 0, 1024); + _status = 0; + } + } else if (_status == 2) { + if (_palCounter > 1) { + for (int i = 0; i < 256; i++) { + fadeColor(_palette + i * 4, _basePalette[i * 4 + 0], _basePalette[i * 4 + 1], _basePalette[i * 4 + 2]); + } + _vm->_screen->testPalette(_palette); + _palCounter--; + } else { + memcpy(_palette, _basePalette, 256 * 4); + _status = 0; + } + } +} + +void Palette::fadeColor(byte *rgb, byte toR, byte toG, byte toB) { + #define FADE(color, toColor) color += _fadeStep < toColor - color ? _fadeStep : (-_fadeStep <= toColor - color ? toColor - color : -_fadeStep) + FADE(rgb[0], toR); + FADE(rgb[1], toG); + FADE(rgb[2], toB); + #undef FADE +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/palette.h b/engines/neverhood/palette.h new file mode 100644 index 0000000000..79660130e1 --- /dev/null +++ b/engines/neverhood/palette.h @@ -0,0 +1,65 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef NEVERHOOD_PALETTE_H +#define NEVERHOOD_PALETTE_H + +#include "neverhood/neverhood.h" +#include "neverhood/entity.h" + +namespace Neverhood { + +class Palette : public Entity { +public: + // Default constructor with black palette + Palette(NeverhoodEngine *vm); + // Create from existing palette + Palette(NeverhoodEngine *vm, byte *palette); + // Create from resource with filename + Palette(NeverhoodEngine *vm, const char *filename); + // Create from resource with fileHash + Palette(NeverhoodEngine *vm, uint32 fileHash); + virtual ~Palette(); + void init(); + void usePalette(); + void addPalette(const char *filename, int toIndex, int count, int fromIndex); + void addPalette(uint32 fileHash, int toIndex, int count, int fromIndex); + void addBasePalette(uint32 fileHash, int toIndex, int count, int fromIndex); + void copyPalette(const byte *palette, int toIndex, int count, int fromIndex); + void copyBasePalette(int toIndex, int count, int fromIndex); + void startFadeToBlack(int counter); + void startFadeToWhite(int counter); + void startFadeToPalette(int counter); +protected: + int _status; + byte *_palette; + byte *_basePalette; + int _palCounter; + byte _fadeToR, _fadeToG, _fadeToB; + int _fadeStep; + void update(); + void fadeColor(byte *rgb, byte toR, byte toG, byte toB); +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_PALETTE_H */ diff --git a/engines/neverhood/resource.cpp b/engines/neverhood/resource.cpp new file mode 100644 index 0000000000..9678031554 --- /dev/null +++ b/engines/neverhood/resource.cpp @@ -0,0 +1,679 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/memstream.h" +#include "neverhood/resource.h" +#include "neverhood/resourceman.h" + +namespace Neverhood { + +// TODO: Since the load() methods are similar in most cases some of the code therein +// can probably be copied into another method (e.g. inside the resource manager) +// to reduce code. + +// SpriteResource + +SpriteResource::SpriteResource(NeverhoodEngine *vm) + : _vm(vm), _resourceHandle(-1), _pixels(NULL) { +} + +SpriteResource::~SpriteResource() { + unload(); +} + +void SpriteResource::draw(byte *dest, int destPitch, bool flipX, bool flipY) { + if (_pixels) { + if (_rle) { + unpackSpriteRle(_pixels, _dimensions.width, _dimensions.height, dest, destPitch, flipX, flipY); + } else { + unpackSpriteNormal(_pixels, _dimensions.width, _dimensions.height, dest, destPitch, flipX, flipY); + } + } +} + +bool SpriteResource::load(uint32 fileHash) { + debug(2, "SpriteResource::load(%08X)", fileHash); + // TODO: Later merge with load2 and make the mode a parameter + unload(); + _resourceHandle = _vm->_res->useResource(fileHash); + debug(2, "SpriteResource::load(0x%08X) _resourceHandle = %d", fileHash, _resourceHandle); + if (_resourceHandle != -1) { + if (_vm->_res->getResourceType(_resourceHandle) == 2) { + byte *spriteData = _vm->_res->loadResource(_resourceHandle, true); + parseBitmapResource(spriteData, &_rle, &_dimensions, NULL, NULL, &_pixels); + } else { + _vm->_res->unuseResource(_resourceHandle); + _resourceHandle = -1; + } + } + return _pixels != NULL; +} + +bool SpriteResource::load2(uint32 fileHash) { + unload(); + _resourceHandle = _vm->_res->useResource(fileHash); + if (_resourceHandle != -1) { + if (_vm->_res->getResourceType(_resourceHandle) == 2) { + byte *spriteData = _vm->_res->loadResource(_resourceHandle, true); + parseBitmapResource(spriteData, &_rle, &_dimensions, &_position, NULL, &_pixels); + } else { + _vm->_res->unuseResource(_resourceHandle); + _resourceHandle = -1; + } + } + return _pixels != NULL; +} + +void SpriteResource::unload() { + if (_resourceHandle != -1) { + _vm->_res->unloadResource(_resourceHandle); + _vm->_res->unuseResource(_resourceHandle); + _resourceHandle = -1; + } else { + delete[] _pixels; + } + _pixels = NULL; + _rle = false; +} + +// PaletteResource + +PaletteResource::PaletteResource(NeverhoodEngine *vm) + : _vm(vm), _resourceHandle(-1), _palette(NULL) { +} + +PaletteResource::~PaletteResource() { + unload(); +} + +bool PaletteResource::load(uint32 fileHash) { + debug(2, "PaletteResource::load(%08X)", fileHash); + unload(); + _resourceHandle = _vm->_res->useResource(fileHash); + if (_resourceHandle != -1) { + _palette = _vm->_res->loadResource(_resourceHandle, true); + switch (_vm->_res->getResourceType(_resourceHandle)) { + case 2: + // Palette is stored in a bitmap + parseBitmapResource(_palette, NULL, NULL, NULL, &_palette, NULL); + break; + case 3: + // _palette already points to the correct data + break; + default: + _vm->_res->unuseResource(_resourceHandle); + _resourceHandle = -1; + break; + } + } + return _palette != NULL; +} + +void PaletteResource::unload() { + if (_resourceHandle != -1) { + _vm->_res->unloadResource(_resourceHandle); + _vm->_res->unuseResource(_resourceHandle); + _resourceHandle = -1; + _palette = NULL; + } +} + +void PaletteResource::copyPalette(byte *destPalette) { + if (_palette) { + memcpy(destPalette, _palette, 1024); + } else { + // TODO?: buildDefaultPalette(palette); + } +} + +// AnimResource + +AnimResource::AnimResource(NeverhoodEngine *vm) + : _vm(vm), _width(0), _height(0), _currSpriteData(NULL) { + + clear(); + clear2(); +} + +AnimResource::~AnimResource() { + unloadInternal(); +} + +void AnimResource::draw(uint frameIndex, byte *dest, int destPitch, bool flipX, bool flipY) { + const AnimFrameInfo frameInfo = _frames[frameIndex]; + _currSpriteData = _spriteData + frameInfo.spriteDataOffs; + _width = frameInfo.rect.width; + _height = frameInfo.rect.height; + if (_replEnabled && _replOldColor != _replNewColor) + unpackSpriteRleRepl(_currSpriteData, _width, _height, dest, destPitch, _replOldColor, _replNewColor, flipX, flipY); + else + unpackSpriteRle(_currSpriteData, _width, _height, dest, destPitch, flipX, flipY); +} + +bool AnimResource::load(uint32 fileHash) { + debug(2, "AnimResource::load(%08X)", fileHash); + + if (fileHash == _fileHash) + return true; + + unload(); + _resourceHandle = _vm->_res->useResource(fileHash); + if (_resourceHandle == -1) + return false; + + byte *resourceData, *animList, *frameList; + uint16 animInfoStartOfs, animListIndex, animListCount; + uint16 frameListStartOfs, frameCount; + uint32 spriteDataOfs, paletteDataOfs; + + if (_vm->_res->getResourceType(_resourceHandle) != 4) { + _vm->_res->unuseResource(_resourceHandle); + _resourceHandle = -1; + return false; + } + + resourceData = _vm->_res->loadResource(_resourceHandle); + if (!resourceData) { + _vm->_res->unuseResource(_resourceHandle); + _resourceHandle = -1; + return false; + } + + animListCount = READ_LE_UINT16(resourceData); + animInfoStartOfs = READ_LE_UINT16(resourceData + 2); + spriteDataOfs = READ_LE_UINT32(resourceData + 4); + paletteDataOfs = READ_LE_UINT32(resourceData + 8); + + animList = resourceData + 12; + for (animListIndex = 0; animListIndex < animListCount; animListIndex++) { + debug(8, "hash: %08X", READ_LE_UINT32(animList)); + if (READ_LE_UINT32(animList) == fileHash) + break; + animList += 8; + } + + if (animListIndex >= animListCount) { + _vm->_res->unloadResource(_resourceHandle); + _vm->_res->unuseResource(_resourceHandle); + _resourceHandle = -1; + return false; + } + + _spriteData = resourceData + spriteDataOfs; + if (paletteDataOfs > 0) + _paletteData = resourceData + paletteDataOfs; + + frameCount = READ_LE_UINT16(animList + 4); + frameListStartOfs = READ_LE_UINT16(animList + 6); + + debug(8, "frameCount = %d; frameListStartOfs = %04X; animInfoStartOfs = %04X", frameCount, frameListStartOfs, animInfoStartOfs); + + frameList = resourceData + animInfoStartOfs + frameListStartOfs; + + _frames.clear(); + _frames.reserve(frameCount); + + for (uint16 frameIndex = 0; frameIndex < frameCount; frameIndex++) { + AnimFrameInfo frameInfo; + frameInfo.frameHash = READ_LE_UINT32(frameList); + frameInfo.counter = READ_LE_UINT16(frameList + 4); + frameInfo.rect.x = READ_LE_UINT16(frameList + 6); + frameInfo.rect.y = READ_LE_UINT16(frameList + 8); + frameInfo.rect.width = READ_LE_UINT16(frameList + 10); + frameInfo.rect.height = READ_LE_UINT16(frameList + 12); + frameInfo.deltaX = READ_LE_UINT16(frameList + 14); + frameInfo.deltaY = READ_LE_UINT16(frameList + 16); + frameInfo.deltaRect.x = READ_LE_UINT16(frameList + 18); + frameInfo.deltaRect.y = READ_LE_UINT16(frameList + 20); + frameInfo.deltaRect.width = READ_LE_UINT16(frameList + 22); + frameInfo.deltaRect.height = READ_LE_UINT16(frameList + 24); + frameInfo.field_1A = READ_LE_UINT16(frameList + 26); + frameInfo.spriteDataOffs = READ_LE_UINT32(frameList + 28); + debug(8, "frameHash = %08X; counter = %d; rect = (%d,%d,%d,%d); deltaX = %d; deltaY = %d; deltaRect = (%d,%d,%d,%d); field_1A = %04X; spriteDataOffs = %08X", + frameInfo.frameHash, frameInfo.counter, + frameInfo.rect.x, frameInfo.rect.y, frameInfo.rect.width, frameInfo.rect.height, + frameInfo.deltaX, frameInfo.deltaY, + frameInfo.deltaRect.x, frameInfo.deltaRect.y, frameInfo.deltaRect.width, frameInfo.deltaRect.height, + frameInfo.field_1A, frameInfo.spriteDataOffs); + frameList += 32; + _frames.push_back(frameInfo); + } + + _fileHash = fileHash; + + return true; + +} + +void AnimResource::unload() { + if (_resourceHandle != -1) { + _vm->_res->unloadResource(_resourceHandle); + _vm->_res->unuseResource(_resourceHandle); + clear(); + } +} + +void AnimResource::clear() { + _resourceHandle = -1; + _currSpriteData = NULL; + _fileHash = 0; + _paletteData = NULL; + _spriteData = NULL; +} + +void AnimResource::clear2() { + clear(); + _replEnabled = true; + _replOldColor = 0; + _replNewColor = 0; +} + +bool AnimResource::loadInternal(uint32 fileHash) { + unloadInternal(); + return load(fileHash); +} + +void AnimResource::unloadInternal() { + unload(); + clear2(); +} + +int16 AnimResource::getFrameIndex(uint32 frameHash) { + int16 frameIndex = -1; + for (uint i = 0; i < _frames.size(); i++) + if (_frames[i].frameHash == frameHash) { + frameIndex = (int16)i; + break; + } + debug(2, "AnimResource::getFrameIndex(%08X) -> %d", frameHash, frameIndex); + return frameIndex; +} + +void AnimResource::setRepl(byte oldColor, byte newColor) { + _replOldColor = oldColor; + _replNewColor = newColor; +} + +NDimensions AnimResource::loadSpriteDimensions(uint32 fileHash) { + NDimensions dimensions; + byte *resDimensions = _vm->_res->getResourceExtDataByHash(fileHash); + if (resDimensions) { + dimensions.width = READ_LE_UINT16(resDimensions + 0); + dimensions.height = READ_LE_UINT16(resDimensions + 2); + } else { + dimensions.width = 0; + dimensions.height = 0; + } + return dimensions; +} + +// MouseCursorResource + +MouseCursorResource::MouseCursorResource(NeverhoodEngine *vm) + : _cursorSprite(vm), _cursorNum(4), _currFileHash(0) { + + _rect.width = 32; + _rect.height = 32; +} + +void MouseCursorResource::load(uint32 fileHash) { + if (_currFileHash != fileHash) { + if (_cursorSprite.load(fileHash) && !_cursorSprite.isRle() && + _cursorSprite.getDimensions().width == 96 && _cursorSprite.getDimensions().height == 224) { + _currFileHash = fileHash; + } else { + unload(); + } + } +} + +void MouseCursorResource::unload() { + _cursorSprite.unload(); + _currFileHash = 0; + _cursorNum = 4; +} + +NDrawRect& MouseCursorResource::getRect() { + static const NPoint kCursorHotSpots[] = { + {-15, -5}, + {-17, -25}, + {-17, -30}, + {-14, -1}, + {-3, -7}, + {-30, -18}, + {-1, -18} + }; + _rect.x = kCursorHotSpots[_cursorNum].x; + _rect.y = kCursorHotSpots[_cursorNum].y; + return _rect; +} + +void MouseCursorResource::draw(int frameNum, byte *dest, int destPitch) { + if (_cursorSprite.getPixels()) { + int sourcePitch = (_cursorSprite.getDimensions().width + 3) & 0xFFFC; // 4 byte alignment + byte *source = _cursorSprite.getPixels() + _cursorNum * (sourcePitch * 32) + frameNum * 32; + for (int16 yc = 0; yc < 32; yc++) { + memcpy(dest, source, 32); + source += sourcePitch; + dest += destPitch; + } + } +} + +// TextResource + +TextResource::TextResource(NeverhoodEngine *vm) + : _vm(vm), _resourceHandle(-1), _textData(NULL), _count(0) { + +} + +TextResource::~TextResource() { + unload(); +} + +void TextResource::load(uint32 fileHash) { + unload(); + _resourceHandle = _vm->_res->useResource(fileHash); + if (_resourceHandle != -1) { + if (_vm->_res->getResourceType(_resourceHandle) == 6) { + _textData = _vm->_res->loadResource(_resourceHandle, true); + _count = READ_LE_UINT32(_textData); + } else { + _vm->_res->unuseResource(_resourceHandle); + _resourceHandle = -1; + } + } +} + +void TextResource::unload() { + if (_resourceHandle != -1) { + _vm->_res->unloadResource(_resourceHandle); + _vm->_res->unuseResource(_resourceHandle); + _resourceHandle = -1; + _textData = NULL; + _count = 0; + } +} + +const char *TextResource::getString(uint index, const char *&textEnd) { + const char *textStart = (const char*)(_textData + 4 + _count * 4 + READ_LE_UINT32(_textData + (index + 1) * 4)); + textEnd = (const char*)(_textData + 4 + _count * 4 + READ_LE_UINT32(_textData + (index + 2) * 4)); + return textStart; +} + +// DataResource + +DataResource::DataResource(NeverhoodEngine *vm) + : _vm(vm), _resourceHandle(-1) { +} + +DataResource::~DataResource() { + unload(); +} + +void DataResource::load(uint32 fileHash) { + debug(2, "DataResource::load(%08X)", fileHash); + byte *data = NULL; + uint32 dataSize = 0; + unload(); + _resourceHandle = _vm->_res->useResource(fileHash); + if (_resourceHandle != -1) { + if (_vm->_res->getResourceType(_resourceHandle) == 5) { + data = _vm->_res->loadResource(_resourceHandle, true); + dataSize = _vm->_res->getResourceSize(_resourceHandle); + } else { + _vm->_res->unuseResource(_resourceHandle); + _resourceHandle = -1; + } + } + if (data && dataSize) { + Common::MemoryReadStream dataS(data, dataSize); + uint itemCount = dataS.readUint16LE(); + uint32 itemStartOffs = 2 + itemCount * 8; + debug(2, "itemCount = %d", itemCount); + for (uint i = 0; i < itemCount; i++) { + dataS.seek(2 + i * 8); + DRDirectoryItem drDirectoryItem; + drDirectoryItem.nameHash = dataS.readUint32LE(); + drDirectoryItem.offset = dataS.readUint16LE(); + drDirectoryItem.type = dataS.readUint16LE(); + debug(2, "%03d nameHash = %08X; offset = %04X; type = %d", i, drDirectoryItem.nameHash, drDirectoryItem.offset, drDirectoryItem.type); + dataS.seek(itemStartOffs + drDirectoryItem.offset); + switch (drDirectoryItem.type) { + case 1: + { + debug(3, "NPoint"); + NPoint point; + point.x = dataS.readUint16LE(); + point.y = dataS.readUint16LE(); + debug(3, "(%d, %d)", point.x, point.y); + drDirectoryItem.offset = _points.size(); + _points.push_back(point); + break; + } + case 2: + { + uint count = dataS.readUint16LE(); + NPointArray *pointArray = new NPointArray(); + debug(3, "NPointArray; count = %d", count); + for (uint j = 0; j < count; j++) { + NPoint point; + point.x = dataS.readUint16LE(); + point.y = dataS.readUint16LE(); + debug(3, "(%d, %d)", point.x, point.y); + pointArray->push_back(point); + } + drDirectoryItem.offset = _pointArrays.size(); + _pointArrays.push_back(pointArray); + break; + } + case 3: + { + uint count = dataS.readUint16LE(); + HitRectList *hitRectList = new HitRectList(); + debug(3, "HitRectList; count = %d", count); + for (uint j = 0; j < count; j++) { + HitRect hitRect; + hitRect.rect.x1 = dataS.readUint16LE(); + hitRect.rect.y1 = dataS.readUint16LE(); + hitRect.rect.x2 = dataS.readUint16LE(); + hitRect.rect.y2 = dataS.readUint16LE(); + hitRect.type = dataS.readUint16LE() + 0x5001; + debug(3, "(%d, %d, %d, %d) -> %04d", hitRect.rect.x1, hitRect.rect.y1, hitRect.rect.x2, hitRect.rect.y2, hitRect.type); + hitRectList->push_back(hitRect); + } + drDirectoryItem.offset = _hitRectLists.size(); + _hitRectLists.push_back(hitRectList); + break; + } + case 4: + { + uint count = dataS.readUint16LE(); + MessageList *messageList = new MessageList(); + debug(3, "MessageList; count = %d", count); + for (uint j = 0; j < count; j++) { + MessageItem messageItem; + messageItem.messageNum = dataS.readUint32LE(); + messageItem.messageValue = dataS.readUint32LE(); + debug(3, "(%08X, %08X)", messageItem.messageNum, messageItem.messageValue); + messageList->push_back(messageItem); + } + drDirectoryItem.offset = _messageLists.size(); + _messageLists.push_back(messageList); + break; + } + case 5: + { + uint count = dataS.readUint16LE(); + DRSubRectList *drSubRectList = new DRSubRectList(); + debug(3, "SubRectList; count = %d", count); + for (uint j = 0; j < count; j++) { + DRSubRect drSubRect; + drSubRect.rect.x1 = dataS.readUint16LE(); + drSubRect.rect.y1 = dataS.readUint16LE(); + drSubRect.rect.x2 = dataS.readUint16LE(); + drSubRect.rect.y2 = dataS.readUint16LE(); + drSubRect.messageListHash = dataS.readUint32LE(); + drSubRect.messageListItemIndex = dataS.readUint16LE(); + debug(3, "(%d, %d, %d, %d) -> %08X (%d)", drSubRect.rect.x1, drSubRect.rect.y1, drSubRect.rect.x2, drSubRect.rect.y2, drSubRect.messageListHash, drSubRect.messageListItemIndex); + drSubRectList->push_back(drSubRect); + } + drDirectoryItem.offset = _drSubRectLists.size(); + _drSubRectLists.push_back(drSubRectList); + break; + } + case 6: + { + DRRect drRect; + drRect.rect.x1 = dataS.readUint16LE(); + drRect.rect.y1 = dataS.readUint16LE(); + drRect.rect.x2 = dataS.readUint16LE(); + drRect.rect.y2 = dataS.readUint16LE(); + drRect.subRectIndex = dataS.readUint16LE(); + debug(3, "(%d, %d, %d, %d) -> %d", drRect.rect.x1, drRect.rect.y1, drRect.rect.x2, drRect.rect.y2, drRect.subRectIndex); + drDirectoryItem.offset = _drRects.size(); + _drRects.push_back(drRect); + break; + } + case 7: + { + uint count = dataS.readUint16LE(); + NRectArray *rectArray = new NRectArray(); + debug(3, "NRectArray; count = %d", count); + for (uint j = 0; j < count; j++) { + NRect rect; + rect.x1 = dataS.readUint16LE(); + rect.y1 = dataS.readUint16LE(); + rect.x2 = dataS.readUint16LE(); + rect.y2 = dataS.readUint16LE(); + debug(3, "(%d, %d, %d, %d)", rect.x1, rect.y1, rect.x2, rect.y2); + rectArray->push_back(rect); + } + drDirectoryItem.offset = _rectArrays.size(); + _rectArrays.push_back(rectArray); + break; + } + } + _directory.push_back(drDirectoryItem); + } + } +} + +void DataResource::unload() { + if (_resourceHandle != -1) { + _vm->_res->unloadResource(_resourceHandle); + _vm->_res->unuseResource(_resourceHandle); + _resourceHandle = -1; + // TODO: Clear arrays + } +} + +NPoint DataResource::getPoint(uint32 nameHash) { + DataResource::DRDirectoryItem *drDirectoryItem = findDRDirectoryItem(nameHash, 1); + if (drDirectoryItem) + return _points[drDirectoryItem->offset]; + return NPoint(); +} + +NPointArray *DataResource::getPointArray(uint32 nameHash) { + DataResource::DRDirectoryItem *drDirectoryItem = findDRDirectoryItem(nameHash, 2); + if (drDirectoryItem) + return _pointArrays[drDirectoryItem->offset]; + return NULL; +} + +NRectArray *DataResource::getRectArray(uint32 nameHash) { + DataResource::DRDirectoryItem *drDirectoryItem = findDRDirectoryItem(nameHash, 3); + if (drDirectoryItem) + return _rectArrays[drDirectoryItem->offset]; + return NULL; +} + +HitRectList *DataResource::getHitRectList() { + DataResource::DRDirectoryItem *drDirectoryItem = findDRDirectoryItem(calcHash("HitArray"), 3); + if (drDirectoryItem) + return _hitRectLists[drDirectoryItem->offset]; + return NULL; +} + +MessageList *DataResource::getMessageListAtPos(int16 klaymanX, int16 klaymanY, int16 mouseX, int16 mouseY) { + for (uint i = 0; i < _drRects.size(); i++) { + if (klaymanX >= _drRects[i].rect.x1 && klaymanX <= _drRects[i].rect.x2 && + klaymanY >= _drRects[i].rect.y1 && klaymanY <= _drRects[i].rect.y2) { + DRSubRectList *drSubRectList = _drSubRectLists[_drRects[i].subRectIndex]; + for (uint j = 0; j < drSubRectList->size(); j++) { + DRSubRect &subRect = (*drSubRectList)[j]; + if (mouseX >= subRect.rect.x1 && mouseX <= subRect.rect.x2 && + mouseY >= subRect.rect.y1 && mouseY <= subRect.rect.y2) { + return _messageLists[subRect.messageListItemIndex]; + } + } + } + } + return NULL; +} + +DataResource::DRDirectoryItem *DataResource::findDRDirectoryItem(uint32 nameHash, uint16 type) { + for (Common::Array<DRDirectoryItem>::iterator it = _directory.begin(); it != _directory.end(); it++) { + if ((*it).nameHash == nameHash && (*it).type == type) + return &(*it); + } + return NULL; +} + +// SoundResource +// ALL TODO + +SoundResource::SoundResource(NeverhoodEngine *vm) + : _vm(vm) { +} + +bool SoundResource::isPlaying() { + return false; +} + +void SoundResource::load(uint32 fileHash) { +} + +void SoundResource::play(uint32 fileHash, bool looping) { +} + +void SoundResource::play() { +} + +uint32 calcHash(const char *value) { + uint32 hash = 0, shiftValue = 0; + while (*value != 0) { + char ch = *value++; + if (ch >= 'a' && ch <= 'z') + ch -= 32; + else if (ch >= '0' && ch <= '9') + ch += 22; + shiftValue += ch - 64; + if (shiftValue >= 32) + shiftValue -= 32; + hash ^= 1 << shiftValue; + } + return hash; +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/resource.h b/engines/neverhood/resource.h new file mode 100644 index 0000000000..6436509228 --- /dev/null +++ b/engines/neverhood/resource.h @@ -0,0 +1,212 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef NEVERHOOD_RESOURCE_H +#define NEVERHOOD_RESOURCE_H + +#include "common/str.h" +#include "neverhood/neverhood.h" +#include "neverhood/graphics.h" +#include "neverhood/staticdata.h" + +namespace Neverhood { + +class SpriteResource { +public: + SpriteResource(NeverhoodEngine *vm); + ~SpriteResource(); + void draw(byte *dest, int destPitch, bool flipX, bool flipY); + bool load(uint32 fileHash); + bool load2(uint32 fileHash); + void unload(); + const NDimensions& getDimensions() { return _dimensions; } + NPoint& getPosition() { return _position; } + bool isRle() const { return _rle; } + byte *getPixels() const { return _pixels; } +protected: + NeverhoodEngine *_vm; + int _resourceHandle; + NDimensions _dimensions; + NPoint _position; + byte *_pixels; + bool _rle; +}; + +class PaletteResource { +public: + PaletteResource(NeverhoodEngine *vm); + ~PaletteResource(); + bool load(uint32 fileHash); + void unload(); + void copyPalette(byte *destPalette); + byte *palette() { return _palette; } +protected: + NeverhoodEngine *_vm; + int _resourceHandle; + byte *_palette; +}; + +struct AnimFrameInfo { + uint32 frameHash; + int16 counter; + NDrawRect rect; + int16 deltaX, deltaY; + NDrawRect deltaRect; + uint16 field_1A; + uint32 spriteDataOffs; +}; + +class AnimResource { +public: + AnimResource(NeverhoodEngine *vm); + ~AnimResource(); + void draw(uint frameIndex, byte *dest, int destPitch, bool flipX, bool flipY); + bool load(uint32 fileHash); + void unload(); + void clear(); + void clear2(); + bool loadInternal(uint32 fileHash); + void unloadInternal(); + uint getFrameCount() const { return _frames.size(); } + const AnimFrameInfo& getFrameInfo(int16 index) const { return _frames[index]; } + int16 getFrameIndex(uint32 frameHash); + void setReplEnabled(bool value) { _replEnabled = value; } + void setRepl(byte oldColor, byte newColor); + NDimensions loadSpriteDimensions(uint32 fileHash); +protected: + NeverhoodEngine *_vm; + int _resourceHandle; + int16 _width, _height; + byte *_currSpriteData; + uint32 _fileHash; + byte *_paletteData; + byte *_spriteData; + bool _replEnabled; + byte _replOldColor; + byte _replNewColor; + Common::Array<AnimFrameInfo> _frames; +}; + +class MouseCursorResource { +public: + MouseCursorResource(NeverhoodEngine *vm); + void load(uint32 fileHash); + void unload(); + NDrawRect& getRect(); + void draw(int frameNum, byte *dest, int destPitch); + int getCursorNum() const { return _cursorNum; } + void setCursorNum(int value) { _cursorNum = value; } +protected: + int _cursorNum; + SpriteResource _cursorSprite; + NDrawRect _rect; + uint32 _currFileHash; +}; + +class TextResource { +public: + TextResource(NeverhoodEngine *vm); + ~TextResource(); + void load(uint32 fileHash); + void unload(); + const char *getString(uint index, const char *&textEnd); + uint getCount() const { return _count;} +protected: + NeverhoodEngine *_vm; + int _resourceHandle; + byte *_textData; + uint _count; +}; + +/* DataResource + 1 Single NPoint + 2 Array of NPoints + 3 Array of NRects + 4 MessageList + 5 SubRectList + 6 RectList +*/ + +class DataResource { +public: + DataResource(NeverhoodEngine *vm); + ~DataResource(); + void load(uint32 fileHash); + void unload(); + NPoint getPoint(uint32 nameHash); + NPointArray *getPointArray(uint32 nameHash); + NRectArray *getRectArray(uint32 nameHash); + HitRectList *getHitRectList(); + MessageList *getMessageListAtPos(int16 klaymanX, int16 klaymanY, int16 mouseX, int16 mouseY); +protected: + + struct DRDirectoryItem { + uint32 nameHash; + uint16 offset; + uint16 type; + }; + + struct DRRect { + NRect rect; + uint16 subRectIndex; + }; + + struct DRSubRect { + NRect rect; + uint32 messageListHash; + uint16 messageListItemIndex; + }; + + typedef Common::Array<DRSubRect> DRSubRectList; + + NeverhoodEngine *_vm; + int _resourceHandle; + Common::Array<DRDirectoryItem> _directory; + Common::Array<NPoint> _points; + Common::Array<NPointArray*> _pointArrays; + Common::Array<NRectArray*> _rectArrays; + Common::Array<HitRectList*> _hitRectLists; + Common::Array<MessageList*> _messageLists; + Common::Array<DRRect> _drRects; + Common::Array<DRSubRectList*> _drSubRectLists; + DataResource::DRDirectoryItem *findDRDirectoryItem(uint32 nameHash, uint16 type); +}; + +// TODO: Dummy class atm + +class SoundResource { +public: + SoundResource(NeverhoodEngine *vm); + bool isPlaying(); + void load(uint32 fileHash); + void play(uint32 fileHash, bool looping = false); + void play(); + void stop() { /*DUMMY*/ } +protected: + NeverhoodEngine *_vm; +}; + +uint32 calcHash(const char *value); + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_RESOURCE_H */ diff --git a/engines/neverhood/resourceman.cpp b/engines/neverhood/resourceman.cpp new file mode 100644 index 0000000000..0538f58e87 --- /dev/null +++ b/engines/neverhood/resourceman.cpp @@ -0,0 +1,172 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "neverhood/resourceman.h" + +namespace Neverhood { + +ResourceMan::ResourceMan() { +} + +ResourceMan::~ResourceMan() { +} + +void ResourceMan::addArchive(const Common::String &filename) { + BlbArchive *archive = new BlbArchive(); + uint archiveIndex = _archives.size(); + archive->open(filename); + _archives.push_back(archive); + debug("ResourceMan::addArchive(%s) %d files", filename.c_str(), archive->getCount()); + _entries.reserve(_entries.size() + archive->getCount()); + for (uint archiveEntryIndex = 0; archiveEntryIndex < archive->getCount(); archiveEntryIndex++) { + BlbArchiveEntry *archiveEntry = archive->getEntry(archiveEntryIndex); + ResourceFileEntry *entry = findEntrySimple(archiveEntry->fileHash); + if (entry) { + if (archiveEntry->timeStamp > _archives[entry->archiveIndex]->getEntry(entry->entryIndex)->timeStamp) { + entry->archiveIndex = archiveIndex; + entry->entryIndex = archiveEntryIndex; + } + } else { + ResourceFileEntry newEntry; + newEntry.fileHash = archiveEntry->fileHash; + newEntry.resourceHandle = -1; + newEntry.archiveIndex = archiveIndex; + newEntry.entryIndex = archiveEntryIndex; + _entries.push_back(newEntry); + } + } +} + +ResourceFileEntry *ResourceMan::findEntrySimple(uint32 fileHash) { + for (uint i = 0; i < _entries.size(); i++) { + if (_entries[i].fileHash == fileHash) + return &_entries[i]; + } + return NULL; +} + +ResourceFileEntry *ResourceMan::findEntry(uint32 fileHash) { + ResourceFileEntry *entry = findEntrySimple(fileHash); + for (; entry && getArchiveEntry(entry)->comprType == 0x65; fileHash = getArchiveEntry(entry)->diskSize) + entry = findEntrySimple(fileHash); + return entry; +} + +BlbArchiveEntry *ResourceMan::getArchiveEntry(ResourceFileEntry *entry) const { + return _archives[entry->archiveIndex]->getEntry(entry->entryIndex); +} + +int ResourceMan::useResource(uint32 fileHash) { + ResourceFileEntry *entry = findEntry(fileHash); + if (entry->resourceHandle != -1) { + _resources[entry->resourceHandle]->useRefCount++; + } else { + Resource *resource = new Resource(); + resource->fileHash = entry->fileHash; + resource->archiveIndex = entry->archiveIndex; + resource->entryIndex = entry->entryIndex; + resource->data = NULL; + resource->dataRefCount = 0; + resource->useRefCount = 1; + entry->resourceHandle = (int)_resources.size(); + _resources.push_back(resource); + } + return entry->resourceHandle; +} + +void ResourceMan::unuseResource(int resourceHandle) { + Resource *resource = _resources[resourceHandle]; + if (resource->useRefCount > 0) + resource->useRefCount--; +} + +void ResourceMan::unuseResourceByHash(uint32 fileHash) { + ResourceFileEntry *entry = findEntry(fileHash); + if (entry->resourceHandle != -1) + unuseResource(entry->resourceHandle); +} + +int ResourceMan::getResourceHandleByHash(uint32 fileHash) { + ResourceFileEntry *entry = findEntry(fileHash); + return entry->resourceHandle; +} + +bool ResourceMan::isResourceDataValid(int resourceHandle) const { + return _resources[resourceHandle]->data != NULL; +} + +uint32 ResourceMan::getResourceSize(int resourceHandle) const { + Resource *resource = _resources[resourceHandle]; + return _archives[resource->archiveIndex]->getEntry(resource->entryIndex)->size; +} + +byte ResourceMan::getResourceType(int resourceHandle) { + Resource *resource = _resources[resourceHandle]; + return _archives[resource->archiveIndex]->getEntry(resource->entryIndex)->type; +} + +byte ResourceMan::getResourceTypeByHash(uint32 fileHash) { + ResourceFileEntry *entry = findEntry(fileHash); + return getArchiveEntry(entry)->type; +} + +byte *ResourceMan::getResourceExtData(int resourceHandle) { + Resource *resource = _resources[resourceHandle]; + return _archives[resource->archiveIndex]->getEntryExtData(resource->entryIndex); +} + +byte *ResourceMan::getResourceExtDataByHash(uint32 fileHash) { + ResourceFileEntry *entry = findEntrySimple(fileHash); + return entry ? _archives[entry->archiveIndex]->getEntryExtData(entry->entryIndex) : NULL; +} + +byte *ResourceMan::loadResource(int resourceHandle, bool moveToFront) { + Resource *resource = _resources[resourceHandle]; + if (resource->data != NULL) { + resource->dataRefCount++; + } else { + BlbArchive *archive = _archives[resource->archiveIndex]; + BlbArchiveEntry *archiveEntry = archive->getEntry(resource->entryIndex); + resource->data = new byte[archiveEntry->size]; + archive->load(resource->entryIndex, resource->data, 0); + resource->dataRefCount = 1; + } + return resource->data; +} + +void ResourceMan::unloadResource(int resourceHandle) { + Resource *resource = _resources[resourceHandle]; + if (resource->dataRefCount > 0) + resource->dataRefCount--; +} + +void ResourceMan::freeResource(Resource *resource) { + delete[] resource->data; + resource->data = NULL; +} + +Common::SeekableReadStream *ResourceMan::createStream(uint32 fileHash) { + ResourceFileEntry *entry = findEntry(fileHash); + return _archives[entry->archiveIndex]->createStream(entry->entryIndex); +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/resourceman.h b/engines/neverhood/resourceman.h new file mode 100644 index 0000000000..ed5bffaf9b --- /dev/null +++ b/engines/neverhood/resourceman.h @@ -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 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef NEVERHOOD_RESOURCEMAN_H +#define NEVERHOOD_RESOURCEMAN_H + +#include "common/array.h" +#include "common/file.h" +#include "neverhood/neverhood.h" +#include "neverhood/blbarchive.h" + +namespace Neverhood { + +struct ResourceFileEntry { + uint32 fileHash; + int resourceHandle; + uint archiveIndex; + uint entryIndex; +}; + +struct Resource { + uint32 fileHash; + uint archiveIndex; + uint entryIndex; + byte *data; + int dataRefCount; + int useRefCount; +}; + +class ResourceMan { +public: + ResourceMan(); + ~ResourceMan(); + void addArchive(const Common::String &filename); + ResourceFileEntry *findEntrySimple(uint32 fileHash); + ResourceFileEntry *findEntry(uint32 fileHash); + BlbArchiveEntry *getArchiveEntry(ResourceFileEntry *entry) const; + int useResource(uint32 fileHash); + void unuseResource(int resourceHandle); + void unuseResourceByHash(uint32 fileHash); + int getResourceHandleByHash(uint32 fileHash); + bool isResourceDataValid(int resourceHandle) const; + uint32 getResourceSize(int resourceHandle) const; + byte getResourceType(int resourceHandle); + byte getResourceTypeByHash(uint32 fileHash); + byte *getResourceExtData(int resourceHandle); + byte *getResourceExtDataByHash(uint32 fileHash); + byte *loadResource(int resourceHandle, bool moveToFront = false); + void unloadResource(int resourceHandle); + void freeResource(Resource *resource); + Common::SeekableReadStream *createStream(uint32 fileHash); + const ResourceFileEntry& getEntry(uint index) { return _entries[index]; } + uint getEntryCount() { return _entries.size(); } +private: + Common::Array<BlbArchive*> _archives; + Common::Array<ResourceFileEntry> _entries; + Common::Array<Resource*> _resources; +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_RESOURCEMAN_H */ diff --git a/engines/neverhood/scene.cpp b/engines/neverhood/scene.cpp new file mode 100644 index 0000000000..9a588f283f --- /dev/null +++ b/engines/neverhood/scene.cpp @@ -0,0 +1,615 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "neverhood/scene.h" +#include "neverhood/collisionman.h" + +namespace Neverhood { + +Scene::Scene(NeverhoodEngine *vm, Module *parentModule, bool clearHitRects) + : Entity(vm, 0), _parentModule(parentModule), _dataResource(vm) { + + _messageListFlag1 = false; + _systemCallbackFlag = false; + _messageList = NULL; + _rectType = 0; + _mouseClickPos.x = 0; + _mouseClickPos.y = 0; + _mouseClicked = false; + _rectList = NULL; + _klayman = NULL; + _mouseCursor = NULL; + _palette = NULL; + _background = NULL; + // TODO _field_8E = -1; + if (clearHitRects) { + _vm->_collisionMan->clearHitRects(); + _vm->_collisionMan->clearSprites(); + } + _vm->_screen->setFps(24); + // TODO g_screen->hSmack = NULL; + // TODO g_screen->field_24 = 0; + // TODO g_screen->field_26 = 0; + // TODO g_screen->resetDirtyRects(); + _messageListFlag = true; + _surfaceFlag = false; + _messageList2 = NULL; + _smackerPlayer = NULL; + _smkFileHash = 0; + _messageListFlag2 = false; + _messageValue = -1; + + SetUpdateHandler(&Scene::update); + SetMessageHandler(&Scene::handleMessage); +} + +Scene::~Scene() { + + if (_palette) { + removeEntity(_palette); + delete _palette; + } + + // Delete all entities + for (Common::Array<Entity*>::iterator iter = _entities.begin(); iter != _entities.end(); iter++) + delete *iter; + + // Don't delete surfaces since they always belong to an entity + +} + +void Scene::draw() { + if (_smackerPlayer) { + if (_surfaceFlag) { + // TODO g_screen->resetDirtyRects(); + // TODO g_screen->copyDirtyRects(); + // TODO g_screen->addDirtyRects(); + } + if (_smackerPlayer->getSurface()) + _smackerPlayer->getSurface()->draw(); + } else { +#if 0 + if (_surfaceFlag) { + // TODO g_screen->copyDirtyRects(); + for (Common::Array<BaseSurface*>::iterator iter = _surfaces.begin(); iter != _surfaces.end(); iter++) + (*iter)->addDirtyRect(); + // TODO g_screen->addDirtyRects(); + } +#endif + for (Common::Array<BaseSurface*>::iterator iter = _surfaces.begin(); iter != _surfaces.end(); iter++) { + //debug(4, "priority = %d", (*iter)->getPriority()); + (*iter)->draw(); + } + } +} + +void Scene::addEntity(Entity *entity) { + int index = 0, insertIndex = -1; + for (Common::Array<Entity*>::iterator iter = _entities.begin(); iter != _entities.end(); iter++) { + if ((*iter)->getPriority() > entity->getPriority()) { + insertIndex = index; + break; + } + index++; + } + if (insertIndex >= 0) + _entities.insert_at(insertIndex, entity); + else + _entities.push_back(entity); +} + +bool Scene::removeEntity(Entity *entity) { + for (uint index = 0; index < _entities.size(); index++) { + if (_entities[index] == entity) { + _entities.remove_at(index); + return true; + } + } + return false; +} + +void Scene::addSurface(BaseSurface *surface) { + int index = 0, insertIndex = -1; + for (Common::Array<BaseSurface*>::iterator iter = _surfaces.begin(); iter != _surfaces.end(); iter++) { + if ((*iter)->getPriority() > surface->getPriority()) { + insertIndex = index; + break; + } + index++; + } + if (insertIndex >= 0) + _surfaces.insert_at(insertIndex, surface); + else + _surfaces.push_back(surface); +} + +bool Scene::removeSurface(BaseSurface *surface) { + for (uint index = 0; index < _surfaces.size(); index++) { + if (_surfaces[index] == surface) { + _surfaces.remove_at(index); + return true; + } + } + return false; +} + +Sprite *Scene::addSprite(Sprite *sprite) { + addEntity(sprite); + addSurface(sprite->getSurface()); + return sprite; +} + +void Scene::setSurfacePriority(BaseSurface *surface, int priority) { + surface->setPriority(priority); + if (removeSurface(surface)) + addSurface(surface); +} + +void Scene::deleteSprite(Sprite **sprite) { + _vm->_collisionMan->removeSprite(*sprite); + removeSurface((*sprite)->getSurface()); + removeEntity(*sprite); + delete *sprite; + *sprite = NULL; +} + +Background *Scene::addBackground(Background *background) { + addEntity(background); + addSurface(background->getSurface()); + return background; +} + +void Scene::setBackground(uint32 fileHash, bool dirtyBackground) { + _background = addBackground(new DirtyBackground(_vm, fileHash, 0, 0)); +} + +void Scene::changeBackground(uint32 fileHash) { + _background->load(fileHash); +} + +void Scene::setPalette(uint32 fileHash) { + _palette = fileHash ? new Palette(_vm, fileHash) : new Palette(_vm); + _palette->usePalette(); +} + +void Scene::setHitRects(uint32 id) { + _vm->_collisionMan->setHitRects(id); +} + +Sprite *Scene::insertStaticSprite(uint32 fileHash, int surfacePriority) { + return addSprite(new StaticSprite(_vm, fileHash, surfacePriority)); +} + +void Scene::insertMouse433(uint32 fileHash, NRect *mouseRect) { + NRect rect(-1, -1, -1, -1); + if (mouseRect) + rect = *mouseRect; + _mouseCursor = new Mouse(_vm, 0x0820C408, rect); + addSprite(_mouseCursor); +} + +void Scene::insertMouse435(uint32 fileHash, int16 x1, int16 x2) { + _mouseCursor = new Mouse(_vm, fileHash, x1, x2); + addSprite(_mouseCursor); +} + +void Scene::insertNavigationMouse(uint32 fileHash, int type) { + _mouseCursor = new Mouse(_vm, fileHash, type); + addSprite(_mouseCursor); +} + +void Scene::showMouse(bool visible) { + _mouseCursor->getSurface()->setVisible(visible); +} + +void Scene::changeMouseCursor(uint32 fileHash) { + _mouseCursor->load(fileHash); + _mouseCursor->updateCursor(); +} + +SmackerPlayer *Scene::addSmackerPlayer(SmackerPlayer *smackerPlayer) { + addEntity(smackerPlayer); + addSurface(smackerPlayer->getSurface()); + return smackerPlayer; +} + +void Scene::update() { + + if (_smkFileHash != 0) { + // TODO + _smackerPlayer = new SmackerPlayer(_vm, this, _smkFileHash, true, 0); + _savedUpdateHandlerCb = _updateHandlerCb; + _savedMessageHandlerCb = _messageHandlerCb; + SetUpdateHandler(&Scene::smackerUpdate); + SetMessageHandler(&Scene::smackerHandleMessage); + _smackerDone = false; + smackerUpdate(); + // g_screen->smackerPlayer = _smackerPlayer; + _smkFileHash = 0; + } else { + if (_mouseClicked) { + if (_klayman) { + // TODO: Merge later + if (_messageListFlag && + _klayman->hasMessageHandler() && + sendMessage(_klayman, 0x1008, 0) != 0 && + queryPositionSprite(_mouseClickPos.x, _mouseClickPos.y)) { + _mouseClicked = false; + } else if (_messageListFlag && + _klayman->hasMessageHandler() && + sendMessage(_klayman, 0x1008, 0) != 0) { + _mouseClicked = !queryPositionRectList(_mouseClickPos.x, _mouseClickPos.y); + } + } else if (queryPositionSprite(_mouseClickPos.x, _mouseClickPos.y)) { + _mouseClicked = false; + } + } + + runMessageList(); + + // Update all entities + for (Common::Array<Entity*>::iterator iter = _entities.begin(); iter != _entities.end(); iter++) + (*iter)->handleUpdate(); + + } + +} + +void Scene::leaveScene(uint32 result) { + sendMessage(_parentModule, 0x1009, result); +} + +uint32 Scene::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + switch (messageNum) { + case 0: // mouse moved + if (_mouseCursor && _mouseCursor->hasMessageHandler()) + sendMessage(_mouseCursor, 0x4002, param); + // TODO queryPositionSomeRects(param.asPoint().x, param.asPoint().y); + break; + case 1: // mouse clicked + _mouseClicked = true; + _mouseClickPos = param.asPoint(); + break; + /* ORIGINAL DEBUG + case 3: + drawSurfaceRects(); + break; + */ + /* ORIGINAL DEBUG + case 4: + drawRectListRects(); + break; + */ + case 5: +#if 0 + broadcastObjectMessage5(); +#endif + break; + case 6: + sendMessage(_parentModule, 0x1009, param); + break; + case 0x1006: + if (_messageListFlag1) { + _messageListFlag1 = false; + if (_messageListIndex == _messageListCount) + sendMessage(_klayman, 0x4004, 0); + else { + runMessageList(); + } + } + break; + case 0x1007: + if (_messageListFlag1) { + _messageListFlag1 = false; + _messageList = NULL; + sendMessage(_klayman, 0x4004, 0); + } + break; + case 0x101D: + if (_mouseCursor) { + _prevVisible = _mouseCursor->getSurface()->getVisible(); + _mouseCursor->getSurface()->setVisible(false); + } + break; + case 0x101E: + if (_prevVisible && _mouseCursor) { + _mouseCursor->getSurface()->setVisible(false); + // TODO sendMessage(_mouseCursor, 0x4002, g_Screen->_mousePos); + } + break; + case 0x1022: + setSurfacePriority(((Sprite*)sender)->getSurface(), param.asInteger()); + break; + } + return 0; +} + +void Scene::smackerUpdate() { + //**ALL TODO +#if 0 + _smackerPlayer->handleUpdate(); + if (_smackerDone) { + delete _smackerPlayer; + _smackerPlayer = NULL; + _updateHandlerCb = _savedUpdateHandlerCb; + _messageHandlerCb = _savedMessageHandlerCb; + if (_palette) + _palette->usePalette(); + // TODO _background->restore(); + // TODO g_screen->smackerPlayer = NULL; + } +#endif +} + +uint32 Scene::smackerHandleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + switch (messageNum) { + case 0x3002: + _smackerDone = true; + break; + } + return 0; +} + +bool Scene::queryPositionSprite(int16 mouseX, int16 mouseY) { + debug("Scene::queryPositionSprite(%d, %d)", mouseX, mouseY); + for (uint i = 0; i < _vm->_collisionMan->getSpriteCount(); i++) { + Sprite *sprite = _vm->_collisionMan->getSprite(i); + if (sprite->hasMessageHandler() && sprite->isPointInside(mouseX, mouseY) && + sendPointMessage(sprite, 0x1011, _mouseClickPos) != 0) { + return true; + } + } + return false; +} + +bool Scene::queryPositionRectList(int16 mouseX, int16 mouseY) { + int16 klaymanX = _klayman->getX(); + int16 klaymanY = _klayman->getY(); + if (_rectType == 1) { + RectList &rectList = *_rectList; + for (uint i = 0; i < rectList.size(); i++) { + debug("(%d, %d) ? (%d, %d, %d, %d)", klaymanX, klaymanY, rectList[i].rect.x1, rectList[i].rect.y1, rectList[i].rect.x2, rectList[i].rect.y2); + if (klaymanX >= rectList[i].rect.x1 && klaymanX <= rectList[i].rect.x2 && + klaymanY >= rectList[i].rect.y1 && klaymanY <= rectList[i].rect.y2) { + for (uint j = 0; j < rectList[i].subRects.size(); j++) { + debug(" (%d, %d) ? (%d, %d, %d, %d)", mouseX, mouseY, rectList[i].subRects[j].rect.x1, rectList[i].subRects[j].rect.y1, rectList[i].subRects[j].rect.x2, rectList[i].subRects[j].rect.y2); + if (mouseX >= rectList[i].subRects[j].rect.x1 && mouseX <= rectList[i].subRects[j].rect.x2 && + mouseY >= rectList[i].subRects[j].rect.y1 && mouseY <= rectList[i].subRects[j].rect.y2) { + debug("Scene::queryPositionRectList() -> %08X", rectList[i].subRects[j].messageListId); + return setMessageList2(rectList[i].subRects[j].messageListId); + } + } + } + } + } else if (_rectType == 2) { + MessageList *messageList = _dataResource.getMessageListAtPos(klaymanX, klaymanY, mouseX, mouseY); + if (messageList && messageList->size()) + setMessageList2(messageList, true, true); + } + return true; +} + +void Scene::setMessageList(uint32 id, bool messageListFlag, bool systemCallbackFlag) { + setMessageList(_vm->_staticData->getMessageList(id), messageListFlag, systemCallbackFlag); +} + +void Scene::setMessageList(MessageList *messageList, bool messageListFlag, bool systemCallbackFlag) { + debug("Scene::setMessageList(%p)", (void*)messageList); + _messageList = messageList; + _messageListCount = _messageList ? _messageList->size() : 0; + _messageListIndex = 0; + _messageListFlag1 = false; + _messageListFlag = messageListFlag; + _systemCallbackFlag = systemCallbackFlag; + _messageListStatus = 1; + sendMessage(_klayman, 0x101C, 0); + + // DEBUG: Show message list + for (uint i = 0; i < messageList->size(); i++) { + debug("A: %02d: %04X, %08X", i, (*messageList)[i].messageNum, (*messageList)[i].messageValue); + } + debug("A: ================================================================"); + +} + +bool Scene::setMessageList2(uint32 id, bool messageListFlag, bool systemCallbackFlag) { + return setMessageList2(_vm->_staticData->getMessageList(id), messageListFlag, systemCallbackFlag); +} + +bool Scene::setMessageList2(MessageList *messageList, bool messageListFlag, bool systemCallbackFlag) { + bool result = false; + + debug("Scene::setMessageList2(%p)", (void*)messageList); + + // DEBUG: Show message list + for (uint i = 0; i < messageList->size(); i++) { + debug("B: %02d: %04X, %08X", i, (*messageList)[i].messageNum, (*messageList)[i].messageValue); + } + debug("B: ================================================================"); + + if (_messageListStatus == 1) { + if (messageList != _messageList2) { + if (_messageValue >= 0) { + sendMessage(_parentModule, 0x1023, _messageValue); + _messageValue = -1; + } + _messageList2 = messageList; + setMessageList(messageList, messageListFlag, systemCallbackFlag); + result = true; + } + } else if (_messageListStatus == 2) { + if (messageList == _messageList2) { + if (_messageValue >= 0) { + sendMessage(_parentModule, 0x1023, _messageValue); + _messageValue = -1; + } + _messageList2 = messageList; + setMessageList(messageList, messageListFlag, systemCallbackFlag); + result = true; + } + } else { + if (_messageValue >= 0) { + sendMessage(_parentModule, 0x1023, _messageValue); + _messageValue = -1; + } + _messageList2 = messageList; + setMessageList(messageList, messageListFlag, systemCallbackFlag); + result = true; + } + return result; +} + +void Scene::runMessageList() { + debug(7, "Scene::runMessageList() _messageListFlag2 = %d; _messageListFlag1 = %d", _messageListFlag2, _messageListFlag1); + + if (_messageListFlag2 || _messageListFlag1) + return; + + _messageListFlag2 = true; + + if (!_messageList) { + _messageList2 = NULL; + _messageListStatus = 0; + } + + if (_messageList && _klayman) { + + while (_messageList && _messageListIndex < _messageListCount && !_messageListFlag1) { + uint32 messageNum = (*_messageList)[_messageListIndex].messageNum; + uint32 messageParam = (*_messageList)[_messageListIndex].messageValue; + + debug("Scene::runMessageList() %04X, %08X", messageNum, messageParam); + + _messageListIndex++; + if (_messageListIndex == _messageListCount) { + sendMessage(_klayman, 0x1021, 0); + } + if (_systemCallbackFlag) { + messageNum = convertMessageNum(messageNum); + } + if (messageNum != 0x4003) { + if (messageNum == 0x1009 || messageNum == 0x1024) { + sendMessage(_parentModule, messageNum, messageParam); + } else if (messageNum == 0x100A) { + _messageValue = messageParam; + sendMessage(_parentModule, messageNum, messageParam); + } else if (messageNum == 0x4001) { + _messageListFlag1 = true; + sendPointMessage(_klayman, 0x4001, _mouseClickPos); + } else if (messageNum == 0x100D) { + if (this->hasMessageHandler() && sendMessage(this, 0x100D, messageParam) != 0) + continue; + } else if (messageNum == 0x101A) { + _messageListStatus = 0; + } else if (messageNum == 0x101B) { + _messageListStatus = 2; + } else if (messageNum == 0x1020) { + _messageListFlag = false; + } else if (messageNum >= 0x2000 && messageNum <= 0x2FFF) { + if (this->hasMessageHandler() && sendMessage(this, messageNum, messageParam) != 0) { + _messageListFlag2 = false; + return; + } + } else { + _messageListFlag1 = true; + if (_klayman->hasMessageHandler() && sendMessage(_klayman, messageNum, messageParam) != 0) { + _messageListFlag1 = false; + } + } + } + if (_messageListIndex == _messageListCount) { + _messageListFlag = true; + _messageList = NULL; + } + } + } + + _messageListFlag2 = false; + +} + +void Scene::messageList402220() { + _messageListFlag1 = false; + _messageList = NULL; + _messageListFlag = true; + sendMessage(_klayman, 0x4004, 0); +} + +void Scene::setRectList(uint32 id) { + setRectList(_vm->_staticData->getRectList(id)); +} + +void Scene::setRectList(RectList *rectList) { + _rectList = rectList; + _rectType = 1; +} + +void Scene::clearRectList() { + _rectList = NULL; + _rectType = 0; +} + +void Scene::loadHitRectList() { + HitRectList *hitRectList = _dataResource.getHitRectList(); + debug("Scene::loadHitRectList() hitRectList = %p", (void*)hitRectList); + if (hitRectList) { + _hitRectList = *hitRectList; + _vm->_collisionMan->setHitRects(&_hitRectList); + } +} + +void Scene::loadDataResource(uint32 fileHash) { + _dataResource.load(fileHash); + _rectType = 2; + if (_klayman) + _klayman->loadDataResource(fileHash); +} + +uint16 Scene::convertMessageNum(uint32 messageNum) { + switch (messageNum) { + case 0x00004004: + return 0x4001; + case 0x00000083: + return 0x100A; + case 0x044001C8: + return 0x481C; + case 0x02420480: + return 0x4818; + case 0x08004025: + return 0x100D; + case 0x04404281: + return 0x4824; + case 0x08400880: + return 0x4825; + case 0x08209081: + return 0x4823; + case 0x24000060: + return 0x1009; + case 0x42002200: + return 0x4004; + case 0x428D4894: + return 0x101A; + } + return 0x1000; +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/scene.h b/engines/neverhood/scene.h new file mode 100644 index 0000000000..e962266168 --- /dev/null +++ b/engines/neverhood/scene.h @@ -0,0 +1,214 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef NEVERHOOD_SCENE_H +#define NEVERHOOD_SCENE_H + +#include "common/array.h" +#include "neverhood/neverhood.h" +#include "neverhood/background.h" +#include "neverhood/entity.h" +#include "neverhood/graphics.h" +#include "neverhood/klayman.h" +#include "neverhood/module.h" +#include "neverhood/palette.h" +#include "neverhood/smackerplayer.h" +#include "neverhood/sprite.h" +#include "neverhood/staticdata.h" + +namespace Neverhood { + +class Scene : public Entity { +public: + Scene(NeverhoodEngine *vm, Module *parentModule, bool clearHitRects); + virtual ~Scene(); + virtual void draw(); + void addEntity(Entity *entity); + bool removeEntity(Entity *entity); + void addSurface(BaseSurface *surface); + bool removeSurface(BaseSurface *surface); + Sprite *addSprite(Sprite *sprite); + void setSurfacePriority(BaseSurface *surface, int priority); + void deleteSprite(Sprite **sprite); + Background *addBackground(Background *background); + void setBackground(uint32 fileHash, bool dirtyBackground = true); + void changeBackground(uint32 fileHash); + void setBackgroundY(int16 y) { _background->getSurface()->getDrawRect().y = y; } + int16 getBackgroundY() { return _background->getSurface()->getDrawRect().y; } + void setPalette(uint32 fileHash = 0); + void setHitRects(uint32 id); + Sprite *insertStaticSprite(uint32 fileHash, int surfacePriority); + void insertMouse433(uint32 fileHash, NRect *mouseRect = NULL); + void insertMouse435(uint32 fileHash, int16 x1, int16 x2); + void insertNavigationMouse(uint32 fileHash, int type); + void showMouse(bool visible); + void changeMouseCursor(uint32 fileHash); + SmackerPlayer *addSmackerPlayer(SmackerPlayer *smackerPlayer); + void update(); + void leaveScene(uint32 result); + // Some crazy templated functions to make the logic code smaller/simpler (imo!) + // insertKlayman + template<class T> + void insertKlayman() { + _klayman = (T*)addSprite(new T(_vm, this)); + } + template<class T, class Arg1> + void insertKlayman(Arg1 arg1) { + _klayman = (T*)addSprite(new T(_vm, this, arg1)); + } + template<class T, class Arg1, class Arg2> + void insertKlayman(Arg1 arg1, Arg2 arg2) { + _klayman = (T*)addSprite(new T(_vm, this, arg1, arg2)); + } + template<class T, class Arg1, class Arg2, class Arg3> + void insertKlayman(Arg1 arg1, Arg2 arg2, Arg3 arg3) { + _klayman = (T*)addSprite(new T(_vm, this, arg1, arg2, arg3)); + } + template<class T, class Arg1, class Arg2, class Arg3, class Arg4> + void insertKlayman(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) { + _klayman = (T*)addSprite(new T(_vm, this, arg1, arg2, arg3, arg4)); + } + template<class T, class Arg1, class Arg2, class Arg3, class Arg4, class Arg5> + void insertKlayman(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) { + _klayman = (T*)addSprite(new T(_vm, this, arg1, arg2, arg3, arg4, arg5)); + } + template<class T, class Arg1, class Arg2, class Arg3, class Arg4, class Arg5, class Arg6> + void insertKlayman(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5, Arg6 arg6) { + _klayman = (T*)addSprite(new T(_vm, this, arg1, arg2, arg3, arg4, arg5, arg6)); + } + // insertSprite + template<class T> + T* insertSprite() { + return (T*)addSprite(new T(_vm)); + } + template<class T, class Arg1> + T* insertSprite(Arg1 arg1) { + return (T*)addSprite(new T(_vm, arg1)); + } + template<class T, class Arg1, class Arg2> + T* insertSprite(Arg1 arg1, Arg2 arg2) { + return (T*)addSprite(new T(_vm, arg1, arg2)); + } + template<class T, class Arg1, class Arg2, class Arg3> + T* insertSprite(Arg1 arg1, Arg2 arg2, Arg3 arg3) { + return (T*)addSprite(new T(_vm, arg1, arg2, arg3)); + } + template<class T, class Arg1, class Arg2, class Arg3, class Arg4> + T* insertSprite(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) { + return (T*)addSprite(new T(_vm, arg1, arg2, arg3, arg4)); + } + template<class T, class Arg1, class Arg2, class Arg3, class Arg4, class Arg5> + T* insertSprite(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) { + return (T*)addSprite(new T(_vm, arg1, arg2, arg3, arg4, arg5)); + } + template<class T, class Arg1, class Arg2, class Arg3, class Arg4, class Arg5, class Arg6> + T* insertSprite(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5, Arg6 arg6) { + return (T*)addSprite(new T(_vm, arg1, arg2, arg3, arg4, arg5, arg6)); + } + // createSprite + template<class T> + T* createSprite() { + return new T(_vm); + } + template<class T, class Arg1> + T* createSprite(Arg1 arg1) { + return new T(_vm, arg1); + } + template<class T, class Arg1, class Arg2> + T* createSprite(Arg1 arg1, Arg2 arg2) { + return new T(_vm, arg1, arg2); + } + template<class T, class Arg1, class Arg2, class Arg3> + T* createSprite(Arg1 arg1, Arg2 arg2, Arg3 arg3) { + return new T(_vm, arg1, arg2, arg3); + } + template<class T, class Arg1, class Arg2, class Arg3, class Arg4> + T* createSprite(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) { + return new T(_vm, arg1, arg2, arg3, arg4); + } + template<class T, class Arg1, class Arg2, class Arg3, class Arg4, class Arg5> + T* createSprite(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) { + return new T(_vm, arg1, arg2, arg3, arg4, arg5); + } + template<class T, class Arg1, class Arg2, class Arg3, class Arg4, class Arg5, class Arg6> + T* createSprite(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5, Arg6 arg6) { + return new T(_vm, arg1, arg2, arg3, arg4, arg5, arg6); + } +protected: + Module *_parentModule; + Common::Array<Entity*> _entities; + Common::Array<BaseSurface*> _surfaces; + bool _systemCallbackFlag; + MessageList *_messageList; + uint _messageListCount; + uint _messageListIndex; + bool _messageListFlag1; + NPoint _mouseClickPos; + bool _mouseClicked; + DataResource _dataResource; + RectList *_rectList; + HitRectList _hitRectList; + int _rectType; + // TODO 0000008E field_8E dw ? + Mouse *_mouseCursor; + Klayman *_klayman; + Palette *_palette; + Background *_background; + bool _surfaceFlag; + bool _messageListFlag; + MessageList *_messageList2; + int _messageListStatus; + SmackerPlayer *_smackerPlayer; + void (Entity::*_savedUpdateHandlerCb)(); + uint32 (Entity::*_savedMessageHandlerCb)(int messageNum, const MessageParam ¶m, Entity *sender); + bool _smackerDone; + // TODO 000000BD field_BD db ? + // TODO 000000BE field_BE db ? + // TODO 000000BF field_BF db ? + uint32 _smkFileHash; + // TODO 000000C4 hitArray dd ? + bool _messageListFlag2; + bool _prevVisible; + int _messageValue; + // TODO 000000CF field_CF db ? + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void smackerUpdate(); + uint32 smackerHandleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + bool queryPositionSprite(int16 mouseX, int16 mouseY); + bool queryPositionRectList(int16 mouseX, int16 mouseY); + void setMessageList(uint32 id, bool messageListFlag = true, bool systemCallbackFlag = false); + void setMessageList(MessageList *messageList, bool messageListFlag = true, bool systemCallbackFlag = false); + bool setMessageList2(uint32 id, bool messageListFlag = true, bool systemCallbackFlag = false); + bool setMessageList2(MessageList *messageList, bool messageListFlag = true, bool systemCallbackFlag = false); + void runMessageList(); + void setRectList(uint32 id); + void setRectList(RectList *rectList); + void clearRectList(); + void loadHitRectList(); + void messageList402220(); + void loadDataResource(uint32 fileHash); + uint16 convertMessageNum(uint32 messageNum); +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_SCENE_H */ diff --git a/engines/neverhood/screen.cpp b/engines/neverhood/screen.cpp new file mode 100644 index 0000000000..351e51a3e9 --- /dev/null +++ b/engines/neverhood/screen.cpp @@ -0,0 +1,289 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "graphics/palette.h" +#include "neverhood/screen.h" + +namespace Neverhood { + +Screen::Screen(NeverhoodEngine *vm) + : _vm(vm), _paletteData(NULL), _paletteChanged(false) { + + _ticks = _vm->_system->getMillis(); + + _backScreen = new Graphics::Surface(); + _backScreen->create(640, 480, Graphics::PixelFormat::createFormatCLUT8()); + +} + +Screen::~Screen() { + delete _backScreen; +} + +void Screen::update() { + updatePalette(); + // TODO: Implement actual code + _vm->_system->copyRectToScreen((const byte*)_backScreen->pixels, _backScreen->pitch, 0, 0, 640, 480); + _vm->_system->updateScreen(); +} + +void Screen::wait() { + // TODO + _vm->_system->delayMillis(40); +} + +void Screen::setFps(int fps) { + _frameDelay = 1000 / fps; +} + +int Screen::getFps() { + return 1000 / _frameDelay; +} + +void Screen::setPaletteData(byte *paletteData) { + _paletteChanged = true; + _paletteData = paletteData; +} + +void Screen::unsetPaletteData(byte *paletteData) { + if (_paletteData == paletteData) { + _paletteChanged = false; + _paletteData = NULL; + } +} + +void Screen::testPalette(byte *paletteData) { + if (_paletteData == paletteData) + _paletteChanged = true; +} + +void Screen::updatePalette() { + if (_paletteChanged && _paletteData) { + byte *tempPalette = new byte[768]; + for (int i = 0; i < 256; i++) { + tempPalette[i * 3 + 0] = _paletteData[i * 4 + 0]; + tempPalette[i * 3 + 1] = _paletteData[i * 4 + 1]; + tempPalette[i * 3 + 2] = _paletteData[i * 4 + 2]; + } + _vm->_system->getPaletteManager()->setPalette(tempPalette, 0, 256); + delete[] tempPalette; + _paletteChanged = false; + } +} + +void Screen::clear() { + memset(_backScreen->pixels, 0, _backScreen->pitch * _backScreen->h); +} + +void Screen::drawSurface2(const Graphics::Surface *surface, NDrawRect &drawRect, NRect &clipRect, bool transparent) { + + int16 destX, destY; + NRect ddRect; + + if (drawRect.x + drawRect.width >= clipRect.x2) + ddRect.x2 = clipRect.x2 - drawRect.x; + else + ddRect.x2 = drawRect.width; + + if (drawRect.x < clipRect.x1) { + destX = clipRect.x1; + ddRect.x1 = clipRect.x1 - drawRect.x; + } else { + destX = drawRect.x; + ddRect.x1 = 0; + } + + if (drawRect.y + drawRect.height >= clipRect.y2) + ddRect.y2 = clipRect.y2 - drawRect.y; + else + ddRect.y2 = drawRect.height; + + if (drawRect.y < clipRect.y1) { + destY = clipRect.y1; + ddRect.y1 = clipRect.y1 - drawRect.y; + } else { + destY = drawRect.y; + ddRect.y1 = 0; + } + + //debug(2, "draw: x = %d; y = %d; (%d, %d, %d, %d)", destX, destY, ddRect.x1, ddRect.y1, ddRect.x2, ddRect.y2); + + blit(surface, destX, destY, ddRect, transparent); + + // Useful for debugging + //_backScreen->frameRect(Common::Rect(clipRect.x1, clipRect.y1, clipRect.x2, clipRect.y2), 250); + //_backScreen->frameRect(Common::Rect(destX, destY, destX + ddRect.x2, destY + ddRect.y2), 255); + //_backScreen->frameRect(Common::Rect(drawRect.x, drawRect.y, drawRect.x + drawRect.width, drawRect.y + drawRect.height), 255); + +} + +void Screen::drawSurface3(const Graphics::Surface *surface, int16 x, int16 y, NDrawRect &drawRect, NRect &clipRect, bool transparent) { + + int16 destX, destY; + NRect ddRect; + + if (x + drawRect.width >= clipRect.x2) + ddRect.x2 = clipRect.x2 - drawRect.x - x; + else + ddRect.x2 = drawRect.x + drawRect.width; + + if (x < clipRect.x1) { + destX = clipRect.x1; + ddRect.x1 = clipRect.x1 + drawRect.x - x; + } else { + destX = x; + ddRect.x1 = drawRect.x; + } + + if (y + drawRect.height >= clipRect.y2) + ddRect.y2 = clipRect.y2 + drawRect.y - y; + else + ddRect.y2 = drawRect.y + drawRect.height; + + if (y < clipRect.y1) { + destY = clipRect.y1; + ddRect.y1 = clipRect.y1 + drawRect.y - y; + } else { + destY = y; + ddRect.y1 = drawRect.y; + } + + blit(surface, destX, destY, ddRect, transparent); + +} + +void Screen::blit(const Graphics::Surface *surface, int16 destX, int16 destY, NRect &ddRect, bool transparent) { + + const byte *source = (const byte*)surface->getBasePtr(ddRect.x1, ddRect.y1); + byte *dest = (byte*)_backScreen->getBasePtr(destX, destY); + int width = ddRect.x2 - ddRect.x1; + int height = ddRect.y2 - ddRect.y1; + + if (width <= 0 || height <= 0) + return; + + if (!transparent) { + while (height--) { + memcpy(dest, source, width); + source += surface->pitch; + dest += _backScreen->pitch; + } + } else { + while (height--) { + for (int xc = 0; xc < width; xc++) + if (source[xc] != 0) + dest[xc] = source[xc]; + source += surface->pitch; + dest += _backScreen->pitch; + } + } + +} + +void Screen::drawDoubleSurface2(const Graphics::Surface *surface, NDrawRect &drawRect) { + + const byte *source = (const byte*)surface->getBasePtr(0, 0); + byte *dest = (byte*)_backScreen->getBasePtr(drawRect.x, drawRect.y); + + for (int16 yc = 0; yc < surface->h; yc++) { + byte *row = dest; + for (int16 xc = 0; xc < surface->w; xc++) { + *row++ = *source; + *row++ = *source++; + } + memcpy(dest + _backScreen->pitch, dest, surface->w * 2); + dest += _backScreen->pitch; + dest += _backScreen->pitch; + } + +} + +void Screen::drawUnk(const Graphics::Surface *surface, NDrawRect &drawRect, NDrawRect &sysRect, NRect &clipRect, bool transparent) { + + int16 x, y; + bool xflag, yflag; + NDrawRect newDrawRect; + + x = sysRect.x; + if (sysRect.width <= x || -sysRect.width >= x) { + x = x % sysRect.width; + } + if (x < 0) + x += sysRect.width; + + y = sysRect.y; + if (y >= sysRect.height || -sysRect.height >= y) { + y = y % sysRect.height; + } + if (y < 0) + y += sysRect.height; + + xflag = x <= 0; + yflag = y <= 0; + + newDrawRect.x = x; + newDrawRect.width = sysRect.width - x; + if (drawRect.width < newDrawRect.width) { + xflag = true; + newDrawRect.width = drawRect.width; + } + + newDrawRect.y = y; + newDrawRect.height = sysRect.height - y; + if (drawRect.height < newDrawRect.height) { + yflag = true; + newDrawRect.height = drawRect.height; + } + + drawSurface3(surface, drawRect.x, drawRect.y, newDrawRect, clipRect, transparent); + + if (!xflag) { + newDrawRect.x = 0; + newDrawRect.y = y; + newDrawRect.width = x + drawRect.width - sysRect.width; + newDrawRect.height = sysRect.height - y; + if (drawRect.height < newDrawRect.height) + newDrawRect.height = drawRect.height; + drawSurface3(surface, sysRect.width + drawRect.x - x, drawRect.y, newDrawRect, clipRect, transparent); + } + + if (!yflag) { + newDrawRect.x = x; + newDrawRect.y = 0; + newDrawRect.width = sysRect.width - x; + newDrawRect.height = y + drawRect.height - sysRect.height; + if (drawRect.width < newDrawRect.width) + newDrawRect.width = drawRect.width; + drawSurface3(surface, drawRect.x, sysRect.height + drawRect.y - y, newDrawRect, clipRect, transparent); + } + + if (!xflag && !yflag) { + newDrawRect.x = 0; + newDrawRect.y = 0; + newDrawRect.width = x + drawRect.width - sysRect.width; + newDrawRect.height = y + drawRect.height - sysRect.height; + drawSurface3(surface, sysRect.width + drawRect.x - x, sysRect.height + drawRect.y - y, newDrawRect, clipRect, transparent); + } + +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/screen.h b/engines/neverhood/screen.h new file mode 100644 index 0000000000..e813e63f20 --- /dev/null +++ b/engines/neverhood/screen.h @@ -0,0 +1,61 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef NEVERHOOD_SCREEN_H +#define NEVERHOOD_SCREEN_H + +#include "graphics/surface.h" +#include "neverhood/neverhood.h" +#include "neverhood/graphics.h" + +namespace Neverhood { + +class Screen { +public: + Screen(NeverhoodEngine *vm); + ~Screen(); + void update(); + void wait(); + void setFps(int fps); + int getFps(); + void setPaletteData(byte *paletteData); + void unsetPaletteData(byte *paletteData); + void testPalette(byte *paletteData); + void updatePalette(); + void clear(); + void drawSurface2(const Graphics::Surface *surface, NDrawRect &drawRect, NRect &clipRect, bool transparent); + void drawSurface3(const Graphics::Surface *surface, int16 x, int16 y, NDrawRect &drawRect, NRect &clipRect, bool transparent); + void blit(const Graphics::Surface *surface, int16 destX, int16 destY, NRect &ddRect, bool transparent); + void drawDoubleSurface2(const Graphics::Surface *surface, NDrawRect &drawRect); + void drawUnk(const Graphics::Surface *surface, NDrawRect &drawRect, NDrawRect &sysRect, NRect &clipRect, bool transparent); +protected: + NeverhoodEngine *_vm; + Graphics::Surface *_backScreen; + uint32 _ticks; + uint32 _frameDelay; + byte *_paletteData; + bool _paletteChanged; +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_SCREEN_H */ diff --git a/engines/neverhood/smackerplayer.cpp b/engines/neverhood/smackerplayer.cpp new file mode 100644 index 0000000000..9ae7e14eed --- /dev/null +++ b/engines/neverhood/smackerplayer.cpp @@ -0,0 +1,250 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "graphics/palette.h" +#include "neverhood/smackerplayer.h" +#include "neverhood/palette.h" +#include "neverhood/resourceman.h" +#include "neverhood/scene.h" + +namespace Neverhood { + +// SmackerSurface + +SmackerSurface::SmackerSurface(NeverhoodEngine *vm) + : BaseSurface(vm, 0, 0, 0), _smackerFrame(NULL) { +} + +void SmackerSurface::draw() { + if (_smackerFrame && _visible && _drawRect.width > 0 && _drawRect.height > 0) { + _vm->_screen->drawSurface2(_smackerFrame, _drawRect, _clipRect, false); + } +} + +void SmackerSurface::setSmackerFrame(const Graphics::Surface *smackerFrame) { + _drawRect.x = 0; + _drawRect.y = 0; + _drawRect.width = smackerFrame->w; + _drawRect.height = smackerFrame->h; + // TODO: Check if _sysRect is needed at all in the reimplementation... + _sysRect.x = 0; + _sysRect.y = 0; + _sysRect.width = (smackerFrame->w + 3) & 0xFFFC; // align by 4 bytes + _sysRect.height = smackerFrame->h; + _smackerFrame = smackerFrame; +} + +// SmackerDoubleSurface + +SmackerDoubleSurface::SmackerDoubleSurface(NeverhoodEngine *vm) + : SmackerSurface(vm) { +} + +void SmackerDoubleSurface::draw() { + if (_smackerFrame && _visible && _drawRect.width > 0 && _drawRect.height > 0) { + _vm->_screen->drawDoubleSurface2(_smackerFrame, _drawRect); + } +} + +// SmackerPlayer + +SmackerPlayer::SmackerPlayer(NeverhoodEngine *vm, Scene *scene, uint32 fileHash, bool doubleSurface, bool flag) + : Entity(vm, 0), _scene(scene), _doubleSurface(doubleSurface), _dirtyFlag(false), _flag2(false), + _palette(NULL), _smackerDecoder(NULL), _smackerSurface(NULL), _stream(NULL), _smackerFirst(true), + _drawX(-1), _drawY(-1) { + + SetUpdateHandler(&SmackerPlayer::update); + open(fileHash, flag); +} + +SmackerPlayer::~SmackerPlayer() { + close(); +} + +void SmackerPlayer::open(uint32 fileHash, bool keepLastFrame) { + debug("SmackerPlayer::open(%08X)", fileHash); + + _fileHash = fileHash; + _keepLastFrame = keepLastFrame; + + close(); + + if (_doubleSurface) { + _smackerSurface = new SmackerDoubleSurface(_vm); + } else { + _smackerSurface = new SmackerSurface(_vm); + } + + _smackerFirst = true; + + _stream = _vm->_res->createStream(fileHash); + + // TODO: _keepLastFrame stuff + + _smackerDecoder = new Video::SmackerDecoder(_vm->_mixer); + _smackerDecoder->loadStream(_stream); + + _palette = new Palette(_vm); + _palette->usePalette(); + +} + +void SmackerPlayer::close() { + delete _smackerDecoder; + delete _palette; + // NOTE: The SmackerDecoder deletes the _stream + delete _smackerSurface; + _smackerDecoder = NULL; + _palette = NULL; + _stream = NULL; + _smackerSurface = NULL; +} + +void SmackerPlayer::gotoFrame(uint frameNumber) { + // TODO? +} + +uint32 SmackerPlayer::getFrameCount() { + return _smackerDecoder ? _smackerDecoder->getFrameCount() : 0; +} + +uint32 SmackerPlayer::getFrameNumber() { + return _smackerDecoder ? _smackerDecoder->getCurFrame() : 0; +} + +uint SmackerPlayer::getStatus() { + return 0; +} + +void SmackerPlayer::setDrawPos(int16 x, int16 y) { + _drawX = x; + _drawY = y; + if (_smackerSurface) { + _smackerSurface->getDrawRect().x = _drawX; + _smackerSurface->getDrawRect().y = _drawY; + } +} + +void SmackerPlayer::rewind() { + + delete _smackerDecoder; + _smackerDecoder = NULL; + _stream = NULL; + + _smackerFirst = true; + + _stream = _vm->_res->createStream(_fileHash); + + _smackerDecoder = new Video::SmackerDecoder(_vm->_mixer); + _smackerDecoder->loadStream(_stream); + +} + +void SmackerPlayer::update() { + debug(8, "SmackerPlayer::update()"); + + if (!_smackerDecoder) + return; + + if (_dirtyFlag) { + // TODO _vm->_screen->resetDirtyRects(); + _dirtyFlag = false; + } + +#if 0 + if (!_smackerDecoder->endOfVideo()) { + updateFrame(); + if (_smackerDecoder->endOfVideo() && !_keepLastFrame) { + // Inform the scene about the end of the video playback + if (_scene) { + sendMessage(_scene, 0x3002, 0); + } + _flag2 = true; + } else { + if (_smackerDecoder->endOfVideo()) { + rewind(); + updateFrame(); + } + _flag2 = false; + } + } +#endif + + if (!_smackerDecoder->endOfVideo()) { + updateFrame(); + } else if (!_keepLastFrame) { + // Inform the scene about the end of the video playback + if (_scene) { + sendMessage(_scene, 0x3002, 0); + } + _flag2 = true; + } else { + rewind(); + updateFrame(); + _flag2 = false; + } + +} + +void SmackerPlayer::updateFrame() { + const Graphics::Surface *smackerFrame = _smackerDecoder->decodeNextFrame(); + + if (_smackerFirst) { + _smackerSurface->setSmackerFrame(smackerFrame); + if (_drawX < 0 || _drawY < 0) { + if (_doubleSurface) { + _drawX = 320 - _smackerDecoder->getWidth(); + _drawY = 240 - _smackerDecoder->getHeight(); + } else { + _drawX = (640 - _smackerDecoder->getWidth()) / 2; + _drawY = (480 - _smackerDecoder->getHeight()) / 2; + } + } + _smackerSurface->getDrawRect().x = _drawX; + _smackerSurface->getDrawRect().y = _drawY; + _smackerFirst = false; + } + + if (_doubleSurface) { + // TODO + } + + // TODO _vm->_screen->_skipUpdate = true; + _dirtyFlag = true; + + if (_smackerDecoder->hasDirtyPalette()) { + updatePalette(); + } +} + +void SmackerPlayer::updatePalette() { + byte tempPalette[1024]; + const byte *smackerPalette = _smackerDecoder->getPalette(); + for (int i = 0; i < 256; i++) { + tempPalette[i * 4 + 0] = smackerPalette[i * 3 + 0]; + tempPalette[i * 4 + 1] = smackerPalette[i * 3 + 1]; + tempPalette[i * 4 + 2] = smackerPalette[i * 3 + 2]; + } + _palette->copyPalette(tempPalette, 0, 256, 0); +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/smackerplayer.h b/engines/neverhood/smackerplayer.h new file mode 100644 index 0000000000..883cb52245 --- /dev/null +++ b/engines/neverhood/smackerplayer.h @@ -0,0 +1,83 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef NEVERHOOD_SMACKERPLAYER_H +#define NEVERHOOD_SMACKERPLAYER_H + +#include "video/smk_decoder.h" +#include "neverhood/neverhood.h" +#include "neverhood/entity.h" + +namespace Neverhood { + +class Scene; +class Palette; + +class SmackerSurface : public BaseSurface { +public: + SmackerSurface(NeverhoodEngine *vm); + virtual void draw(); + void setSmackerFrame(const Graphics::Surface *smackerFrame); +protected: + const Graphics::Surface *_smackerFrame; +}; + +class SmackerDoubleSurface : public SmackerSurface { +public: + SmackerDoubleSurface(NeverhoodEngine *vm); + virtual void draw(); +}; + +class SmackerPlayer : public Entity { +public: + SmackerPlayer(NeverhoodEngine *vm, Scene *scene, uint32 fileHash, bool doubleSurface, bool flag); + ~SmackerPlayer(); + BaseSurface *getSurface() { return _smackerSurface; } + void open(uint32 fileHash, bool keepLastFrame); + void close(); + void gotoFrame(uint frameNumber); + uint32 getFrameCount(); + uint32 getFrameNumber(); + uint getStatus(); + void setDrawPos(int16 x, int16 y); + void rewind(); +protected: + Scene *_scene; + Palette *_palette; + Video::SmackerDecoder *_smackerDecoder; + SmackerSurface *_smackerSurface; + uint32 _fileHash; + bool _smackerFirst; + bool _doubleSurface; + Common::SeekableReadStream *_stream; + bool _keepLastFrame; + bool _flag2; + bool _dirtyFlag; + int _drawX, _drawY; + void update(); + void updateFrame(); + void updatePalette(); +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_SMACKERPLAYER_H */ diff --git a/engines/neverhood/smackerscene.cpp b/engines/neverhood/smackerscene.cpp new file mode 100644 index 0000000000..fe78e5021e --- /dev/null +++ b/engines/neverhood/smackerscene.cpp @@ -0,0 +1,122 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "neverhood/smackerscene.h" + +namespace Neverhood { + +SmackerScene::SmackerScene(NeverhoodEngine *vm, Module *parentModule, bool doubleSurface, bool flag1, bool canAbort) + : Scene(vm, parentModule, true), _doubleSurface(doubleSurface), _flag1(flag1), _canAbort(canAbort), _fieldDF(false), + _fileHashListIndex(-1), _fileHashList(NULL), _playNextVideoFlag(false) { + + debug("SmackerScene::SmackerScene(%d, %d, %d)", doubleSurface, flag1, canAbort); + + // NOTE: Merged from SmackerScene::init, maybe split again if needed (incl. parameter flags) + + if (getGlobalVar(0x06C02850)) { + _flag1 = true; + _canAbort = true; + } + + if (_doubleSurface) { + _vm->_screen->clear(); + } + + _fileHash[0] = 0; + _fileHash[1] = 0; + + SetUpdateHandler(&SmackerScene::update); + SetMessageHandler(&SmackerScene::handleMessage); + +} + +SmackerScene::~SmackerScene() { + +} + +void SmackerScene::setFileHash(uint32 fileHash) { + debug("SmackerScene::setFileHash(%08X)", fileHash); + _fileHash[0] = fileHash; + _fileHashList = _fileHash; +} + +void SmackerScene::setFileHashList(const uint32 *fileHashList) { + debug("SmackerScene::setFileHashList(...)"); + _fileHashList = fileHashList; +} + +void SmackerScene::nextVideo() { + debug("SmackerScene::nextVideo()"); + + _fileHashListIndex++; + + if (_fileHashList && _fileHashList[_fileHashListIndex] != 0) { + uint32 smackerFileHash = _fileHashList[_fileHashListIndex]; + if (_vm->_res->getResourceTypeByHash(smackerFileHash) != 10) { + // Not a Smacker file + sendMessage(_parentModule, 0x1009, 0); + return; + } + _fieldDF = getSubVar(0x00800410, smackerFileHash); + if (!_fieldDF) { + setSubVar(0x00800410, smackerFileHash, 1); + } + if (_fileHashListIndex == 0) { + _smackerPlayer = addSmackerPlayer(new SmackerPlayer(_vm, this, smackerFileHash, _doubleSurface, false)); + // TODO? Screen.hSmack = _smackerPlayer; + } else { + _smackerPlayer->open(smackerFileHash, false); + } + } else { + sendMessage(_parentModule, 0x1009, 0); + } + +} + +void SmackerScene::update() { + if (_playNextVideoFlag) { + nextVideo(); + _playNextVideoFlag = false; + } + Scene::update(); +} + +uint32 SmackerScene::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x0009: + if ((_fieldDF && _flag1) || (_canAbort && _flag1)) + _playNextVideoFlag = true; + break; + case 0x000C: + if (_canAbort) { + sendMessage(_parentModule, 0x1009, 0); + } + break; + case 0x3002: + _playNextVideoFlag = true; + break; + } + return messageResult; +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/smackerscene.h b/engines/neverhood/smackerscene.h new file mode 100644 index 0000000000..05237664c3 --- /dev/null +++ b/engines/neverhood/smackerscene.h @@ -0,0 +1,54 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef NEVERHOOD_SMACKERSCENE_H +#define NEVERHOOD_SMACKERSCENE_H + +#include "neverhood/neverhood.h" +#include "neverhood/resourceman.h" +#include "neverhood/scene.h" + +namespace Neverhood { + +class SmackerScene : public Scene { +public: + SmackerScene(NeverhoodEngine *vm, Module *parentModule, bool doubleSurface, bool flag1, bool canAbort); + virtual ~SmackerScene(); + void setFileHash(uint32 fileHash); + void setFileHashList(const uint32 *fileHashList); + void nextVideo(); +protected: + bool _doubleSurface; + bool _flag1; + bool _canAbort; + bool _fieldDF; + bool _playNextVideoFlag; + int _fileHashListIndex; + const uint32 *_fileHashList; + uint32 _fileHash[2]; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_SMACKERSCENE_H */ diff --git a/engines/neverhood/sprite.cpp b/engines/neverhood/sprite.cpp new file mode 100644 index 0000000000..697bd6e262 --- /dev/null +++ b/engines/neverhood/sprite.cpp @@ -0,0 +1,528 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "neverhood/sprite.h" + +namespace Neverhood { + +// Sprite + +Sprite::Sprite(NeverhoodEngine *vm, int objectPriority) + : Entity(vm, objectPriority), _x(0), _y(0), _spriteUpdateCb(NULL), _filterXCb(NULL), _filterYCb(NULL), + _dataResource(vm), _doDeltaX(false), _doDeltaY(false), _needRefresh(false), _flags(0) { + + _name = "Sprite"; + SetMessageHandler(&Sprite::handleMessage); + +} + +Sprite::~Sprite() { + delete _surface; +} + +void Sprite::processDelta() { + if (_doDeltaX) { + _rect.x1 = _x - _deltaRect.x - _deltaRect.width + 1; + _rect.x2 = _x - _deltaRect.x; + } else { + _rect.x1 = _x + _deltaRect.x; + _rect.x2 = _x + _deltaRect.x + _deltaRect.width - 1; + } + if (_doDeltaY) { + _rect.y1 = _y - _deltaRect.y - _deltaRect.height + 1; + _rect.y2 = _y - _deltaRect.y; + } else { + _rect.y1 = _y + _deltaRect.y; + _rect.y2 = _y + _deltaRect.y + _deltaRect.height - 1; + } +} + +void Sprite::setDoDeltaX(int type) { + // Clear, set or toggle + _doDeltaX = type == 2 ? !_doDeltaX : type == 1; +} + +void Sprite::setDoDeltaY(int type) { + // Clear, set or toggle + _doDeltaY = type == 2 ? !_doDeltaY : type == 1; +} + +bool Sprite::isPointInside(int16 x, int16 y) { + return x >= _rect.x1 && x <= _rect.x2 && y >= _rect.y1 && y <= _rect.y2; +} + +bool Sprite::checkCollision(NRect &rect) { + return (_rect.x1 < rect.x2) && (rect.x1 < _rect.x2) && (_rect.y1 < rect.y2) && (rect.y1 < _rect.y2); +} + +uint32 Sprite::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + switch (messageNum) { + case 0x0005: + // TODO: Draw debug marker (?) + // TODO g_Screen->drawLine(_x - 5, _y, _x + 6, _y); + // TODO g_Screen->drawLine(_x, _y - 5, _x, _y + 6); + break; + } + return 0; +} + +void Sprite::loadDataResource(uint32 fileHash) { + _dataResource.load(fileHash); +} + +void Sprite::createSurface(int surfacePriority, int16 width, int16 height) { + _surface = new BaseSurface(_vm, surfacePriority, width, height); +} + +int16 Sprite::defFilterY(int16 y) { + // TODO return y - g_screen->field_26; + return y; +} + +void Sprite::setClipRect(int16 x1, int16 y1, int16 x2, int16 y2) { + NRect &clipRect = _surface->getClipRect(); + clipRect.x1 = x1; + clipRect.y1 = y1; + clipRect.x2 = x2; + clipRect.y2 = y2; +} + +void Sprite::setClipRect(NRect& clipRect) { + _surface->getClipRect() = clipRect; +} + +void Sprite::setClipRect(NDrawRect& drawRect) { + setClipRect(drawRect.x, drawRect.y, drawRect.x2(), drawRect.y2()); +} + +// StaticSprite + +StaticSprite::StaticSprite(NeverhoodEngine *vm, int objectPriority) + : Sprite(vm, objectPriority), _spriteResource(vm) { + + _name = "StaticSprite"; + +} + +StaticSprite::StaticSprite(NeverhoodEngine *vm, const char *filename, int surfacePriority, int16 x, int16 y, int16 width, int16 height) + : Sprite(vm, 0), _spriteResource(vm) { + + _name = "StaticSprite"; + init(calcHash(filename), surfacePriority, x, y, width, height); + +} + +StaticSprite::StaticSprite(NeverhoodEngine *vm, uint32 fileHash, int surfacePriority, int16 x, int16 y, int16 width, int16 height) + : Sprite(vm, 0), _spriteResource(vm) { + _name = "StaticSprite"; + init(fileHash, surfacePriority, x, y, width, height); +} + +void StaticSprite::init(uint32 fileHash, int surfacePriority, int16 x, int16 y, int16 width, int16 height) { + + _spriteResource.load2(fileHash); + + if (width == 0) + width = _spriteResource.getDimensions().width; + + if (height == 0) + height = _spriteResource.getDimensions().height; + + createSurface(surfacePriority, width, height); + + _x = x == kDefPosition ? _spriteResource.getPosition().x : x; + _y = y == kDefPosition ? _spriteResource.getPosition().y : y; + + _drawRect.x = 0; + _drawRect.y = 0; + _drawRect.width = width; + _drawRect.height = height; + + _needRefresh = true; + + update(); + +} + +void StaticSprite::update() { + + if (!_surface) + return; + + if (_doDeltaX) { + _surface->getDrawRect().x = filterX(_x - _drawRect.x - _drawRect.width + 1); + } else { + _surface->getDrawRect().x = filterX(_x + _drawRect.x); + } + + if (_doDeltaY) { + _surface->getDrawRect().y = filterY(_y - _drawRect.y - _drawRect.height + 1); + } else { + _surface->getDrawRect().y = filterY(_y + _drawRect.y); + } + + if (_needRefresh) { + _surface->drawSpriteResourceEx(_spriteResource, _doDeltaX, _doDeltaY, _drawRect.width, _drawRect.height); + _needRefresh = false; + } + +} + +void StaticSprite::load(uint32 fileHash, bool dimensions, bool position) { + + _spriteResource.load2(fileHash); + + if (dimensions) { + _drawRect.x = 0; + _drawRect.y = 0; + _drawRect.width = _spriteResource.getDimensions().width; + _drawRect.height = _spriteResource.getDimensions().height; + } + + if (position) { + _x = _spriteResource.getPosition().x; + _y = _spriteResource.getPosition().y; + } + + _needRefresh = true; + +} + +// AnimatedSprite + +AnimatedSprite::AnimatedSprite(NeverhoodEngine *vm, int objectPriority) + : Sprite(vm, objectPriority), _animResource(vm) { + + init(); +} + +AnimatedSprite::AnimatedSprite(NeverhoodEngine *vm, uint32 fileHash, int surfacePriority, int16 x, int16 y) + : Sprite(vm, 1100), _animResource(vm) { + + init(); + SetUpdateHandler(&AnimatedSprite::update); + createSurface1(fileHash, surfacePriority); + _x = x; + _y = y; + setFileHash(fileHash, 0, -1); +} + +void AnimatedSprite::init() { + _name = "AnimatedSprite"; + _counter = 0; + _fileHash1 = 0; + _deltaX = 0; + _deltaY = 0; + _fileHash2 = 0; + // TODO _callbackList = 0; + _frameIndex3 = 0; + _frameIndex = 0; + _hashListIndex = -1; + _finalizeStateCb = NULL; + _currStateCb = NULL; + _nextStateCb = NULL; + _newHashListIndex = -1; + _fileHash4 = 0; + _flag = false; + _replOldColor = 0; + _replNewColor = 0; + _animResource.setReplEnabled(false); + _playBackwards = false; +} + +void AnimatedSprite::update() { + updateAnim(); + handleSpriteUpdate(); + updatePosition(); +} + +void AnimatedSprite::updateDeltaXY() { + if (_doDeltaX) { + _x -= _deltaX; + } else { + _x += _deltaX; + } + if (_doDeltaY) { + _y -= _deltaY; + } else { + _y += _deltaY; + } + _deltaX = 0; + _deltaY = 0; + processDelta(); +} + +void AnimatedSprite::setRepl(byte oldColor, byte newColor) { + _replOldColor = oldColor; + _replNewColor = newColor; + _animResource.setReplEnabled(true); +} + +void AnimatedSprite::clearRepl() { + _replOldColor = 0; + _replNewColor = 0; + _animResource.setReplEnabled(false); +} + +void AnimatedSprite::updateAnim() { + + _flag = false; + + if (_fileHash1 == 0) { + if (_newHashListIndex != -1) { + _hashListIndex = _newHashListIndex == -2 ? _animResource.getFrameCount() - 1 : _newHashListIndex; + _newHashListIndex = -1; + } else if (_fileHash4 != 0) { + _hashListIndex = MAX<int16>(0, _animResource.getFrameIndex(_fileHash4)); + _fileHash4 = 0; + } + if (_fileHash1 == 0 && _frameIndex != _hashListIndex) { + if (_counter != 0) + _counter--; + if (_counter == 0 && _animResource.getFrameCount() != 0) { + + if (_fileHash2 != 0) { + if (_animResource.loadInternal(_fileHash2)) { + _currAnimFileHash = _fileHash2; + } else { + _animResource.loadInternal(calcHash("sqDefault")); + _currAnimFileHash = 0; + } + if (_replOldColor != _replNewColor) { + _animResource.setRepl(_replOldColor, _replNewColor); + } + _fileHash2 = 0; + if (_animStatus != 0) { + _frameIndex = _fileHash6 != 0 ? MAX<int16>(0, _animResource.getFrameIndex(_fileHash6)) : 0; + _frameIndex2 = _fileHash5 != 0 ? MAX<int16>(0, _animResource.getFrameIndex(_fileHash5)) : _animResource.getFrameCount() - 1; + } else { + _frameIndex = _frameIndex3 != -1 ? _frameIndex3 : _animResource.getFrameCount() - 1; + _frameIndex2 = _frameIndex4 != -1 ? _frameIndex4 : _animResource.getFrameCount() - 1; + } + } else { + updateFrameIndex(); + } + if (_fileHash1 == 0) + updateFrameInfo(); + } + } + } + + if (_fileHash1 != 0) { + if (_animStatus == 2) { + _hashListIndex = _frameIndex; + } else { + if (_animStatus == 1) { + if (_animResource.loadInternal(_fileHash1)) { + _currAnimFileHash = _fileHash1; + } else { + _animResource.loadInternal(calcHash("sqDefault")); + _currAnimFileHash = 0; + } + if (_replOldColor != _replNewColor) { + _animResource.setRepl(_replOldColor, _replNewColor); + } + _fileHash1 = 0; + _frameIndex = _fileHash6 != 0 ? MAX<int16>(0, _animResource.getFrameIndex(_fileHash6)) : 0; + _frameIndex2 = _fileHash5 != 0 ? MAX<int16>(0, _animResource.getFrameIndex(_fileHash5)) : _animResource.getFrameCount() - 1; + } else { + if (_animResource.loadInternal(_fileHash1)) { + _currAnimFileHash = _fileHash1; + } else { + _animResource.loadInternal(calcHash("sqDefault")); + _currAnimFileHash = 0; + } + if (_replOldColor != _replNewColor) { + _animResource.setRepl(_replOldColor, _replNewColor); + } + _fileHash1 = 0; + _frameIndex = _frameIndex3 != -1 ? _frameIndex3 : _animResource.getFrameCount() - 1; + _frameIndex2 = _frameIndex4 != -1 ? _frameIndex4 : _animResource.getFrameCount() - 1; + } + updateFrameInfo(); + } + + if (_newHashListIndex != -1) { + _hashListIndex = _newHashListIndex == -2 ? _animResource.getFrameCount() - 1 : _newHashListIndex; + _newHashListIndex = -1; + } else if (_fileHash4 != 0) { + _hashListIndex = MAX<int16>(0, _animResource.getFrameIndex(_fileHash4)); + _fileHash4 = 0; + } + + } + +} + +void AnimatedSprite::updatePosition() { + + if (!_surface) + return; + + if (_doDeltaX) { + _surface->getDrawRect().x = filterX(_x - _drawRect.x - _drawRect.width + 1); + } else { + _surface->getDrawRect().x = filterX(_x + _drawRect.x); + } + + if (_doDeltaY) { + _surface->getDrawRect().y = filterY(_y - _drawRect.y - _drawRect.height + 1); + } else { + _surface->getDrawRect().y = filterY(_y + _drawRect.y); + } + + if (_needRefresh) { + _surface->drawAnimResource(_animResource, _frameIndex, _doDeltaX, _doDeltaY, _drawRect.width, _drawRect.height); + _needRefresh = false; + } + +} + +void AnimatedSprite::updateFrameIndex() { + if (!_playBackwards) { + if (_frameIndex < _frameIndex2) { + _frameIndex++; + } else { + // Inform self about end of current animation + // The caller can then e.g. set a new animation fileHash + sendMessage(this, 0x3002, 0); + if (_fileHash1 == 0) + _frameIndex = 0; + } + } else { + if (_frameIndex > 0) { + _frameIndex--; + } else { + sendMessage(this, 0x3002, 0); + if (_fileHash1 == 0) + _frameIndex = _frameIndex2; + } + } +} + +void AnimatedSprite::updateFrameInfo() { + debug(8, "AnimatedSprite::updateFrameInfo()"); + + const AnimFrameInfo &frameInfo = _animResource.getFrameInfo(_frameIndex); + + _flag = true; + _drawRect = frameInfo.rect; + _deltaX = frameInfo.deltaX; + _deltaY = frameInfo.deltaY; + _deltaRect = frameInfo.deltaRect; + _counter = frameInfo.counter; + + processDelta(); + + _needRefresh = true; + + if (frameInfo.frameHash != 0) { + sendMessage(this, 0x100D, frameInfo.frameHash); + } + +} + +void AnimatedSprite::createSurface1(uint32 fileHash, int surfacePriority) { + NDimensions dimensions = _animResource.loadSpriteDimensions(fileHash); + _surface = new BaseSurface(_vm, surfacePriority, dimensions.width, dimensions.height); +} + +void AnimatedSprite::setFileHash(uint32 fileHash, int16 frameIndex3, int16 frameIndex4) { + debug(2, "AnimatedSprite::setFileHash(%08X, %d, %d)", fileHash, frameIndex3, frameIndex4); + _fileHash1 = fileHash; + _frameIndex3 = frameIndex3; + _frameIndex4 = frameIndex4; + _fileHash4 = 0; + _animStatus = 0; + _playBackwards = false; + _newHashListIndex = -1; + _hashListIndex = -1; +} + +void AnimatedSprite::stopAnimation() { + _fileHash1 = 1; + _animStatus = 2; +} + +void AnimatedSprite::setFileHash2(uint32 fileHash, uint32 fileHash6, uint32 fileHash5) { + debug(2, "AnimatedSprite::setFileHash2(%08X, %08X, %08X)", fileHash, fileHash6, fileHash5); + _fileHash1 = fileHash; + _fileHash6 = fileHash6; + _fileHash5 = fileHash5; + _fileHash4 = 0; + _animStatus = 1; + _playBackwards = false; + _newHashListIndex = -1; + _hashListIndex = -1; +} + +void AnimatedSprite::setFileHash3(uint32 fileHash2, uint32 fileHash6, uint32 fileHash5) { + _fileHash2 = fileHash2; + _fileHash6 = fileHash6; + _fileHash5 = fileHash5; + _fileHash4 = 0; + _animStatus = 1; + _playBackwards = false; + _newHashListIndex = -1; + _hashListIndex = -1; +} + +void AnimatedSprite::setFinalizeState(AnimationCb finalizeStateCb) { + if (_finalizeStateCb) + (this->*_finalizeStateCb)(); + _finalizeStateCb = finalizeStateCb; +} + +void AnimatedSprite::gotoState(AnimationCb currStateCb) { + if (_finalizeStateCb) { + AnimationCb cb = _finalizeStateCb; + _finalizeStateCb = NULL; + (this->*cb)(); + } + // TODO _callbackList = NULL; + _nextStateCb = NULL; + _currStateCb = currStateCb; + if (_currStateCb) + (this->*_currStateCb)(); +} + +void AnimatedSprite::removeCallbacks() { + if (_finalizeStateCb) { + AnimationCb cb = _finalizeStateCb; + _finalizeStateCb = NULL; + (this->*cb)(); + } + if (_nextStateCb) { + _currStateCb = _nextStateCb; + _nextStateCb = NULL; + debug("Fire _nextStateCb '%s'", _nextStateCbName.c_str()); + (this->*_currStateCb)(); +#if 0 // TODO + } else if (_callbackList) { + removeCallbackList(); +#endif + } else { + _currStateCb = NULL; + } +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/sprite.h b/engines/neverhood/sprite.h new file mode 100644 index 0000000000..aa2272464e --- /dev/null +++ b/engines/neverhood/sprite.h @@ -0,0 +1,178 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef NEVERHOOD_SPRITE_H +#define NEVERHOOD_SPRITE_H + +#include "neverhood/neverhood.h" +#include "neverhood/entity.h" +#include "neverhood/graphics.h" +#include "neverhood/resource.h" + +namespace Neverhood { + +#define SetSpriteCallback(callback) _spriteUpdateCb = static_cast <void (Sprite::*)(void)> (callback); debug(2, "SetSpriteCallback(" #callback ")"); _spriteUpdateCbName = #callback +#define SetFilterX(callback) _filterXCb = static_cast <int16 (Sprite::*)(int16)> (callback); debug(2, "SetFilterX(" #callback ")") +#define SetFilterY(callback) _filterYCb = static_cast <int16 (Sprite::*)(int16)> (callback); debug(2, "SetFilterY(" #callback ")") + +const int16 kDefPosition = -32768; + +class Sprite : public Entity { +public: + Sprite(NeverhoodEngine *vm, int objectPriority); + ~Sprite(); + void init() {} + BaseSurface *getSurface() { return _surface; } + void processDelta(); + void setDoDeltaX(int type); + void setDoDeltaY(int type); + bool isPointInside(int16 x, int16 y); + bool checkCollision(NRect &rect); + int16 getX() const { return _x; } + int16 getY() const { return _y; } + void setX(int16 value) { _x = value; } + void setY(int16 value) { _y = value; } + uint16 getFlags() const { return _flags; } + bool isDoDeltaX() const { return _doDeltaX; } + bool isDoDeltaY() const { return _doDeltaY; } + NRect& getRect() { return _rect; } + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void loadDataResource(uint32 fileHash); + int16 defFilterY(int16 y); + void setVisible(bool value) { _surface->setVisible(value); } + NDrawRect& getDrawRect() { return _surface->getDrawRect(); } + // Some shortcuts to set the clipRect + NRect& getClipRect() { return _surface->getClipRect(); } + void setClipRect(int16 x1, int16 y1, int16 x2, int16 y2); + void setClipRect(NRect& clipRect); + void setClipRect(NDrawRect& drawRect); +protected: + void (Sprite::*_spriteUpdateCb)(); + Common::String _spriteUpdateCbName; // For debugging purposes + int16 (Sprite::*_filterXCb)(int16); + int16 (Sprite::*_filterYCb)(int16); + BaseSurface *_surface; + int16 _x, _y; + bool _doDeltaX, _doDeltaY; + bool _needRefresh; + //0000002B field_2B db ? + //0000002C field2C dd ? // unused + NDrawRect _drawRect; + NDrawRect _deltaRect; + NRect _rect; + uint16 _flags; + //0000004A field4A dw ? // seems to be unused except in ctor + DataResource _dataResource; + void createSurface(int surfacePriority, int16 width, int16 height); + void handleSpriteUpdate() { + if (_spriteUpdateCb) + (this->*_spriteUpdateCb)(); + } + int16 filterX(int16 x) { + return _filterXCb ? (this->*_filterXCb)(x) : x; + } + int16 filterY(int16 y) { + return _filterYCb ? (this->*_filterYCb)(y) : y; + } +}; + +class StaticSprite : public Sprite { +public: + StaticSprite(NeverhoodEngine *vm, int objectPriority); + StaticSprite(NeverhoodEngine *vm, const char *filename, int surfacePriority, int16 x = kDefPosition, int16 y = kDefPosition, int16 width = 0, int16 height = 0); + StaticSprite(NeverhoodEngine *vm, uint32 fileHash, int surfacePriority, int16 x = kDefPosition, int16 y = kDefPosition, int16 width = 0, int16 height = 0); + void load(uint32 fileHash, bool dimensions, bool position); + void update(); +protected: + SpriteResource _spriteResource; + void init(uint32 fileHash, int surfacePriority, int16 x = kDefPosition, int16 y = kDefPosition, int16 width = 0, int16 height = 0); +}; + +#define AnimationCallback(callback) static_cast <void (AnimatedSprite::*)()> (callback) +#define GotoState(callback) gotoState(static_cast <void (AnimatedSprite::*)()> (callback)) +#define NextState(callback) _nextStateCb = static_cast <void (AnimatedSprite::*)(void)> (callback); debug(2, "NextState(" #callback ")"); _nextStateCbName = #callback +#define FinalizeState(callback) setFinalizeState(static_cast <void (AnimatedSprite::*)()> (callback)); + +class AnimatedSprite : public Sprite { +public: + AnimatedSprite(NeverhoodEngine *vm, int objectPriority); + AnimatedSprite(NeverhoodEngine *vm, uint32 fileHash, int surfacePriority, int16 x, int16 y); + void update(); + void updateDeltaXY(); + void setRepl(byte oldColor, byte newColor); + void clearRepl(); + uint32 getCurrAnimFileHash() const { return _currAnimFileHash; } + int16 getFrameIndex() const { return _frameIndex; } + int16 getFrameIndex(uint32 frameHash) { return _animResource.getFrameIndex(frameHash); } + void setNewHashListIndex(int value) { _newHashListIndex = value; } + void setFileHash(uint32 fileHash, int16 frameIndex3, int16 frameIndex4); +protected: + typedef void (AnimatedSprite::*AnimationCb)(); + AnimResource _animResource; + uint32 _currAnimFileHash; + uint32 _fileHash1; + uint32 _fileHash2; + int16 _frameIndex; + int16 _frameIndex3; + int16 _frameIndex2; + int16 _frameIndex4; + uint32 _fileHash6; + uint32 _fileHash5; + int16 _animStatus; + int16 _counter; + int _hashListIndex; + int _newHashListIndex; + uint32 _fileHash4; + int16 _deltaX, _deltaY; + byte _replOldColor; + byte _replNewColor; + bool _playBackwards; + bool _flag; + /* TODO + callbackListIndex dw ? + callbackListCount dw ? + callbackList dd ? + */ + AnimationCb _finalizeStateCb; + AnimationCb _currStateCb; + AnimationCb _nextStateCb; + // For debugging purposes + Common::String _finalizeStateCbName; + Common::String _currStateCbName; + Common::String _nextStateCbName; + void init(); + void updateAnim(); + void updatePosition(); + void updateFrameIndex(); + void updateFrameInfo(); + void createSurface1(uint32 fileHash, int surfacePriority); + void stopAnimation(); + void setFileHash2(uint32 fileHash, uint32 fileHash6, uint32 fileHash5); + void setFileHash3(uint32 fileHash2, uint32 fileHash6, uint32 fileHash5); + void setFinalizeState(AnimationCb finalizeStateCb); + void gotoState(AnimationCb currStateCb); + void removeCallbacks(); +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_SPRITE_H */ diff --git a/engines/neverhood/staticdata.cpp b/engines/neverhood/staticdata.cpp new file mode 100644 index 0000000000..147a319d2e --- /dev/null +++ b/engines/neverhood/staticdata.cpp @@ -0,0 +1,200 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "neverhood/staticdata.h" + +namespace Neverhood { + +StaticData::StaticData() { +} + +StaticData::~StaticData() { +} + +void StaticData::load(const char *filename) { + + Common::File fd; + + if (!fd.open(filename)) + error("StaticData::load() Could not open %s", filename); + + fd.readUint32LE(); // magic + fd.readUint32LE(); // version + + // Load message lists + uint32 messageListsCount = fd.readUint32LE(); + debug("messageListsCount: %d", messageListsCount); + for (uint32 i = 0; i < messageListsCount; i++) { + MessageList *messageList = new MessageList(); + uint32 id = fd.readUint32LE(); + uint32 itemCount = fd.readUint32LE(); + for (uint32 itemIndex = 0; itemIndex < itemCount; itemIndex++) { + MessageItem messageItem; + messageItem.messageNum = fd.readUint16LE(); + messageItem.messageValue = fd.readUint32LE(); + messageList->push_back(messageItem); + } + _messageLists[id] = messageList; + } + + // Load rect lists + uint32 rectListsCount = fd.readUint32LE(); + debug("rectListsCount: %d", rectListsCount); + for (uint32 i = 0; i < rectListsCount; i++) { + RectList *rectList = new RectList(); + uint32 id = fd.readUint32LE(); + uint32 itemCount = fd.readUint32LE(); + for (uint32 itemIndex = 0; itemIndex < itemCount; itemIndex++) { + RectItem rectItem; + rectItem.rect.x1 = fd.readUint16LE(); + rectItem.rect.y1 = fd.readUint16LE(); + rectItem.rect.x2 = fd.readUint16LE(); + rectItem.rect.y2 = fd.readUint16LE(); + uint32 subItemCount = fd.readUint32LE(); + rectItem.subRects.reserve(subItemCount); + for (uint32 subItemIndex = 0; subItemIndex < subItemCount; subItemIndex++) { + SubRectItem subRectItem; + subRectItem.rect.x1 = fd.readUint16LE(); + subRectItem.rect.y1 = fd.readUint16LE(); + subRectItem.rect.x2 = fd.readUint16LE(); + subRectItem.rect.y2 = fd.readUint16LE(); + subRectItem.messageListId = fd.readUint32LE(); + rectItem.subRects.push_back(subRectItem); + } + rectList->push_back(rectItem); + } + _rectLists[id] = rectList; + } + + // Load hit rects + uint32 hitRectListsCount = fd.readUint32LE(); + debug("hitRectListsCount: %d", hitRectListsCount); + for (uint32 i = 0; i < hitRectListsCount; i++) { + HitRectList *hitRectList = new HitRectList(); + uint32 id = fd.readUint32LE(); + uint32 itemCount = fd.readUint32LE(); + for (uint32 itemIndex = 0; itemIndex < itemCount; itemIndex++) { + HitRect hitRect; + hitRect.rect.x1 = fd.readUint16LE(); + hitRect.rect.y1 = fd.readUint16LE(); + hitRect.rect.x2 = fd.readUint16LE(); + hitRect.rect.y2 = fd.readUint16LE(); + hitRect.type = fd.readUint16LE(); + hitRectList->push_back(hitRect); + } + _hitRectLists[id] = hitRectList; + } + + // Load navigation lists + uint32 navigationListsCount = fd.readUint32LE(); + debug("navigationListsCount: %d", navigationListsCount); + for (uint32 i = 0; i < navigationListsCount; i++) { + NavigationList *navigationList = new NavigationList(); + uint32 id = fd.readUint32LE(); + uint32 itemCount = fd.readUint32LE(); + for (uint32 itemIndex = 0; itemIndex < itemCount; itemIndex++) { + NavigationItem navigationItem; + navigationItem.fileHash = fd.readUint32LE(); + navigationItem.leftSmackerFileHash = fd.readUint32LE(); + navigationItem.rightSmackerFileHash = fd.readUint32LE(); + navigationItem.middleSmackerFileHash = fd.readUint32LE(); + navigationItem.interactive = fd.readByte(); + navigationItem.middleFlag = fd.readByte(); + navigationItem.mouseCursorFileHash = fd.readUint32LE(); + navigationList->push_back(navigationItem); + } + _navigationLists[id] = navigationList; + } + + // Load SceneInfo140 items + uint32 sceneInfo140ItemsCount = fd.readUint32LE(); + debug("sceneInfo140ItemsCount: %d", sceneInfo140ItemsCount); + for (uint32 i = 0; i < sceneInfo140ItemsCount; i++) { + SceneInfo140 *sceneInfo140 = new SceneInfo140(); + uint32 id = fd.readUint32LE(); + sceneInfo140->bgFilename1 = fd.readUint32LE(); + sceneInfo140->bgFilename2 = fd.readUint32LE(); + sceneInfo140->txFilename = fd.readUint32LE(); + sceneInfo140->bgFilename3 = fd.readUint32LE(); + sceneInfo140->xPosIndex = fd.readByte(); + sceneInfo140->count = fd.readByte(); + _sceneInfo140Items[id] = sceneInfo140; + } + + // Load SceneInfo2700 items + uint32 sceneInfo2700ItemsCount = fd.readUint32LE(); + debug("sceneInfo2700ItemsCount: %d", sceneInfo2700ItemsCount); + for (uint32 i = 0; i < sceneInfo2700ItemsCount; i++) { + SceneInfo2700 *sceneInfo2700 = new SceneInfo2700(); + uint32 id = fd.readUint32LE(); + sceneInfo2700->bgFilename = fd.readUint32LE(); + sceneInfo2700->class437Filename = fd.readUint32LE(); + sceneInfo2700->dataResourceFilename = fd.readUint32LE(); + sceneInfo2700->pointListName = fd.readUint32LE(); + sceneInfo2700->rectListName = fd.readUint32LE(); + sceneInfo2700->exPaletteFilename2 = fd.readUint32LE(); + sceneInfo2700->exPaletteFilename1 = fd.readUint32LE(); + sceneInfo2700->mouseCursorFilename = fd.readUint32LE(); + sceneInfo2700->which1 = fd.readUint16LE(); + sceneInfo2700->which2 = fd.readUint16LE(); + _sceneInfo2700Items[id] = sceneInfo2700; + } + +} + +HitRectList *StaticData::getHitRectList(uint32 id) { + if (!_hitRectLists[id]) + error("StaticData::getHitRectList() HitRectList with id %08X not found", id); + return _hitRectLists[id]; +} + +RectList *StaticData::getRectList(uint32 id) { + if (!_rectLists[id]) + error("StaticData::getRectList() RectList with id %08X not found", id); + return _rectLists[id]; +} + +MessageList *StaticData::getMessageList(uint32 id) { + if (!_messageLists[id]) + error("StaticData::getMessageList() MessageList with id %08X not found", id); + return _messageLists[id]; +} + +NavigationList *StaticData::getNavigationList(uint32 id) { + if (!_navigationLists[id]) + error("StaticData::getNavigationList() NavigationList with id %08X not found", id); + return _navigationLists[id]; +} + +SceneInfo140 *StaticData::getSceneInfo140Item(uint32 id) { + if (!_sceneInfo140Items[id]) + error("StaticData::getSceneInfo140Item() SceneInfo140 with id %08X not found", id); + return _sceneInfo140Items[id]; +} + +SceneInfo2700 *StaticData::getSceneInfo2700(uint32 id) { + if (!_sceneInfo2700Items[id]) + error("StaticData::getSceneInfo2700() SceneInfo2700 with id %08X not found", id); + return _sceneInfo2700Items[id]; +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/staticdata.h b/engines/neverhood/staticdata.h new file mode 100644 index 0000000000..2a2db556e0 --- /dev/null +++ b/engines/neverhood/staticdata.h @@ -0,0 +1,116 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef NEVERHOOD_STATICDATA_H +#define NEVERHOOD_STATICDATA_H + +#include "common/array.h" +#include "common/hashmap.h" +#include "neverhood/neverhood.h" +#include "neverhood/graphics.h" + +namespace Neverhood { + +struct HitRect { + NRect rect; + uint16 type; +}; + +typedef Common::Array<HitRect> HitRectList; + +struct SubRectItem { + NRect rect; + uint32 messageListId; +}; + +struct RectItem { + NRect rect; + Common::Array<SubRectItem> subRects; +}; + +typedef Common::Array<RectItem> RectList; + +struct MessageItem { + uint32 messageNum; + uint32 messageValue; +}; + +typedef Common::Array<MessageItem> MessageList; + +struct NavigationItem { + uint32 fileHash; + uint32 leftSmackerFileHash; + uint32 rightSmackerFileHash; + uint32 middleSmackerFileHash; + byte interactive; + byte middleFlag; + uint32 mouseCursorFileHash; +}; + +typedef Common::Array<NavigationItem> NavigationList; + +struct SceneInfo140 { + uint32 bgFilename1; + uint32 bgFilename2; + uint32 txFilename; + uint32 bgFilename3; + byte xPosIndex; + byte count; +}; + +struct SceneInfo2700 { + uint32 id; + uint32 bgFilename; + uint32 class437Filename; + uint32 dataResourceFilename; + uint32 pointListName; + uint32 rectListName; + uint32 exPaletteFilename2; + uint32 exPaletteFilename1; + uint32 mouseCursorFilename; + int16 which1; + int16 which2; +}; + +class StaticData { +public: + StaticData(); + ~StaticData(); + void load(const char *filename); + HitRectList *getHitRectList(uint32 id); + RectList *getRectList(uint32 id); + MessageList *getMessageList(uint32 id); + NavigationList *getNavigationList(uint32 id); + SceneInfo140 *getSceneInfo140Item(uint32 id); + SceneInfo2700 *getSceneInfo2700(uint32 id); +protected: + Common::HashMap<uint32, HitRectList*> _hitRectLists; + Common::HashMap<uint32, RectList*> _rectLists; + Common::HashMap<uint32, MessageList*> _messageLists; + Common::HashMap<uint32, NavigationList*> _navigationLists; + Common::HashMap<uint32, SceneInfo140*> _sceneInfo140Items; + Common::HashMap<uint32, SceneInfo2700*> _sceneInfo2700Items; +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_STATICDATA_H */ |