diff options
author | Matthew Hoops | 2012-08-26 15:49:45 -0400 |
---|---|---|
committer | Matthew Hoops | 2012-08-26 16:12:25 -0400 |
commit | bb1e60e8b2f3bba06ae3b089097f94ea82a70c8a (patch) | |
tree | a434233367725fbb6dc7072776c312f52254d57f /engines | |
parent | 7a49b3669a0e18210a2f5409cb35da735f549b11 (diff) | |
parent | 857b92f8ffececa9c1f990d21a6a8d1630199a62 (diff) | |
download | scummvm-rg350-bb1e60e8b2f3bba06ae3b089097f94ea82a70c8a.tar.gz scummvm-rg350-bb1e60e8b2f3bba06ae3b089097f94ea82a70c8a.tar.bz2 scummvm-rg350-bb1e60e8b2f3bba06ae3b089097f94ea82a70c8a.zip |
Merge remote branch 'upstream/master' into pegasus
Conflicts:
AUTHORS
devtools/credits.pl
gui/credits.h
Diffstat (limited to 'engines')
378 files changed, 13213 insertions, 4933 deletions
diff --git a/engines/advancedDetector.cpp b/engines/advancedDetector.cpp index ac06e74e0a..9beba6ce4a 100644 --- a/engines/advancedDetector.cpp +++ b/engines/advancedDetector.cpp @@ -22,7 +22,6 @@ #include "common/debug.h" #include "common/util.h" -#include "common/hash-str.h" #include "common/file.h" #include "common/macresman.h" #include "common/md5.h" @@ -308,14 +307,7 @@ Common::Error AdvancedMetaEngine::createInstance(OSystem *syst, Engine **engine) return Common::kNoError; } -struct SizeMD5 { - int size; - Common::String md5; -}; - -typedef Common::HashMap<Common::String, SizeMD5, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> SizeMD5Map; - -static void reportUnknown(const Common::FSNode &path, const SizeMD5Map &filesSizeMD5) { +void AdvancedMetaEngine::reportUnknown(const Common::FSNode &path, const ADFilePropertiesMap &filesProps) const { // TODO: This message should be cleaned up / made more specific. // For example, we should specify at least which engine triggered this. // @@ -327,7 +319,7 @@ static void reportUnknown(const Common::FSNode &path, const SizeMD5Map &filesSiz report += _("of the game you tried to add and its version/language/etc.:"); report += "\n"; - for (SizeMD5Map::const_iterator file = filesSizeMD5.begin(); file != filesSizeMD5.end(); ++file) + for (ADFilePropertiesMap::const_iterator file = filesProps.begin(); file != filesProps.end(); ++file) report += Common::String::format(" {\"%s\", 0, \"%s\", %d},\n", file->_key.c_str(), file->_value.md5.c_str(), file->_value.size); report += "\n"; @@ -375,8 +367,36 @@ void AdvancedMetaEngine::composeFileHashMap(FileMap &allFiles, const Common::FSL } } +bool AdvancedMetaEngine::getFileProperties(const Common::FSNode &parent, const FileMap &allFiles, const ADGameDescription &game, const Common::String fname, ADFileProperties &fileProps) const { + // FIXME/TODO: We don't handle the case that a file is listed as a regular + // file and as one with resource fork. + + if (game.flags & ADGF_MACRESFORK) { + Common::MacResManager macResMan; + + if (!macResMan.open(parent, fname)) + return false; + + fileProps.md5 = macResMan.computeResForkMD5AsString(_md5Bytes); + fileProps.size = macResMan.getResForkDataSize(); + return true; + } + + if (!allFiles.contains(fname)) + return false; + + Common::File testFile; + + if (!testFile.open(allFiles[fname])) + return false; + + fileProps.size = (int32)testFile.size(); + fileProps.md5 = Common::computeStreamMD5AsString(testFile, _md5Bytes); + return true; +} + ADGameDescList AdvancedMetaEngine::detectGame(const Common::FSNode &parent, const FileMap &allFiles, Common::Language language, Common::Platform platform, const Common::String &extra) const { - SizeMD5Map filesSizeMD5; + ADFilePropertiesMap filesProps; const ADGameFileDescription *fileDesc; const ADGameDescription *g; @@ -391,39 +411,14 @@ ADGameDescList AdvancedMetaEngine::detectGame(const Common::FSNode &parent, cons for (fileDesc = g->filesDescriptions; fileDesc->fileName; fileDesc++) { Common::String fname = fileDesc->fileName; - SizeMD5 tmp; + ADFileProperties tmp; - if (filesSizeMD5.contains(fname)) + if (filesProps.contains(fname)) continue; - // FIXME/TODO: We don't handle the case that a file is listed as a regular - // file and as one with resource fork. - - if (g->flags & ADGF_MACRESFORK) { - Common::MacResManager macResMan; - - if (macResMan.open(parent, fname)) { - tmp.md5 = macResMan.computeResForkMD5AsString(_md5Bytes); - tmp.size = macResMan.getResForkDataSize(); - debug(3, "> '%s': '%s'", fname.c_str(), tmp.md5.c_str()); - filesSizeMD5[fname] = tmp; - } - } else { - if (allFiles.contains(fname)) { - debug(3, "+ %s", fname.c_str()); - - Common::File testFile; - - if (testFile.open(allFiles[fname])) { - tmp.size = (int32)testFile.size(); - tmp.md5 = Common::computeStreamMD5AsString(testFile, _md5Bytes); - } else { - tmp.size = -1; - } - - debug(3, "> '%s': '%s'", fname.c_str(), tmp.md5.c_str()); - filesSizeMD5[fname] = tmp; - } + if (getFileProperties(parent, allFiles, *g, fname, tmp)) { + debug(3, "> '%s': '%s'", fname.c_str(), tmp.md5.c_str()); + filesProps[fname] = tmp; } } } @@ -456,19 +451,19 @@ ADGameDescList AdvancedMetaEngine::detectGame(const Common::FSNode &parent, cons for (fileDesc = g->filesDescriptions; fileDesc->fileName; fileDesc++) { Common::String tstr = fileDesc->fileName; - if (!filesSizeMD5.contains(tstr)) { + if (!filesProps.contains(tstr)) { fileMissing = true; allFilesPresent = false; break; } - if (fileDesc->md5 != NULL && fileDesc->md5 != filesSizeMD5[tstr].md5) { - debug(3, "MD5 Mismatch. Skipping (%s) (%s)", fileDesc->md5, filesSizeMD5[tstr].md5.c_str()); + if (fileDesc->md5 != NULL && fileDesc->md5 != filesProps[tstr].md5) { + debug(3, "MD5 Mismatch. Skipping (%s) (%s)", fileDesc->md5, filesProps[tstr].md5.c_str()); fileMissing = true; break; } - if (fileDesc->fileSize != -1 && fileDesc->fileSize != filesSizeMD5[tstr].size) { + if (fileDesc->fileSize != -1 && fileDesc->fileSize != filesProps[tstr].size) { debug(3, "Size Mismatch. Skipping"); fileMissing = true; break; @@ -514,8 +509,8 @@ ADGameDescList AdvancedMetaEngine::detectGame(const Common::FSNode &parent, cons // We didn't find a match if (matched.empty()) { - if (!filesSizeMD5.empty() && gotAnyMatchesWithAllFiles) { - reportUnknown(parent, filesSizeMD5); + if (!filesProps.empty() && gotAnyMatchesWithAllFiles) { + reportUnknown(parent, filesProps); } // Filename based fallback @@ -524,7 +519,7 @@ ADGameDescList AdvancedMetaEngine::detectGame(const Common::FSNode &parent, cons return matched; } -const ADGameDescription *AdvancedMetaEngine::detectGameFilebased(const FileMap &allFiles, const ADFileBasedFallback *fileBasedFallback) const { +const ADGameDescription *AdvancedMetaEngine::detectGameFilebased(const FileMap &allFiles, const Common::FSList &fslist, const ADFileBasedFallback *fileBasedFallback, ADFilePropertiesMap *filesProps) const { const ADFileBasedFallback *ptr; const char* const* filenames; @@ -554,6 +549,16 @@ const ADGameDescription *AdvancedMetaEngine::detectGameFilebased(const FileMap & maxNumMatchedFiles = numMatchedFiles; debug(4, "and overridden"); + + if (filesProps) { + for (filenames = ptr->filenames; *filenames; ++filenames) { + ADFileProperties tmp; + + if (getFileProperties(fslist.begin()->getParent(), allFiles, *agdesc, *filenames, tmp)) + (*filesProps)[*filenames] = tmp; + } + } + } } } diff --git a/engines/advancedDetector.h b/engines/advancedDetector.h index 45a9f183e8..8c19d03691 100644 --- a/engines/advancedDetector.h +++ b/engines/advancedDetector.h @@ -26,6 +26,8 @@ #include "engines/metaengine.h" #include "engines/engine.h" +#include "common/hash-str.h" + #include "common/gui_options.h" // FIXME: Temporary hack? namespace Common { @@ -46,6 +48,20 @@ struct ADGameFileDescription { }; /** + * A record describing the properties of a file. Used on the existing + * files while detecting a game. + */ +struct ADFileProperties { + int32 size; + Common::String md5; +}; + +/** + * A map of all relevant existing files in a game directory while detecting. + */ +typedef Common::HashMap<Common::String, ADFileProperties, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> ADFilePropertiesMap; + +/** * A shortcut to produce an empty ADGameFileDescription record. Used to mark * the end of a list of these. */ @@ -286,9 +302,17 @@ protected: * In case of a tie, the entry coming first in the list is chosen. * * @param allFiles a map describing all present files + * @param fslist a list of nodes for all present files * @param fileBasedFallback a list of ADFileBasedFallback records, zero-terminated + * @param filesProps if not 0, return a map of properties for all detected files here + */ + const ADGameDescription *detectGameFilebased(const FileMap &allFiles, const Common::FSList &fslist, const ADFileBasedFallback *fileBasedFallback, ADFilePropertiesMap *filesProps = 0) const; + + /** + * Log and print a report that we found an unknown game variant, together with the file + * names, sizes and MD5 sums. */ - const ADGameDescription *detectGameFilebased(const FileMap &allFiles, const ADFileBasedFallback *fileBasedFallback) const; + void reportUnknown(const Common::FSNode &path, const ADFilePropertiesMap &filesProps) const; // TODO void updateGameDescriptor(GameDescriptor &desc, const ADGameDescription *realDesc) const; @@ -298,6 +322,9 @@ protected: * Includes nifty stuff like removing trailing dots and ignoring case. */ void composeFileHashMap(FileMap &allFiles, const Common::FSList &fslist, int depth) const; + + /** Get the properties (size and MD5) of this file. */ + bool getFileProperties(const Common::FSNode &parent, const FileMap &allFiles, const ADGameDescription &game, const Common::String fname, ADFileProperties &fileProps) const; }; #endif diff --git a/engines/agi/detection.cpp b/engines/agi/detection.cpp index 805fe7d366..5f7780bfe3 100644 --- a/engines/agi/detection.cpp +++ b/engines/agi/detection.cpp @@ -20,9 +20,6 @@ * */ -// FIXME: Avoid using printf -#define FORBIDDEN_SYMBOL_EXCEPTION_printf - #include "base/plugins.h" #include "engines/advancedDetector.h" @@ -491,10 +488,14 @@ const ADGameDescription *AgiMetaEngine::fallbackDetect(const FileMap &allFilesXX g_fallbackDesc.desc.gameid = _gameid.c_str(); g_fallbackDesc.desc.extra = _extra.c_str(); - printf("Your game version has been detected using fallback matching as a\n"); - printf("variant of %s (%s).\n", g_fallbackDesc.desc.gameid, g_fallbackDesc.desc.extra); - printf("If this is an original and unmodified version or new made Fanmade game,\n"); - printf("please report any, information previously printed by ScummVM to the team.\n"); + Common::String fallbackWarning; + + fallbackWarning = "Your game version has been detected using fallback matching as a\n"; + fallbackWarning += Common::String::format("variant of %s (%s).\n", g_fallbackDesc.desc.gameid, g_fallbackDesc.desc.extra); + fallbackWarning += "If this is an original and unmodified version or new made Fanmade game,\n"; + fallbackWarning += "please report any, information previously printed by ScummVM to the team.\n"; + + g_system->logMessage(LogMessageType::kWarning, fallbackWarning.c_str()); return (const ADGameDescription *)&g_fallbackDesc; } diff --git a/engines/agos/animation.cpp b/engines/agos/animation.cpp index 10c01741ae..9176412e0e 100644 --- a/engines/agos/animation.cpp +++ b/engines/agos/animation.cpp @@ -260,9 +260,6 @@ bool MoviePlayerDXA::load() { debug(0, "Playing video %s", videoName.c_str()); CursorMan.showMouse(false); - - _firstFrameOffset = _fileStream->pos(); - return true; } @@ -271,6 +268,10 @@ void MoviePlayerDXA::copyFrameToBuffer(byte *dst, uint x, uint y, uint pitch) { uint w = getWidth(); const Graphics::Surface *surface = decodeNextFrame(); + + if (!surface) + return; + byte *src = (byte *)surface->pixels; dst += y * pitch + x; @@ -281,7 +282,7 @@ void MoviePlayerDXA::copyFrameToBuffer(byte *dst, uint x, uint y, uint pitch) { } while (--h); if (hasDirtyPalette()) - setSystemPalette(); + g_system->getPaletteManager()->setPalette(getPalette(), 0, 256); } void MoviePlayerDXA::playVideo() { @@ -302,34 +303,7 @@ void MoviePlayerDXA::stopVideo() { } void MoviePlayerDXA::startSound() { - uint32 offset, size; - - if (getSoundTag() == MKTAG('W','A','V','E')) { - size = _fileStream->readUint32BE(); - - if (_sequenceNum) { - Common::File in; - - _fileStream->seek(size, SEEK_CUR); - - in.open("audio.wav"); - if (!in.isOpen()) { - error("Can't read offset file 'audio.wav'"); - } - - in.seek(_sequenceNum * 8, SEEK_SET); - offset = in.readUint32LE(); - size = in.readUint32LE(); - - in.seek(offset, SEEK_SET); - _bgSoundStream = Audio::makeWAVStream(in.readStream(size), DisposeAfterUse::YES); - in.close(); - } else { - _bgSoundStream = Audio::makeWAVStream(_fileStream->readStream(size), DisposeAfterUse::YES); - } - } else { - _bgSoundStream = Audio::SeekableAudioStream::openStreamFile(baseName); - } + start(); if (_bgSoundStream != NULL) { _vm->_mixer->stopHandle(_bgSound); @@ -344,8 +318,7 @@ void MoviePlayerDXA::nextFrame() { } if (_vm->_interactiveVideo == TYPE_LOOPING && endOfVideo()) { - _fileStream->seek(_firstFrameOffset); - _curFrame = -1; + rewind(); startSound(); } @@ -374,13 +347,15 @@ bool MoviePlayerDXA::processFrame() { copyFrameToBuffer((byte *)screen->pixels, (_vm->_screenWidth - getWidth()) / 2, (_vm->_screenHeight - getHeight()) / 2, screen->pitch); _vm->_system->unlockScreen(); - Common::Rational soundTime(_mixer->getSoundElapsedTime(_bgSound), 1000); - if ((_bgSoundStream == NULL) || ((soundTime * getFrameRate()).toInt() / 1000 < getCurFrame() + 1)) { + uint32 soundTime = _mixer->getSoundElapsedTime(_bgSound); + uint32 nextFrameStartTime = ((Video::VideoDecoder::VideoTrack *)getTrack(0))->getNextFrameStartTime(); + + if ((_bgSoundStream == NULL) || soundTime < nextFrameStartTime) { if (_bgSoundStream && _mixer->isSoundHandleActive(_bgSound)) { - while (_mixer->isSoundHandleActive(_bgSound) && (soundTime * getFrameRate()).toInt() < getCurFrame()) { + while (_mixer->isSoundHandleActive(_bgSound) && soundTime < nextFrameStartTime) { _vm->_system->delayMillis(10); - soundTime = Common::Rational(_mixer->getSoundElapsedTime(_bgSound), 1000); + soundTime = _mixer->getSoundElapsedTime(_bgSound); } // In case the background sound ends prematurely, update // _ticks so that we can still fall back on the no-sound @@ -399,14 +374,35 @@ bool MoviePlayerDXA::processFrame() { return false; } -void MoviePlayerDXA::updateVolume() { - if (g_system->getMixer()->isSoundHandleActive(_bgSound)) - g_system->getMixer()->setChannelVolume(_bgSound, getVolume()); -} +void MoviePlayerDXA::readSoundData(Common::SeekableReadStream *stream) { + uint32 tag = stream->readUint32BE(); + + if (tag == MKTAG('W','A','V','E')) { + uint32 size = stream->readUint32BE(); + + if (_sequenceNum) { + Common::File in; + + stream->skip(size); + + in.open("audio.wav"); + if (!in.isOpen()) { + error("Can't read offset file 'audio.wav'"); + } + + in.seek(_sequenceNum * 8, SEEK_SET); + uint32 offset = in.readUint32LE(); + size = in.readUint32LE(); -void MoviePlayerDXA::updateBalance() { - if (g_system->getMixer()->isSoundHandleActive(_bgSound)) - g_system->getMixer()->setChannelBalance(_bgSound, getBalance()); + in.seek(offset, SEEK_SET); + _bgSoundStream = Audio::makeWAVStream(in.readStream(size), DisposeAfterUse::YES); + in.close(); + } else { + _bgSoundStream = Audio::makeWAVStream(stream->readStream(size), DisposeAfterUse::YES); + } + } else { + _bgSoundStream = Audio::SeekableAudioStream::openStreamFile(baseName); + } } /////////////////////////////////////////////////////////////////////////////// @@ -415,7 +411,7 @@ void MoviePlayerDXA::updateBalance() { MoviePlayerSMK::MoviePlayerSMK(AGOSEngine_Feeble *vm, const char *name) - : MoviePlayer(vm), SmackerDecoder(vm->_mixer) { + : MoviePlayer(vm), SmackerDecoder() { debug(0, "Creating SMK cutscene player"); memset(baseName, 0, sizeof(baseName)); @@ -435,8 +431,6 @@ bool MoviePlayerSMK::load() { CursorMan.showMouse(false); - _firstFrameOffset = _fileStream->pos(); - return true; } @@ -445,6 +439,10 @@ void MoviePlayerSMK::copyFrameToBuffer(byte *dst, uint x, uint y, uint pitch) { uint w = getWidth(); const Graphics::Surface *surface = decodeNextFrame(); + + if (!surface) + return; + byte *src = (byte *)surface->pixels; dst += y * pitch + x; @@ -455,7 +453,7 @@ void MoviePlayerSMK::copyFrameToBuffer(byte *dst, uint x, uint y, uint pitch) { } while (--h); if (hasDirtyPalette()) - setSystemPalette(); + g_system->getPaletteManager()->setPalette(getPalette(), 0, 256); } void MoviePlayerSMK::playVideo() { @@ -468,6 +466,7 @@ void MoviePlayerSMK::stopVideo() { } void MoviePlayerSMK::startSound() { + start(); } void MoviePlayerSMK::handleNextFrame() { @@ -477,10 +476,8 @@ void MoviePlayerSMK::handleNextFrame() { } void MoviePlayerSMK::nextFrame() { - if (_vm->_interactiveVideo == TYPE_LOOPING && endOfVideo()) { - _fileStream->seek(_firstFrameOffset); - _curFrame = -1; - } + if (_vm->_interactiveVideo == TYPE_LOOPING && endOfVideo()) + rewind(); if (!endOfVideo()) { decodeNextFrame(); @@ -503,7 +500,7 @@ bool MoviePlayerSMK::processFrame() { uint32 waitTime = getTimeToNextFrame(); - if (!waitTime) { + if (!waitTime && !endOfVideoTracks()) { warning("dropped frame %i", getCurFrame()); return false; } diff --git a/engines/agos/animation.h b/engines/agos/animation.h index d1ff074b03..9e31fced6d 100644 --- a/engines/agos/animation.h +++ b/engines/agos/animation.h @@ -67,9 +67,6 @@ protected: virtual void handleNextFrame(); virtual bool processFrame() = 0; virtual void startSound() {} - -protected: - uint32 _firstFrameOffset; }; class MoviePlayerDXA : public MoviePlayer, Video::DXADecoder { @@ -84,9 +81,7 @@ public: virtual void stopVideo(); protected: - // VideoDecoder API - void updateVolume(); - void updateBalance(); + void readSoundData(Common::SeekableReadStream *stream); private: void handleNextFrame(); diff --git a/engines/agos/event.cpp b/engines/agos/event.cpp index ed26b96381..cc1c40c207 100644 --- a/engines/agos/event.cpp +++ b/engines/agos/event.cpp @@ -467,11 +467,7 @@ void AGOSEngine::delay(uint amount) { memset(_saveLoadName, 0, sizeof(_saveLoadName)); sprintf(_saveLoadName, "Quick %d", _saveLoadSlot); _saveLoadType = (event.kbd.hasFlags(Common::KBD_ALT)) ? 1 : 2; - - // We should only allow a load or save when it was possible in original - // This stops load/save during copy protection, conversations and cut scenes - if (!_mouseHideCount && !_showPreposition) - quickLoadOrSave(); + quickLoadOrSave(); } else if (event.kbd.hasFlags(Common::KBD_CTRL)) { if (event.kbd.keycode == Common::KEYCODE_a) { GUI::Dialog *_aboutDialog; diff --git a/engines/agos/saveload.cpp b/engines/agos/saveload.cpp index b3ec916b47..c6bca1a6e6 100644 --- a/engines/agos/saveload.cpp +++ b/engines/agos/saveload.cpp @@ -142,23 +142,41 @@ void AGOSEngine_Feeble::quickLoadOrSave() { } #endif +// The function uses segments of code from the original game scripts +// to allow quick loading and saving, but isn't perfect. +// +// Unfortuntely this allows loading and saving in locations, +// which aren't supported, and will not restore correctly: +// Various locations in Elvira 1/2 and Waxworks where saving +// was disabled void AGOSEngine::quickLoadOrSave() { - // The function uses segments of code from the original game scripts - // to allow quick loading and saving, but isn't perfect. - // - // Unfortuntely this allows loading and saving in locations, - // which aren't supported, and will not restore correctly: - // Any overhead maps in Simon the Sorcerer 2 - // Various locations in Elvira 1/2 and Waxworks where saving - // was disabled - - // The floppy disk demo of Simon the Sorcerer 1 doesn't work. - if (getFeatures() & GF_DEMO) - return; - bool success; Common::String buf; + // Disable loading and saving when it was not possible in the original: + // In overhead maps areas in Simon the Sorcerer 2 + // In the floppy disk demo of Simon the Sorcerer 1 + // In copy protection, conversations and cut scenes + if ((getGameType() == GType_SIMON2 && _boxStarHeight == 200) || + (getGameType() == GType_SIMON1 && (getFeatures() & GF_DEMO)) || + _mouseHideCount || _showPreposition) { + buf = Common::String::format("Quick load or save game isn't supported in this location"); + GUI::MessageDialog dialog(buf, "OK"); + dialog.runModal(); + return; + } + + // Check if Simon is walking, and stop when required + if (getGameType() == GType_SIMON1 && getBitFlag(11)) { + vcStopAnimation(11, 1122); + animate(4, 11, 1122, 0, 0, 2); + waitForSync(1122); + } else if (getGameType() == GType_SIMON2 && getBitFlag(11)) { + vcStopAnimation(11, 232); + animate(4, 11, 232, 0, 0, 2); + waitForSync(1122); + } + char *filename = genSaveName(_saveLoadSlot); if (_saveLoadType == 2) { Subroutine *sub; diff --git a/engines/cge/bitmap.cpp b/engines/cge/bitmap.cpp index 309b89bdda..4f85957b3d 100644 --- a/engines/cge/bitmap.cpp +++ b/engines/cge/bitmap.cpp @@ -123,12 +123,15 @@ Bitmap::~Bitmap() { Bitmap &Bitmap::operator=(const Bitmap &bmp) { debugC(1, kCGEDebugBitmap, "&Bitmap::operator ="); + if (this == &bmp) + return *this; uint8 *v0 = bmp._v; _w = bmp._w; _h = bmp._h; _m = NULL; _map = 0; + _vm = bmp._vm; delete[] _v; if (v0 == NULL) { diff --git a/engines/cge/cge.cpp b/engines/cge/cge.cpp index 875ac34cd0..6cc0c45963 100644 --- a/engines/cge/cge.cpp +++ b/engines/cge/cge.cpp @@ -30,6 +30,8 @@ #include "common/fs.h" #include "engines/advancedDetector.h" #include "engines/util.h" +#include "gui/message.h" + #include "cge/cge.h" #include "cge/vga13h.h" #include "cge/cge_main.h" @@ -50,7 +52,6 @@ CGEEngine::CGEEngine(OSystem *syst, const ADGameDescription *gameDescription) DebugMan.addDebugChannel(kCGEDebugEngine, "engine", "CGE Engine debug channel"); _startupMode = 1; - _demoText = kDemo; _oldLev = 0; _pocPtr = 0; _bitmapPalette = NULL; @@ -122,7 +123,7 @@ void CGEEngine::init() { _maxScene = 0; _dark = false; _game = false; - _finis = false; + _endGame = false; _now = 1; _lev = -1; _recentStep = -2; @@ -134,7 +135,6 @@ void CGEEngine::init() { _soundOk = 1; _sprTv = NULL; _gameCase2Cpt = 0; - _offUseCount = 0; _startGameSlot = ConfMan.hasKey("save_slot") ? ConfMan.getInt("save_slot") : -1; } @@ -196,6 +196,16 @@ Common::Error CGEEngine::run() { // Run the game cge_main(); + // If game is finished, display ending message + if (_flag[3]) { + Common::String msg = Common::String(_text->getText(kSayTheEnd)); + if (msg.size() != 0) { + g_system->delayMillis(10); + GUI::MessageDialog dialog(msg, "OK"); + dialog.runModal(); + } + } + // Remove game objects deinit(); diff --git a/engines/cge/cge.h b/engines/cge/cge.h index 4ebc836ee0..0e8c5a05bb 100644 --- a/engines/cge/cge.h +++ b/engines/cge/cge.h @@ -78,6 +78,8 @@ class Talk; #define kMapZCnt 20 #define kMapTop 80 +#define kSayTheEnd 41 + // our engine debug channels enum { kCGEDebugBitmap = 1 << 0, @@ -147,7 +149,6 @@ public: const ADGameDescription *_gameDescription; int _startupMode; - int _demoText; int _oldLev; int _pocPtr; bool _music; @@ -157,7 +158,7 @@ public: bool _flag[4]; bool _dark; bool _game; - bool _finis; + bool _endGame; int _now; int _lev; int _mode; diff --git a/engines/cge/cge_main.cpp b/engines/cge/cge_main.cpp index 2620147c4d..3ba5f7fed9 100644 --- a/engines/cge/cge_main.cpp +++ b/engines/cge/cge_main.cpp @@ -150,11 +150,11 @@ void CGEEngine::sndSetVolume() { void CGEEngine::syncHeader(Common::Serializer &s) { debugC(1, kCGEDebugEngine, "CGEEngine::syncHeader(s)"); - int i; + int i = kDemo; s.syncAsUint16LE(_now); s.syncAsUint16LE(_oldLev); - s.syncAsUint16LE(_demoText); + s.syncAsUint16LE(i); // unused Demo string id for (i = 0; i < 5; i++) s.syncAsUint16LE(_game); s.syncAsSint16LE(i); // unused VGA::Mono variable @@ -305,12 +305,21 @@ Common::Error CGEEngine::saveGameState(int slot, const Common::String &desc) { _hero->park(); _oldLev = _lev; + int x = _hero->_x; + int y = _hero->_y; + int z = _hero->_z; + // Write out the user's progress saveGame(slot, desc); + _commandHandler->addCommand(kCmdLevel, -1, _oldLev, &_sceneLight); // Reload the scene sceneUp(); + _hero->_x = x; + _hero->_y = y; + _hero->_z = z; + return Common::kNoError; } @@ -518,8 +527,8 @@ Square::Square(CGEEngine *vm) : Sprite(vm, NULL), _vm(vm) { setShapeList(MB); } -void Square::touch(uint16 mask, int x, int y) { - Sprite::touch(mask, x, y); +void Square::touch(uint16 mask, int x, int y, Common::KeyCode keyCode) { + Sprite::touch(mask, x, y, keyCode); if (mask & kMouseLeftUp) { _vm->XZ(_x + x, _y + y).cell() = 0; _vm->_commandHandlerTurbo->addCommand(kCmdKill, -1, 0, this); @@ -706,7 +715,7 @@ void CGEEngine::qGame() { saveGame(0, Common::String("Automatic Savegame")); _vga->sunset(); - _finis = true; + _endGame = true; } void CGEEngine::switchScene(int newScene) { @@ -758,11 +767,11 @@ void System::funTouch() { _funDel = n; } -void System::touch(uint16 mask, int x, int y) { +void System::touch(uint16 mask, int x, int y, Common::KeyCode keyCode) { funTouch(); if (mask & kEventKeyb) { - if (x == Common::KEYCODE_ESCAPE) { + if (keyCode == Common::KEYCODE_ESCAPE) { // The original was calling keyClick() // The sound is uselessly annoying and noisy, so it has been removed _vm->killText(); @@ -926,7 +935,7 @@ void CGEEngine::optionTouch(int opt, uint16 mask) { } #pragma argsused -void Sprite::touch(uint16 mask, int x, int y) { +void Sprite::touch(uint16 mask, int x, int y, Common::KeyCode keyCode) { _vm->_sys->funTouch(); if ((mask & kEventAttn) != 0) @@ -1312,7 +1321,7 @@ void CGEEngine::runGame() { _sceneLight->_flags._tran = true; _vga->_showQ->append(_sceneLight); - _sceneLight->_flags._hide = true; + _sceneLight->_flags._hide = false; const Seq pocSeq[] = { { 0, 0, 0, 0, 20 }, @@ -1403,14 +1412,14 @@ void CGEEngine::runGame() { _keyboard->setClient(_sys); // main loop - while (!_finis && !_quitFlag) { - if (_flag[3]) + while (!_endGame && !_quitFlag) { + if (_flag[3]) // Flag FINIS _commandHandler->addCallback(kCmdExec, -1, 0, kQGame); mainLoop(); } // If finishing game due to closing ScummVM window, explicitly save the game - if (!_finis && canSaveGameStateCurrently()) + if (!_endGame && canSaveGameStateCurrently()) qGame(); _keyboard->setClient(NULL); diff --git a/engines/cge/cge_main.h b/engines/cge/cge_main.h index 87199ee524..bde8306f36 100644 --- a/engines/cge/cge_main.h +++ b/engines/cge/cge_main.h @@ -78,7 +78,7 @@ namespace CGE { #define kScrHeight 200 #define kWorldHeight (kScrHeight - kPanHeight) #define kStackSize 2048 -#define kSavegameCheckSum (1956 + _now + _oldLev + _game + _music + _demoText) +#define kSavegameCheckSum (1956 + _now + _oldLev + _game + _music + kDemo) #define kSavegame0Name ("{{INIT}}" kSvgExt) #define kSavegameStrSize 11 #define kGameFrameDelay (1000 / 50) @@ -92,7 +92,7 @@ public: void setPal(); void funTouch(); - virtual void touch(uint16 mask, int x, int y); + virtual void touch(uint16 mask, int x, int y, Common::KeyCode keyCode); void tick(); private: CGEEngine *_vm; @@ -101,7 +101,7 @@ private: class Square : public Sprite { public: Square(CGEEngine *vm); - virtual void touch(uint16 mask, int x, int y); + virtual void touch(uint16 mask, int x, int y, Common::KeyCode keyCode); private: CGEEngine *_vm; }; diff --git a/engines/cge/detection.cpp b/engines/cge/detection.cpp index f723ec8fbd..2e04b82026 100644 --- a/engines/cge/detection.cpp +++ b/engines/cge/detection.cpp @@ -108,7 +108,7 @@ public: } virtual const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const { - return detectGameFilebased(allFiles, CGE::fileBasedFallback); + return detectGameFilebased(allFiles, fslist, CGE::fileBasedFallback); } virtual const char *getName() const { @@ -217,8 +217,6 @@ SaveStateDescriptor CGEMetaEngine::querySaveMetaInfos(const char *target, int sl } else { // Create the return descriptor SaveStateDescriptor desc(slot, header.saveName); - desc.setDeletableFlag(true); - desc.setWriteProtectedFlag(false); desc.setThumbnail(header.thumbnail); desc.setSaveDate(header.saveYear, header.saveMonth, header.saveDay); desc.setSaveTime(header.saveHour, header.saveMinutes); diff --git a/engines/cge/events.cpp b/engines/cge/events.cpp index 095aac2412..1530c870ef 100644 --- a/engines/cge/events.cpp +++ b/engines/cge/events.cpp @@ -55,7 +55,7 @@ Sprite *Keyboard::setClient(Sprite *spr) { bool Keyboard::getKey(Common::Event &event) { Common::KeyCode keycode = event.kbd.keycode; - if ((keycode == Common::KEYCODE_LALT) || (keycode == Common::KEYCODE_RALT)) + if (((keycode == Common::KEYCODE_LALT) || (keycode == Common::KEYCODE_RALT)) && event.type == Common::EVENT_KEYDOWN) _keyAlt = true; else _keyAlt = false; @@ -109,9 +109,19 @@ bool Keyboard::getKey(Common::Event &event) { case Common::KEYCODE_3: case Common::KEYCODE_4: if (event.kbd.flags & Common::KBD_ALT) { - _vm->_commandHandler->addCommand(kCmdLevel, -1, keycode - '0', NULL); + _vm->_commandHandler->addCommand(kCmdLevel, -1, keycode - Common::KEYCODE_0, NULL); return false; } + // Fallthrough intended + case Common::KEYCODE_5: + case Common::KEYCODE_6: + case Common::KEYCODE_7: + case Common::KEYCODE_8: + if (event.type == Common::EVENT_KEYDOWN && !(event.kbd.flags & Common::KBD_ALT) && keycode != Common::KEYCODE_0) { + _vm->selectPocket(keycode - Common::KEYCODE_1); + return false; + } + break; default: break; } @@ -125,9 +135,11 @@ void Keyboard::newKeyboard(Common::Event &event) { if ((event.type == Common::EVENT_KEYDOWN) && (_client)) { CGEEvent &evt = _vm->_eventManager->getNextEvent(); - evt._x = event.kbd.keycode; // Keycode - evt._mask = kEventKeyb; // Event mask - evt._spritePtr = _client; // Sprite pointer + evt._x = 0; + evt._y = 0; + evt._keyCode = event.kbd.keycode; // Keycode + evt._mask = kEventKeyb; // Event mask + evt._spritePtr = _client; // Sprite pointer } } @@ -194,6 +206,7 @@ void Mouse::newMouse(Common::Event &event) { CGEEvent &evt = _vm->_eventManager->getNextEvent(); evt._x = event.mouse.x; evt._y = event.mouse.y; + evt._keyCode = Common::KEYCODE_INVALID; evt._spritePtr = _vm->spriteAt(evt._x, evt._y); switch (event.type) { @@ -259,7 +272,7 @@ void EventManager::handleEvents() { CGEEvent e = _eventQueue[_eventQueueTail]; if (e._mask) { if (_vm->_mouse->_hold && e._spritePtr != _vm->_mouse->_hold) - _vm->_mouse->_hold->touch(e._mask | kEventAttn, e._x - _vm->_mouse->_hold->_x, e._y - _vm->_mouse->_hold->_y); + _vm->_mouse->_hold->touch(e._mask | kEventAttn, e._x - _vm->_mouse->_hold->_x, e._y - _vm->_mouse->_hold->_y, e._keyCode); // update mouse cursor position if (e._mask & kMouseRoll) @@ -268,11 +281,11 @@ void EventManager::handleEvents() { // activate current touched SPRITE if (e._spritePtr) { if (e._mask & kEventKeyb) - e._spritePtr->touch(e._mask, e._x, e._y); + e._spritePtr->touch(e._mask, e._x, e._y, e._keyCode); else - e._spritePtr->touch(e._mask, e._x - e._spritePtr->_x, e._y - e._spritePtr->_y); + e._spritePtr->touch(e._mask, e._x - e._spritePtr->_x, e._y - e._spritePtr->_y, e._keyCode); } else if (_vm->_sys) - _vm->_sys->touch(e._mask, e._x, e._y); + _vm->_sys->touch(e._mask, e._x, e._y, e._keyCode); if (e._mask & kMouseLeftDown) { _vm->_mouse->_hold = e._spritePtr; diff --git a/engines/cge/events.h b/engines/cge/events.h index 6bbd52e4a5..522aa67905 100644 --- a/engines/cge/events.h +++ b/engines/cge/events.h @@ -70,6 +70,7 @@ struct CGEEvent { uint16 _mask; uint16 _x; uint16 _y; + Common::KeyCode _keyCode; Sprite *_spritePtr; }; diff --git a/engines/cge/fileio.cpp b/engines/cge/fileio.cpp index c50db4e929..f23105d823 100644 --- a/engines/cge/fileio.cpp +++ b/engines/cge/fileio.cpp @@ -201,9 +201,28 @@ EncryptedStream::EncryptedStream(CGEEngine *vm, const char *name) : _vm(vm) { _error = true; _vm->_resman->seek(kp->_pos); - byte *dataBuffer = (byte *)malloc(kp->_size); - _vm->_resman->read(dataBuffer, kp->_size); - _readStream = new Common::MemoryReadStream(dataBuffer, kp->_size, DisposeAfterUse::YES); + byte *dataBuffer; + int bufSize; + + if ((strlen(name) > 4) && (scumm_stricmp(name + strlen(name) - 4, ".SPR") == 0)) { + // SPR files have some inconsistencies. Some have extra 0x1A at the end, some others + // do not have a carriage return at the end of the last line + // Therefore, we remove this ending 0x1A and add extra new lines. + // This fixes bug #3537527 + dataBuffer = (byte *)malloc(kp->_size + 2); + _vm->_resman->read(dataBuffer, kp->_size); + if (dataBuffer[kp->_size - 1] == 0x1A) + dataBuffer[kp->_size - 1] = '\n'; + dataBuffer[kp->_size] = '\n'; + dataBuffer[kp->_size + 1] = '\n'; + bufSize = kp->_size + 2; + } else { + dataBuffer = (byte *)malloc(kp->_size); + _vm->_resman->read(dataBuffer, kp->_size); + bufSize = kp->_size; + } + + _readStream = new Common::MemoryReadStream(dataBuffer, bufSize, DisposeAfterUse::YES); } uint32 EncryptedStream::read(void *dataPtr, uint32 dataSize) { diff --git a/engines/cge/sound.cpp b/engines/cge/sound.cpp index 7f74794474..b378898955 100644 --- a/engines/cge/sound.cpp +++ b/engines/cge/sound.cpp @@ -91,6 +91,12 @@ void Sound::sndDigiStart(SmpInfo *PSmpInfo) { // Start the new sound _vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, &_soundHandle, Audio::makeLoopingAudioStream(_audioStream, (uint)PSmpInfo->_counter)); + + // CGE pan: + // 8 = Center + // Less = Left + // More = Right + _vm->_mixer->setChannelBalance(_soundHandle, (int8)CLIP(((PSmpInfo->_span - 8) * 16), -127, 127)); } void Sound::stop() { diff --git a/engines/cge/vga13h.cpp b/engines/cge/vga13h.cpp index 3800b6643a..e178795b7c 100644 --- a/engines/cge/vga13h.cpp +++ b/engines/cge/vga13h.cpp @@ -637,15 +637,6 @@ Vga::Vga(CGEEngine *vm) : _frmCnt(0), _msg(NULL), _name(NULL), _setPal(false), _ _page[idx]->create(320, 200, Graphics::PixelFormat::createFormatCLUT8()); } -#if 0 - // This part was used to display credits at the beginning of the game - for (int i = 10; i < 20; i++) { - char *text = _text->getText(i); - if (text) { - debugN(1, "%s\n", text); - } - } -#endif _oldColors = (Dac *)malloc(sizeof(Dac) * kPalCount); _newColors = (Dac *)malloc(sizeof(Dac) * kPalCount); getColors(_oldColors); diff --git a/engines/cge/vga13h.h b/engines/cge/vga13h.h index beca19f667..a816f7756f 100644 --- a/engines/cge/vga13h.h +++ b/engines/cge/vga13h.h @@ -29,6 +29,7 @@ #define CGE_VGA13H_H #include "common/serializer.h" +#include "common/events.h" #include "graphics/surface.h" #include "cge/general.h" #include "cge/bitmap.h" @@ -146,7 +147,7 @@ public: void step(int nr = -1); Seq *setSeq(Seq *seq); CommandHandler::Command *snList(SnList type); - virtual void touch(uint16 mask, int x, int y); + virtual void touch(uint16 mask, int x, int y, Common::KeyCode keyCode); virtual void tick(); void sync(Common::Serializer &s); private: diff --git a/engines/cge/vmenu.cpp b/engines/cge/vmenu.cpp index a317a765d4..910e54d267 100644 --- a/engines/cge/vmenu.cpp +++ b/engines/cge/vmenu.cpp @@ -63,7 +63,7 @@ Vmenu *Vmenu::_addr = NULL; int Vmenu::_recent = -1; Vmenu::Vmenu(CGEEngine *vm, Choice *list, int x, int y) - : Talk(vm, VMGather(list), kTBRect), _menu(list), _bar(NULL), _vm(vm) { + : Talk(vm, VMGather(list), kTBRect), _menu(list), _bar(NULL), _vmgt(NULL), _vm(vm) { Choice *cp; _addr = this; @@ -89,11 +89,11 @@ Vmenu::~Vmenu() { #define CALL_MEMBER_FN(object,ptrToMember) ((object).*(ptrToMember)) -void Vmenu::touch(uint16 mask, int x, int y) { +void Vmenu::touch(uint16 mask, int x, int y, Common::KeyCode keyCode) { if (!_items) return; - Sprite::touch(mask, x, y); + Sprite::touch(mask, x, y, keyCode); y -= kTextVMargin - 1; int n = 0; diff --git a/engines/cge/vmenu.h b/engines/cge/vmenu.h index 89ef7a9484..928b48f11c 100644 --- a/engines/cge/vmenu.h +++ b/engines/cge/vmenu.h @@ -58,7 +58,7 @@ public: MenuBar *_bar; Vmenu(CGEEngine *vm, Choice *list, int x, int y); ~Vmenu(); - virtual void touch(uint16 mask, int x, int y); + virtual void touch(uint16 mask, int x, int y, Common::KeyCode keyCode); private: char *_vmgt; CGEEngine *_vm; diff --git a/engines/cine/anim.cpp b/engines/cine/anim.cpp index 410fcca1f3..60168831a1 100644 --- a/engines/cine/anim.cpp +++ b/engines/cine/anim.cpp @@ -682,9 +682,10 @@ void convert8BBP2(byte *dest, byte *source, int16 width, int16 height) { * Load image set * @param resourceName Image set filename * @param idx Target index in animDataTable (-1 if any empty space will do) + * @param frameIndex frame of animation to load (-1 for all frames) * @return The number of the animDataTable entry after the loaded image set (-1 if error) */ -int loadSet(const char *resourceName, int16 idx) { +int loadSet(const char *resourceName, int16 idx, int16 frameIndex =-1 ) { AnimHeader2Struct header2; uint16 numSpriteInAnim; int16 foundFileIdx = findFileInBundle(resourceName); @@ -708,7 +709,17 @@ int loadSet(const char *resourceName, int16 idx) { entry = idx < 0 ? emptyAnimSpace() : idx; assert(entry >= 0); - for (int16 i = 0; i < numSpriteInAnim; i++, entry++) { + int16 startFrame = 0; + int16 endFrame = numSpriteInAnim; + + if(frameIndex>=0) + { + startFrame = frameIndex; + endFrame = frameIndex+1; + ptr += 0x10 * frameIndex; + } + + for (int16 i = startFrame; i < endFrame; i++, entry++) { Common::MemoryReadStream readS(ptr, 0x10); header2.field_0 = readS.readUint32BE(); @@ -767,7 +778,7 @@ int loadSeq(const char *resourceName, int16 idx) { * @return The number of the animDataTable entry after the loaded resource (-1 if error) * @todo Implement loading of all resource types */ -int loadResource(const char *resourceName, int16 idx) { +int loadResource(const char *resourceName, int16 idx, int16 frameIndex) { int result = -1; // Return an error by default if (strstr(resourceName, ".SPL")) { result = loadSpl(resourceName, idx); @@ -778,7 +789,7 @@ int loadResource(const char *resourceName, int16 idx) { } else if (strstr(resourceName, ".ANM")) { result = loadAni(resourceName, idx); } else if (strstr(resourceName, ".SET")) { - result = loadSet(resourceName, idx); + result = loadSet(resourceName, idx, frameIndex); } else if (strstr(resourceName, ".SEQ")) { result = loadSeq(resourceName, idx); } else if (strstr(resourceName, ".H32")) { diff --git a/engines/cine/anim.h b/engines/cine/anim.h index 9c06c260ce..c5130aab82 100644 --- a/engines/cine/anim.h +++ b/engines/cine/anim.h @@ -98,7 +98,7 @@ public: void freeAnimDataTable(); void freeAnimDataRange(byte startIdx, byte numIdx); -int loadResource(const char *resourceName, int16 idx = -1); +int loadResource(const char *resourceName, int16 idx = -1, int16 frameIndex = -1); void loadResourcesFromSave(Common::SeekableReadStream &fHandle, enum CineSaveGameFormat saveGameFormat); void generateMask(const byte *sprite, byte *mask, uint16 size, byte transparency); diff --git a/engines/cine/bg_list.cpp b/engines/cine/bg_list.cpp index 693fea3294..36ecf53dea 100644 --- a/engines/cine/bg_list.cpp +++ b/engines/cine/bg_list.cpp @@ -39,9 +39,9 @@ uint32 var8; * @param objIdx Sprite description */ void addToBGList(int16 objIdx) { - renderer->incrustSprite(g_cine->_objectTable[objIdx]); - createBgIncrustListElement(objIdx, 0); + + renderer->incrustSprite(g_cine->_bgIncrustList.back()); } /** @@ -49,9 +49,9 @@ void addToBGList(int16 objIdx) { * @param objIdx Sprite description */ void addSpriteFilledToBGList(int16 objIdx) { - renderer->incrustMask(g_cine->_objectTable[objIdx]); - createBgIncrustListElement(objIdx, 1); + + renderer->incrustMask(g_cine->_bgIncrustList.back()); } /** @@ -103,9 +103,9 @@ void loadBgIncrustFromSave(Common::SeekableReadStream &fHandle) { g_cine->_bgIncrustList.push_back(tmp); if (tmp.param == 0) { - renderer->incrustSprite(g_cine->_objectTable[tmp.objIdx]); + renderer->incrustSprite(tmp); } else { - renderer->incrustMask(g_cine->_objectTable[tmp.objIdx]); + renderer->incrustMask(tmp); } } } diff --git a/engines/cine/cine.cpp b/engines/cine/cine.cpp index 6b94c33c31..bbe2cd4896 100644 --- a/engines/cine/cine.cpp +++ b/engines/cine/cine.cpp @@ -189,6 +189,8 @@ void CineEngine::initialize() { g_cine->_messageTable.clear(); resetObjectTable(); + disableSystemMenu = 1; + var8 = 0; var2 = var3 = var4 = var5 = 0; diff --git a/engines/cine/detection_tables.h b/engines/cine/detection_tables.h index 0ec2768bae..bf02f0519c 100644 --- a/engines/cine/detection_tables.h +++ b/engines/cine/detection_tables.h @@ -251,7 +251,7 @@ static const CINEGameDescription gameDescriptions[] = { AD_ENTRY1("procs00", "d6752e7d25924cb866b61eb7cb0c8b56"), Common::EN_GRB, Common::kPlatformPC, - ADGF_NO_FLAGS, + ADGF_UNSTABLE, GUIO0() }, GType_OS, @@ -267,7 +267,7 @@ static const CINEGameDescription gameDescriptions[] = { AD_ENTRY1("procs1", "9629129b86979fa592c1787385bf3695"), Common::EN_GRB, Common::kPlatformPC, - ADGF_NO_FLAGS, + ADGF_UNSTABLE, GUIO0() }, GType_OS, @@ -281,7 +281,7 @@ static const CINEGameDescription gameDescriptions[] = { AD_ENTRY1("procs1", "d8c3a9d05a63e4cfa801826a7063a126"), Common::EN_USA, Common::kPlatformPC, - ADGF_NO_FLAGS, + ADGF_UNSTABLE, GUIO0() }, GType_OS, @@ -295,7 +295,7 @@ static const CINEGameDescription gameDescriptions[] = { AD_ENTRY1("procs00", "862a75d76fb7fffec30e52be9ad1c474"), Common::EN_USA, Common::kPlatformPC, - ADGF_NO_FLAGS, + ADGF_UNSTABLE, GUIO0() }, GType_OS, @@ -309,7 +309,7 @@ static const CINEGameDescription gameDescriptions[] = { AD_ENTRY1("procs1", "39b91ae35d1297ce0a76a1a803ca1593"), Common::DE_DEU, Common::kPlatformPC, - ADGF_NO_FLAGS, + ADGF_UNSTABLE, GUIO0() }, GType_OS, @@ -323,7 +323,7 @@ static const CINEGameDescription gameDescriptions[] = { AD_ENTRY1("procs1", "74c2dabd9d212525fca8875a5f6d8994"), Common::ES_ESP, Common::kPlatformPC, - ADGF_NO_FLAGS, + ADGF_UNSTABLE, GUIO0() }, GType_OS, @@ -341,7 +341,7 @@ static const CINEGameDescription gameDescriptions[] = { }, Common::ES_ESP, Common::kPlatformPC, - ADGF_NO_FLAGS, + ADGF_UNSTABLE, GUIO0() }, GType_OS, @@ -355,7 +355,7 @@ static const CINEGameDescription gameDescriptions[] = { AD_ENTRY1("procs00", "f143567f08cfd1a9b1c9a41c89eadfef"), Common::FR_FRA, Common::kPlatformPC, - ADGF_NO_FLAGS, + ADGF_UNSTABLE, GUIO0() }, GType_OS, @@ -369,7 +369,7 @@ static const CINEGameDescription gameDescriptions[] = { AD_ENTRY1("procs1", "da066e6b8dd93f2502c2a3755f08dc12"), Common::IT_ITA, Common::kPlatformPC, - ADGF_NO_FLAGS, + ADGF_UNSTABLE, GUIO0() }, GType_OS, @@ -383,7 +383,7 @@ static const CINEGameDescription gameDescriptions[] = { AD_ENTRY1("procs0", "a9da5531ead0ebf9ad387fa588c0cbb0"), Common::EN_GRB, Common::kPlatformAmiga, - ADGF_NO_FLAGS, + ADGF_UNSTABLE, GUIO1(GUIO_NOMIDI) }, GType_OS, @@ -397,7 +397,7 @@ static const CINEGameDescription gameDescriptions[] = { AD_ENTRY1("procs0", "8a429ced2f4acff8a15ae125174042e8"), Common::EN_GRB, Common::kPlatformAmiga, - ADGF_NO_FLAGS, + ADGF_UNSTABLE, GUIO1(GUIO_NOMIDI) }, GType_OS, @@ -411,7 +411,7 @@ static const CINEGameDescription gameDescriptions[] = { AD_ENTRY1("procs0", "d5f27e33fc29c879f36f15b86ccfa58c"), Common::EN_USA, Common::kPlatformAmiga, - ADGF_NO_FLAGS, + ADGF_UNSTABLE, GUIO1(GUIO_NOMIDI) }, GType_OS, @@ -425,7 +425,7 @@ static const CINEGameDescription gameDescriptions[] = { AD_ENTRY1("procs0", "8b7dce249821d3a62b314399c4334347"), Common::DE_DEU, Common::kPlatformAmiga, - ADGF_NO_FLAGS, + ADGF_UNSTABLE, GUIO1(GUIO_NOMIDI) }, GType_OS, @@ -439,7 +439,7 @@ static const CINEGameDescription gameDescriptions[] = { AD_ENTRY1("procs0", "35fc295ddd0af9da932d256ba799a4b0"), Common::ES_ESP, Common::kPlatformAmiga, - ADGF_NO_FLAGS, + ADGF_UNSTABLE, GUIO1(GUIO_NOMIDI) }, GType_OS, @@ -453,7 +453,7 @@ static const CINEGameDescription gameDescriptions[] = { AD_ENTRY1("procs0", "d4ea4a97e01fa67ea066f9e785050ed2"), Common::FR_FRA, Common::kPlatformAmiga, - ADGF_NO_FLAGS, + ADGF_UNSTABLE, GUIO1(GUIO_NOMIDI) }, GType_OS, @@ -467,7 +467,7 @@ static const CINEGameDescription gameDescriptions[] = { AD_ENTRY1("demo", "8d3a750d1c840b1b1071e42f9e6f6aa2"), Common::EN_GRB, Common::kPlatformAmiga, - ADGF_DEMO, + ADGF_DEMO | ADGF_UNSTABLE, GUIO1(GUIO_NOMIDI) }, GType_OS, @@ -481,7 +481,7 @@ static const CINEGameDescription gameDescriptions[] = { AD_ENTRY1("procs0", "1501d5ae364b2814a33ed19347c3fcae"), Common::EN_GRB, Common::kPlatformAtariST, - ADGF_NO_FLAGS, + ADGF_UNSTABLE, GUIO1(GUIO_NOMIDI) }, GType_OS, @@ -495,7 +495,7 @@ static const CINEGameDescription gameDescriptions[] = { AD_ENTRY1("procs0", "2148d25de3219dd4a36580ca735d0afa"), Common::FR_FRA, Common::kPlatformAtariST, - ADGF_NO_FLAGS, + ADGF_UNSTABLE, GUIO1(GUIO_NOMIDI) }, GType_OS, diff --git a/engines/cine/gfx.cpp b/engines/cine/gfx.cpp index 918d522606..7a988227f6 100644 --- a/engines/cine/gfx.cpp +++ b/engines/cine/gfx.cpp @@ -174,7 +174,8 @@ void FWRenderer::fillSprite(const ObjectStruct &obj, uint8 color) { * @param obj Object info * @param fillColor Sprite color */ -void FWRenderer::incrustMask(const ObjectStruct &obj, uint8 color) { +void FWRenderer::incrustMask(const BGIncrust &incrust, uint8 color) { + const ObjectStruct &obj = g_cine->_objectTable[incrust.objIdx]; const byte *data = g_cine->_animDataTable[obj.frame].data(); int x, y, width, height; @@ -218,7 +219,9 @@ void FWRenderer::drawSprite(const ObjectStruct &obj) { * Draw color sprite on background * @param obj Object info */ -void FWRenderer::incrustSprite(const ObjectStruct &obj) { +void FWRenderer::incrustSprite(const BGIncrust &incrust) { + const ObjectStruct &obj = g_cine->_objectTable[incrust.objIdx]; + const byte *data = g_cine->_animDataTable[obj.frame].data(); const byte *mask = g_cine->_animDataTable[obj.frame].mask(); int x, y, width, height; @@ -246,14 +249,16 @@ void FWRenderer::drawCommand() { unsigned int i; int x = 10, y = _cmdY; - drawPlainBox(x, y, 301, 11, 0); - drawBorder(x - 1, y - 1, 302, 12, 2); + if(disableSystemMenu == 0) { + drawPlainBox(x, y, 301, 11, 0); + drawBorder(x - 1, y - 1, 302, 12, 2); - x += 2; - y += 2; + x += 2; + y += 2; - for (i = 0; i < _cmd.size(); i++) { - x = drawChar(_cmd[i], x, y); + for (i = 0; i < _cmd.size(); i++) { + x = drawChar(_cmd[i], x, y); + } } } @@ -298,7 +303,8 @@ void FWRenderer::drawMessage(const char *str, int x, int y, int width, int color for (i = 0; str[i]; i++, line--) { // Fit line of text into textbox if (!line) { - while (str[i] == ' ') i++; + while (str[i] == ' ') + i++; line = fitLine(str + i, tw, words, cw); if ( str[i + line] != '\0' && str[i + line] != 0x7C && words) { @@ -839,7 +845,7 @@ void OSRenderer::restorePalette(Common::SeekableReadStream &fHandle, int version fHandle.read(buf, kHighPalNumBytes); - if (colorCount == kHighPalNumBytes) { + if (colorCount == kHighPalNumColors) { // Load the active 256 color palette from file _activePal.load(buf, sizeof(buf), kHighPalFormat, kHighPalNumColors, CINE_LITTLE_ENDIAN); } else { @@ -1119,7 +1125,8 @@ void OSRenderer::clear() { * @param obj Object info * @param fillColor Sprite color */ -void OSRenderer::incrustMask(const ObjectStruct &obj, uint8 color) { +void OSRenderer::incrustMask(const BGIncrust &incrust, uint8 color) { + const ObjectStruct &obj = g_cine->_objectTable[incrust.objIdx]; const byte *data = g_cine->_animDataTable[obj.frame].data(); int x, y, width, height; @@ -1154,15 +1161,16 @@ void OSRenderer::drawSprite(const ObjectStruct &obj) { * Draw color sprite * @param obj Object info */ -void OSRenderer::incrustSprite(const ObjectStruct &obj) { - const byte *data = g_cine->_animDataTable[obj.frame].data(); +void OSRenderer::incrustSprite(const BGIncrust &incrust) { + const ObjectStruct &obj = g_cine->_objectTable[incrust.objIdx]; + const byte *data = g_cine->_animDataTable[incrust.frame].data(); int x, y, width, height, transColor; - x = obj.x; - y = obj.y; + x = incrust.x; + y = incrust.y; transColor = obj.part; - width = g_cine->_animDataTable[obj.frame]._realWidth; - height = g_cine->_animDataTable[obj.frame]._height; + width = g_cine->_animDataTable[incrust.frame]._realWidth; + height = g_cine->_animDataTable[incrust.frame]._height; if (_bgTable[_currentBg].bg) { drawSpriteRaw2(data, transColor, width, height, _bgTable[_currentBg].bg, x, y); @@ -1225,7 +1233,6 @@ void OSRenderer::renderOverlay(const Common::List<overlay>::iterator &it) { int len, idx, width, height; ObjectStruct *obj; AnimData *sprite; - byte *mask; byte color; switch (it->type) { @@ -1235,14 +1242,8 @@ void OSRenderer::renderOverlay(const Common::List<overlay>::iterator &it) { break; } sprite = &g_cine->_animDataTable[g_cine->_objectTable[it->objIdx].frame]; - len = sprite->_realWidth * sprite->_height; - mask = new byte[len]; - generateMask(sprite->data(), mask, len, g_cine->_objectTable[it->objIdx].part); - remaskSprite(mask, it); - drawMaskedSprite(g_cine->_objectTable[it->objIdx], mask); - delete[] mask; + drawSprite(&(*it), sprite->data(), sprite->_realWidth, sprite->_height, _backBuffer, g_cine->_objectTable[it->objIdx].x, g_cine->_objectTable[it->objIdx].y, g_cine->_objectTable[it->objIdx].part, sprite->_bpp); break; - // game message case 2: if (it->objIdx >= g_cine->_messageTable.size()) { @@ -1290,14 +1291,6 @@ void OSRenderer::renderOverlay(const Common::List<overlay>::iterator &it) { maskBgOverlay(_bgTable[it->x].bg, sprite->data(), sprite->_realWidth, sprite->_height, _backBuffer, obj->x, obj->y); break; - // FIXME: Implement correct drawing of type 21 overlays. - // Type 21 overlays aren't just filled rectangles, I found their drawing routine - // from Operation Stealth's drawSprite routine. So they're likely some kind of sprites - // and it's just a coincidence that the oxygen meter during the first arcade sequence - // works even somehow currently. I tried the original under DOSBox and the oxygen gauge - // is a long red bar that gets shorter as the air runs out. - case 21: - // A filled rectangle: case 22: // TODO: Check it this implementation really works correctly (Some things might be wrong, needs testing). assert(it->objIdx < NUM_MAX_OBJECT); @@ -1752,6 +1745,82 @@ void drawSpriteRaw(const byte *spritePtr, const byte *maskPtr, int16 width, int1 } } +void OSRenderer::drawSprite(overlay *overlayPtr, const byte *spritePtr, int16 width, int16 height, byte *page, int16 x, int16 y, byte transparentColor, byte bpp) { + byte *pMask = NULL; + + // draw the mask based on next objects in the list + Common::List<overlay>::iterator it; + for (it = g_cine->_overlayList.begin(); it != g_cine->_overlayList.end(); ++it) { + if(&(*it) == overlayPtr) { + break; + } + } + + while(it != g_cine->_overlayList.end()) { + overlay *pCurrentOverlay = &(*it); + if ((pCurrentOverlay->type == 5) || ((pCurrentOverlay->type == 21) && (pCurrentOverlay->x == overlayPtr->objIdx))) { + AnimData *sprite = &g_cine->_animDataTable[g_cine->_objectTable[it->objIdx].frame]; + + if (pMask == NULL) { + pMask = new byte[width*height]; + + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) { + byte spriteColor= spritePtr[width * i + j]; + pMask[width * i + j] = spriteColor; + } + } + } + + for (int i = 0; i < sprite->_realWidth; i++) { + for (int j = 0; j < sprite->_height; j++) { + int inMaskX = (g_cine->_objectTable[it->objIdx].x + i) - x; + int inMaskY = (g_cine->_objectTable[it->objIdx].y + j) - y; + + if (inMaskX >=0 && inMaskX < width) { + if (inMaskY >= 0 && inMaskY < height) { + if (sprite->_bpp == 1) { + if (!sprite->getColor(i, j)) { + pMask[inMaskY * width + inMaskX] = page[x + y * 320 + inMaskX + inMaskY * 320]; + } + } + } + } + } + } + } + it++; + } + + // now, draw with the mask we created + if(pMask) { + spritePtr = pMask; + } + + // ignore transparent color in 1bpp + if (bpp == 1) { + transparentColor = 1; + } + + { + for (int i = 0; i < height; i++) { + byte *destPtr = page + x + y * 320; + destPtr += i * 320; + + for (int j = 0; j < width; j++) { + byte color= *(spritePtr++); + if ((transparentColor != color) && x + j >= 0 && x + j < 320 && i + y >= 0 && i + y < 200) { + *(destPtr++) = color; + } else { + destPtr++; + } + } + } + } + + delete[] pMask; +} + void drawSpriteRaw2(const byte *spritePtr, byte transColor, int16 width, int16 height, byte *page, int16 x, int16 y) { int16 i, j; diff --git a/engines/cine/gfx.h b/engines/cine/gfx.h index 737c49cc36..3434cf9fc2 100644 --- a/engines/cine/gfx.h +++ b/engines/cine/gfx.h @@ -27,6 +27,7 @@ #include "common/rect.h" #include "common/stack.h" #include "cine/object.h" +#include "cine/bg_list.h" namespace Cine { @@ -177,8 +178,8 @@ public: void drawFrame(); void setCommand(Common::String cmd); - virtual void incrustMask(const ObjectStruct &obj, uint8 color = 0); - virtual void incrustSprite(const ObjectStruct &obj); + virtual void incrustMask(const BGIncrust &incrust, uint8 color = 0); + virtual void incrustSprite(const BGIncrust &incrust); virtual void loadBg16(const byte *bg, const char *name, unsigned int idx = 0); virtual void loadCt16(const byte *ct, const char *name); @@ -223,6 +224,7 @@ private: protected: void drawSprite(const ObjectStruct &obj); + void drawSprite(overlay *overlayPtr, const byte *spritePtr, int16 width, int16 height, byte *page, int16 x, int16 y, byte transparentColor, byte bpp); int drawChar(char character, int x, int y); void drawBackground(); void renderOverlay(const Common::List<overlay>::iterator &it); @@ -238,8 +240,8 @@ public: void clear(); - void incrustMask(const ObjectStruct &obj, uint8 color = 0); - void incrustSprite(const ObjectStruct &obj); + void incrustMask(const BGIncrust &incrust, uint8 color = 0); + void incrustSprite(const BGIncrust &incrust); void loadBg16(const byte *bg, const char *name, unsigned int idx = 0); void loadCt16(const byte *ct, const char *name); diff --git a/engines/cine/main_loop.cpp b/engines/cine/main_loop.cpp index 971830ce8f..f13f38a45e 100644 --- a/engines/cine/main_loop.cpp +++ b/engines/cine/main_loop.cpp @@ -56,6 +56,12 @@ static void processEvent(Common::Event &event) { case Common::EVENT_RBUTTONDOWN: mouseRight = 1; break; + case Common::EVENT_LBUTTONUP: + mouseLeft = 0; + break; + case Common::EVENT_RBUTTONUP: + mouseRight = 0; + break; case Common::EVENT_MOUSEMOVE: break; case Common::EVENT_KEYDOWN: @@ -115,7 +121,7 @@ static void processEvent(Common::Event &event) { } break; case Common::KEYCODE_F10: - if (!disableSystemMenu && !inMenu) { + if (!inMenu) { g_cine->makeSystemMenu(); } break; @@ -214,8 +220,6 @@ void manageEvents() { g_sound->update(); mouseData.left = mouseLeft; mouseData.right = mouseRight; - mouseLeft = 0; - mouseRight = 0; } void getMouseData(uint16 param, uint16 *pButton, uint16 *pX, uint16 *pY) { @@ -311,6 +315,7 @@ void CineEngine::mainLoop(int bootScriptIdx) { // HACK: Force amount of oxygen left to maximum during Operation Stealth's first arcade sequence. // This makes it possible to pass the arcade sequence for now. // FIXME: Remove the hack and make the first arcade sequence normally playable. + /* if (g_cine->getGameType() == Cine::GType_OS) { Common::String bgName(renderer->getBgName()); // Check if the background is one of the three backgrounds @@ -320,7 +325,7 @@ void CineEngine::mainLoop(int bootScriptIdx) { // Force the amount of oxygen left to the maximum. g_cine->_objectTable[oxygenObjNum].x = maxOxygen; } - } + }*/ // HACK: In Operation Stealth after the first arcade sequence jump player's position to avoid getting stuck. // After the first arcade sequence the player comes up stairs from @@ -379,8 +384,8 @@ void CineEngine::mainLoop(int bootScriptIdx) { playerAction = false; _messageLen <<= 3; - if (_messageLen < 0x800) - _messageLen = 0x800; + if (_messageLen < 800) + _messageLen = 800; do { manageEvents(); diff --git a/engines/cine/saveload.cpp b/engines/cine/saveload.cpp index 223099a587..20952eea52 100644 --- a/engines/cine/saveload.cpp +++ b/engines/cine/saveload.cpp @@ -991,7 +991,7 @@ void CineEngine::makeSave(char *saveFileName) { * at a time. */ void loadResourcesFromSave(Common::SeekableReadStream &fHandle, enum CineSaveGameFormat saveGameFormat) { - int16 currentAnim, foundFileIdx; + int16 foundFileIdx; char *animName, part[256], name[10]; strcpy(part, currentPartName); @@ -1001,10 +1001,10 @@ void loadResourcesFromSave(Common::SeekableReadStream &fHandle, enum CineSaveGam const int entrySize = ((saveGameFormat == ANIMSIZE_23) ? 23 : 30); const int fileStartPos = fHandle.pos(); - currentAnim = 0; - while (currentAnim < NUM_MAX_ANIMDATA) { + + for(int resourceIndex=0; resourceIndex<NUM_MAX_ANIMDATA; resourceIndex++) { // Seek to the start of the current animation's entry - fHandle.seek(fileStartPos + currentAnim * entrySize); + fHandle.seek(fileStartPos + resourceIndex * entrySize); // Read in the current animation entry fHandle.readUint16BE(); // width fHandle.readUint16BE(); @@ -1019,7 +1019,7 @@ void loadResourcesFromSave(Common::SeekableReadStream &fHandle, enum CineSaveGam } foundFileIdx = fHandle.readSint16BE(); - fHandle.readSint16BE(); // frame + int16 frameIndex = fHandle.readSint16BE(); // frame fHandle.read(name, 10); // Handle variables only present in animation entries of size 23 @@ -1029,7 +1029,7 @@ void loadResourcesFromSave(Common::SeekableReadStream &fHandle, enum CineSaveGam // Don't try to load invalid entries. if (foundFileIdx < 0 || !validPtr) { - currentAnim++; // Jump over the invalid entry + //resourceIndex++; // Jump over the invalid entry continue; } @@ -1041,9 +1041,7 @@ void loadResourcesFromSave(Common::SeekableReadStream &fHandle, enum CineSaveGam animName = g_cine->_partBuffer[foundFileIdx].partName; loadRelatedPalette(animName); // Is this for Future Wars only? - const int16 prevAnim = currentAnim; - currentAnim = loadResource(animName, currentAnim); - assert(currentAnim > prevAnim); // Make sure we advance forward + loadResource(animName, resourceIndex, frameIndex); } loadPart(part); diff --git a/engines/cine/script_fw.cpp b/engines/cine/script_fw.cpp index 66150cc5b2..9cbe3c3fab 100644 --- a/engines/cine/script_fw.cpp +++ b/engines/cine/script_fw.cpp @@ -533,7 +533,6 @@ void RawScript::setData(const FWScriptInfo &info, const byte *data) { * @return Precalculated script labels */ const ScriptVars &RawScript::labels() const { - assert(_data); return _labels; } @@ -687,7 +686,7 @@ const char *FWScript::getNextString() { * @param pos Restored script position */ void FWScript::load(const ScriptVars &labels, const ScriptVars &local, uint16 compare, uint16 pos) { - assert(pos < _script._size); + assert(pos <= _script._size); _labels = labels; _localVars = local; _compare = compare; @@ -705,13 +704,15 @@ void FWScript::load(const ScriptVars &labels, const ScriptVars &local, uint16 co int FWScript::execute() { int ret = 0; - while (!ret) { - _line = _pos; - byte opcode = getNextByte(); - OpFunc handler = _info->opcodeHandler(opcode); + if(_script._size) { + while (!ret) { + _line = _pos; + byte opcode = getNextByte(); + OpFunc handler = _info->opcodeHandler(opcode); - if (handler) { - ret = (this->*handler)(); + if (handler) { + ret = (this->*handler)(); + } } } @@ -1861,7 +1862,7 @@ int FWScript::o1_disableSystemMenu() { byte param = getNextByte(); debugC(5, kCineDebugScript, "Line: %d: disableSystemMenu(%d)", _line, param); - disableSystemMenu = (param != 0); + disableSystemMenu = param; return 0; } diff --git a/engines/cine/sound.cpp b/engines/cine/sound.cpp index b2e992e8f6..52e1cdac7e 100644 --- a/engines/cine/sound.cpp +++ b/engines/cine/sound.cpp @@ -785,7 +785,6 @@ PCSoundFxPlayer::~PCSoundFxPlayer() { bool PCSoundFxPlayer::load(const char *song) { debug(9, "PCSoundFxPlayer::load('%s')", song); - Common::StackLock lock(_mutex); /* stop (w/ fade out) the previous song */ while (_fadeOutCounter != 0 && _fadeOutCounter < 100) { @@ -793,6 +792,8 @@ bool PCSoundFxPlayer::load(const char *song) { } _fadeOutCounter = 0; + Common::StackLock lock(_mutex); + stop(); _sfxData = readBundleSoundFile(song); diff --git a/engines/cine/various.cpp b/engines/cine/various.cpp index 9b73ae1101..eccd71cf05 100644 --- a/engines/cine/various.cpp +++ b/engines/cine/various.cpp @@ -36,7 +36,7 @@ namespace Cine { -bool disableSystemMenu = false; +int16 disableSystemMenu = 0; bool inMenu; int16 commandVar3[4]; @@ -341,7 +341,7 @@ void CineEngine::makeSystemMenu() { int16 mouseX, mouseY, mouseButton; int16 selectedSave; - if (!disableSystemMenu) { + if (disableSystemMenu != 1) { inMenu = true; do { @@ -544,14 +544,16 @@ int16 buildObjectListCommand(int16 param) { int16 selectSubObject(int16 x, int16 y, int16 param) { int16 listSize = buildObjectListCommand(param); - int16 selectedObject; + int16 selectedObject = -1; bool osExtras = g_cine->getGameType() == Cine::GType_OS; if (!listSize) { return -2; } - selectedObject = makeMenuChoice(objectListCommand, listSize, x, y, 140, osExtras); + if (disableSystemMenu == 0) { + selectedObject = makeMenuChoice(objectListCommand, listSize, x, y, 140, osExtras); + } if (selectedObject == -1) return -1; @@ -691,9 +693,6 @@ int16 makeMenuChoice(const CommandeType commandList[], uint16 height, uint16 X, int16 var_4; SelectionMenu *menu; - if (disableSystemMenu) - return -1; - paramY = (height * 9) + 10; if (X + width > 319) { @@ -810,14 +809,18 @@ void makeActionMenu() { getMouseData(mouseUpdateStatus, &mouseButton, &mouseX, &mouseY); if (g_cine->getGameType() == Cine::GType_OS) { - playerCommand = makeMenuChoice(defaultActionCommand, 6, mouseX, mouseY, 70, true); + if(disableSystemMenu == 0) { + playerCommand = makeMenuChoice(defaultActionCommand, 6, mouseX, mouseY, 70, true); + } if (playerCommand >= 8000) { playerCommand -= 8000; canUseOnObject = canUseOnItemTable[playerCommand]; } } else { - playerCommand = makeMenuChoice(defaultActionCommand, 6, mouseX, mouseY, 70); + if(disableSystemMenu == 0) { + playerCommand = makeMenuChoice(defaultActionCommand, 6, mouseX, mouseY, 70); + } } inMenu = false; diff --git a/engines/cine/various.h b/engines/cine/various.h index 0c1883c323..813619816d 100644 --- a/engines/cine/various.h +++ b/engines/cine/various.h @@ -41,7 +41,7 @@ void makeActionMenu(); void waitPlayerInput(); void setTextWindow(uint16 param1, uint16 param2, uint16 param3, uint16 param4); -extern bool disableSystemMenu; +extern int16 disableSystemMenu; extern bool inMenu; extern CommandeType currentSaveName[10]; diff --git a/engines/composer/composer.cpp b/engines/composer/composer.cpp index 556dad7e94..23a9d2ff85 100644 --- a/engines/composer/composer.cpp +++ b/engines/composer/composer.cpp @@ -381,11 +381,17 @@ void ComposerEngine::loadLibrary(uint id) { filename = getStringFromConfig(_bookGroup, Common::String::format("%d", id)); filename = mangleFilename(filename); + // bookGroup is the basename of the path. + // TODO: tidy this up. _bookGroup.clear(); for (uint i = 0; i < filename.size(); i++) { - if (filename[i] == '\\' || filename[i] == ':') + if (filename[i] == '~' || filename[i] == '/' || filename[i] == ':') continue; for (uint j = 0; j < filename.size(); j++) { + if (filename[j] == '/') { + _bookGroup.clear(); + continue; + } if (filename[j] == '.') break; _bookGroup += filename[j]; diff --git a/engines/cruise/detection.cpp b/engines/cruise/detection.cpp index b2e267ca49..cbe17ea4d3 100644 --- a/engines/cruise/detection.cpp +++ b/engines/cruise/detection.cpp @@ -303,8 +303,6 @@ SaveStateDescriptor CruiseMetaEngine::querySaveMetaInfos(const char *target, int // Create the return descriptor SaveStateDescriptor desc(slot, header.saveName); - desc.setDeletableFlag(true); - desc.setWriteProtectedFlag(false); desc.setThumbnail(header.thumbnail); return desc; diff --git a/engines/draci/detection.cpp b/engines/draci/detection.cpp index de76eb83e0..61705a1e59 100644 --- a/engines/draci/detection.cpp +++ b/engines/draci/detection.cpp @@ -161,8 +161,6 @@ SaveStateDescriptor DraciMetaEngine::querySaveMetaInfos(const char *target, int // Create the return descriptor SaveStateDescriptor desc(slot, header.saveName); - desc.setDeletableFlag(true); - desc.setWriteProtectedFlag(false); desc.setThumbnail(header.thumbnail); int day = (header.date >> 24) & 0xFF; diff --git a/engines/dreamweb/detection.cpp b/engines/dreamweb/detection.cpp index 6468281ac2..f2e2f42216 100644 --- a/engines/dreamweb/detection.cpp +++ b/engines/dreamweb/detection.cpp @@ -172,8 +172,6 @@ SaveStateDescriptor DreamWebMetaEngine::querySaveMetaInfos(const char *target, i saveName += (char)in->readByte(); SaveStateDescriptor desc(slot, saveName); - desc.setDeletableFlag(true); - desc.setWriteProtectedFlag(false); // Check if there is a ScummVM data block if (header.len(6) == SCUMMVM_BLOCK_MAGIC_SIZE) { diff --git a/engines/dreamweb/detection_tables.h b/engines/dreamweb/detection_tables.h index 063aabbd89..8a2f94f99b 100644 --- a/engines/dreamweb/detection_tables.h +++ b/engines/dreamweb/detection_tables.h @@ -46,7 +46,7 @@ static const DreamWebGameDescription gameDescriptions[] = { }, Common::EN_ANY, Common::kPlatformPC, - ADGF_TESTING, + 0, GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_BRIGHTPALETTE) }, }, @@ -63,7 +63,7 @@ static const DreamWebGameDescription gameDescriptions[] = { }, Common::EN_ANY, Common::kPlatformPC, - ADGF_CD | ADGF_TESTING, + ADGF_CD, GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_BRIGHTPALETTE) }, }, @@ -84,7 +84,7 @@ static const DreamWebGameDescription gameDescriptions[] = { }, Common::EN_GRB, Common::kPlatformPC, - ADGF_CD | ADGF_TESTING, + ADGF_CD, GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_BRIGHTPALETTE) }, }, @@ -101,7 +101,7 @@ static const DreamWebGameDescription gameDescriptions[] = { }, Common::EN_USA, Common::kPlatformPC, - ADGF_CD | ADGF_TESTING, + ADGF_CD, GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_BRIGHTPALETTE) }, }, @@ -118,7 +118,7 @@ static const DreamWebGameDescription gameDescriptions[] = { }, Common::FR_FRA, Common::kPlatformPC, - ADGF_CD | ADGF_TESTING, + ADGF_CD, GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_BRIGHTPALETTE) }, }, @@ -136,7 +136,7 @@ static const DreamWebGameDescription gameDescriptions[] = { }, Common::FR_FRA, Common::kPlatformPC, - ADGF_CD | ADGF_TESTING, + ADGF_CD, GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_BRIGHTPALETTE) }, }, @@ -153,7 +153,7 @@ static const DreamWebGameDescription gameDescriptions[] = { }, Common::DE_DEU, Common::kPlatformPC, - ADGF_TESTING, + 0, GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_BRIGHTPALETTE) }, }, @@ -170,7 +170,7 @@ static const DreamWebGameDescription gameDescriptions[] = { }, Common::DE_DEU, Common::kPlatformPC, - ADGF_CD | ADGF_TESTING, + ADGF_CD, GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_BRIGHTPALETTE) }, }, @@ -187,7 +187,7 @@ static const DreamWebGameDescription gameDescriptions[] = { }, Common::ES_ESP, Common::kPlatformPC, - ADGF_TESTING, + 0, GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_BRIGHTPALETTE) }, }, @@ -204,7 +204,7 @@ static const DreamWebGameDescription gameDescriptions[] = { }, Common::ES_ESP, Common::kPlatformPC, - ADGF_CD | ADGF_TESTING, + ADGF_CD, GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_BRIGHTPALETTE) }, }, @@ -222,7 +222,7 @@ static const DreamWebGameDescription gameDescriptions[] = { }, Common::ES_ESP, Common::kPlatformPC, - ADGF_CD | ADGF_TESTING, + ADGF_CD, GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_BRIGHTPALETTE) }, }, @@ -239,7 +239,7 @@ static const DreamWebGameDescription gameDescriptions[] = { }, Common::IT_ITA, Common::kPlatformPC, - ADGF_TESTING, + 0, GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_BRIGHTPALETTE) }, }, diff --git a/engines/dreamweb/dreamweb.h b/engines/dreamweb/dreamweb.h index 48d44c0380..1f6deb8566 100644 --- a/engines/dreamweb/dreamweb.h +++ b/engines/dreamweb/dreamweb.h @@ -152,8 +152,8 @@ public: uint8 modifyChar(uint8 c) const; Common::String modifyFileName(const char *); - const Common::String& getDatafilePrefix() { return _datafilePrefix; }; - const Common::String& getSpeechDirName() { return _speechDirName; }; + const Common::String& getDatafilePrefix() { return _datafilePrefix; } + const Common::String& getSpeechDirName() { return _speechDirName; } private: void keyPressed(uint16 ascii); diff --git a/engines/dreamweb/stubs.cpp b/engines/dreamweb/stubs.cpp index 4515939ebc..f235f7c2fd 100644 --- a/engines/dreamweb/stubs.cpp +++ b/engines/dreamweb/stubs.cpp @@ -2920,9 +2920,7 @@ void DreamWebEngine::setupInitialVars() { _vars._progressPoints = 0; _vars._watchOn = 0; _vars._shadesOn = 0; - _vars._secondCount = 0; - _vars._minuteCount = 30; - _vars._hourCount = 19; + getTime(); _vars._zoomOn = 1; _vars._location = 0; _vars._exPos = 0; diff --git a/engines/gob/anifile.cpp b/engines/gob/anifile.cpp index 2671fe0405..085ac800cd 100644 --- a/engines/gob/anifile.cpp +++ b/engines/gob/anifile.cpp @@ -37,30 +37,38 @@ ANIFile::ANIFile(GobEngine *vm, const Common::String &fileName, uint16 width, uint8 bpp) : _vm(vm), _width(width), _bpp(bpp), _hasPadding(false) { - Common::SeekableReadStream *ani = _vm->_dataIO->getFile(fileName); - if (ani) { - Common::SeekableSubReadStreamEndian sub(ani, 0, ani->size(), false, DisposeAfterUse::YES); + bool bigEndian = false; + Common::String endianFileName = fileName; - load(sub, fileName); - return; - } + if ((_vm->getEndiannessMethod() == kEndiannessMethodAltFile) && + !_vm->_dataIO->hasFile(fileName)) { + // If the game has alternate big-endian files, look if one exist - // File doesn't exist, try to open the big-endian'd alternate file - Common::String alternateFileName = fileName; - alternateFileName.setChar('_', 0); + Common::String alternateFileName = fileName; + alternateFileName.setChar('_', 0); - ani = _vm->_dataIO->getFile(alternateFileName); + if (_vm->_dataIO->hasFile(alternateFileName)) { + bigEndian = true; + endianFileName = alternateFileName; + } + } else if ((_vm->getEndiannessMethod() == kEndiannessMethodBE) || + ((_vm->getEndiannessMethod() == kEndiannessMethodSystem) && + (_vm->getEndianness() == kEndiannessBE))) + // Game always little endian or it follows the system and it is big endian + bigEndian = true; + + Common::SeekableReadStream *ani = _vm->_dataIO->getFile(endianFileName); if (ani) { - Common::SeekableSubReadStreamEndian sub(ani, 0, ani->size(), true, DisposeAfterUse::YES); + Common::SeekableSubReadStreamEndian sub(ani, 0, ani->size(), bigEndian, DisposeAfterUse::YES); // The big endian version pads a few fields to even size - _hasPadding = true; + _hasPadding = bigEndian; load(sub, fileName); return; } - warning("ANIFile::ANIFile(): No such file \"%s\"", fileName.c_str()); + warning("ANIFile::ANIFile(): No such file \"%s\" (\"%s\")", endianFileName.c_str(), fileName.c_str()); } ANIFile::~ANIFile() { @@ -281,4 +289,9 @@ void ANIFile::drawLayer(Surface &dest, uint16 layer, uint16 part, _layers[layer]->draw(dest, part, x, y, transp); } +void ANIFile::recolor(uint8 from, uint8 to) { + for (LayerArray::iterator l = _layers.begin(); l != _layers.end(); ++l) + (*l)->recolor(from, to); +} + } // End of namespace Gob diff --git a/engines/gob/anifile.h b/engines/gob/anifile.h index b6d9c735b5..c930aafc6b 100644 --- a/engines/gob/anifile.h +++ b/engines/gob/anifile.h @@ -92,6 +92,9 @@ public: /** Draw an animation frame. */ void draw(Surface &dest, uint16 animation, uint16 frame, int16 x, int16 y) const; + /** Recolor the animation sprites. */ + void recolor(uint8 from, uint8 to); + private: typedef Common::Array<CMPFile *> LayerArray; typedef Common::Array<Animation> AnimationArray; diff --git a/engines/gob/aniobject.cpp b/engines/gob/aniobject.cpp index 8d739fb3a4..7e3668a0ce 100644 --- a/engines/gob/aniobject.cpp +++ b/engines/gob/aniobject.cpp @@ -28,23 +28,20 @@ namespace Gob { ANIObject::ANIObject(const ANIFile &ani) : _ani(&ani), _cmp(0), - _visible(false), _paused(false), _mode(kModeContinuous), - _x(0), _y(0), _background(0), _drawn(false) { + _visible(false), _paused(false), _mode(kModeContinuous), _x(0), _y(0) { setAnimation(0); setPosition(); } ANIObject::ANIObject(const CMPFile &cmp) : _ani(0), _cmp(&cmp), - _visible(false), _paused(false), _mode(kModeContinuous), - _x(0), _y(0), _background(0), _drawn(false) { + _visible(false), _paused(false), _mode(kModeContinuous), _x(0), _y(0) { setAnimation(0); setPosition(); } ANIObject::~ANIObject() { - delete _background; } void ANIObject::setVisible(bool visible) { @@ -104,7 +101,7 @@ void ANIObject::getPosition(int16 &x, int16 &y) const { y = _y; } -void ANIObject::getFramePosition(int16 &x, int16 &y) const { +void ANIObject::getFramePosition(int16 &x, int16 &y, uint16 n) const { // CMP "animations" have no specific frame positions if (_cmp) { getPosition(x, y); @@ -118,11 +115,24 @@ void ANIObject::getFramePosition(int16 &x, int16 &y) const { if (_frame >= animation.frameCount) return; - x = _x + animation.frameAreas[_frame].left; - y = _y + animation.frameAreas[_frame].top; + // If we're paused, we don't advance any frames + if (_paused) + n = 0; + + // Number of cycles run through after n frames + uint16 cycles = (_frame + n) / animation.frameCount; + // Frame position after n frames + uint16 frame = (_frame + n) % animation.frameCount; + + // Only doing one cycle? + if (_mode == kModeOnce) + cycles = MAX<uint16>(cycles, 1); + + x = _x + animation.frameAreas[frame].left + cycles * animation.deltaX; + y = _y + animation.frameAreas[frame].top + cycles * animation.deltaY; } -void ANIObject::getFrameSize(int16 &width, int16 &height) const { +void ANIObject::getFrameSize(int16 &width, int16 &height, uint16 n) const { if (_cmp) { width = _cmp->getWidth (_animation); height = _cmp->getHeight(_animation); @@ -137,8 +147,15 @@ void ANIObject::getFrameSize(int16 &width, int16 &height) const { if (_frame >= animation.frameCount) return; - width = animation.frameAreas[_frame].right - animation.frameAreas[_frame].left + 1; - height = animation.frameAreas[_frame].bottom - animation.frameAreas[_frame].top + 1; + // If we're paused, we don't advance any frames + if (_paused) + n = 0; + + // Frame position after n frames + uint16 frame = (_frame + n) % animation.frameCount; + + width = animation.frameAreas[frame].right - animation.frameAreas[frame].left + 1; + height = animation.frameAreas[frame].bottom - animation.frameAreas[frame].top + 1; } bool ANIObject::isIn(int16 x, int16 y) const { @@ -188,46 +205,36 @@ bool ANIObject::draw(Surface &dest, int16 &left, int16 &top, bool ANIObject::drawCMP(Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom) { - if (!_background) { + if (!hasBuffer()) { uint16 width, height; _cmp->getMaxSize(width, height); - _background = new Surface(width, height, dest.getBPP()); + resizeBuffer(width, height); } - const uint16 cR = _cmp->getWidth (_animation) - 1; - const uint16 cB = _cmp->getHeight(_animation) - 1; - - _backgroundLeft = CLIP<int16>( + _x, 0, dest.getWidth () - 1); - _backgroundTop = CLIP<int16>( + _y, 0, dest.getHeight() - 1); - _backgroundRight = CLIP<int16>(cR + _x, 0, dest.getWidth () - 1); - _backgroundBottom = CLIP<int16>(cB + _y, 0, dest.getHeight() - 1); + left = _x; + top = _y; + right = _x + _cmp->getWidth (_animation) - 1; + bottom = _y + _cmp->getHeight(_animation) - 1; - _background->blit(dest, _backgroundLeft , _backgroundTop, - _backgroundRight, _backgroundBottom, 0, 0); + if (!saveScreen(dest, left, top, right, bottom)) + return false; _cmp->draw(dest, _animation, _x, _y, 0); - _drawn = true; - - left = _backgroundLeft; - top = _backgroundTop; - right = _backgroundRight; - bottom = _backgroundBottom; - return true; } bool ANIObject::drawANI(Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom) { - if (!_background) { + if (!hasBuffer()) { uint16 width, height; _ani->getMaxSize(width, height); - _background = new Surface(width, height, dest.getBPP()); + resizeBuffer(width, height); } const ANIFile::Animation &animation = _ani->getAnimationInfo(_animation); @@ -236,45 +243,23 @@ bool ANIObject::drawANI(Surface &dest, int16 &left, int16 &top, const ANIFile::FrameArea &area = animation.frameAreas[_frame]; - _backgroundLeft = CLIP<int16>(area.left + _x, 0, dest.getWidth () - 1); - _backgroundTop = CLIP<int16>(area.top + _y, 0, dest.getHeight() - 1); - _backgroundRight = CLIP<int16>(area.right + _x, 0, dest.getWidth () - 1); - _backgroundBottom = CLIP<int16>(area.bottom + _y, 0, dest.getHeight() - 1); + left = _x + area.left; + top = _y + area.top; + right = _x + area.right; + bottom = _y + area.bottom; - _background->blit(dest, _backgroundLeft , _backgroundTop, - _backgroundRight, _backgroundBottom, 0, 0); + if (!saveScreen(dest, left, top, right, bottom)) + return false; _ani->draw(dest, _animation, _frame, _x, _y); - _drawn = true; - - left = _backgroundLeft; - top = _backgroundTop; - right = _backgroundRight; - bottom = _backgroundBottom; - return true; } bool ANIObject::clear(Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom) { - if (!_drawn) - return false; - - const int16 bgRight = _backgroundRight - _backgroundLeft; - const int16 bgBottom = _backgroundBottom - _backgroundTop; - - dest.blit(*_background, 0, 0, bgRight, bgBottom, _backgroundLeft, _backgroundTop); - - _drawn = false; - - left = _backgroundLeft; - top = _backgroundTop; - right = _backgroundRight; - bottom = _backgroundBottom; - - return true; + return restoreScreen(dest, left, top, right, bottom); } void ANIObject::advance() { diff --git a/engines/gob/aniobject.h b/engines/gob/aniobject.h index 00f42b43ce..d8c8edc2b8 100644 --- a/engines/gob/aniobject.h +++ b/engines/gob/aniobject.h @@ -25,6 +25,8 @@ #include "common/system.h" +#include "gob/backbuffer.h" + namespace Gob { class ANIFile; @@ -32,7 +34,7 @@ class CMPFile; class Surface; /** An ANI object, controlling an animation within an ANI file. */ -class ANIObject { +class ANIObject : public BackBuffer { public: enum Mode { kModeContinuous, ///< Play the animation continuously. @@ -68,10 +70,10 @@ public: /** Return the current position. */ void getPosition(int16 &x, int16 &y) const; - /** Return the current frame position. */ - void getFramePosition(int16 &x, int16 &y) const; - /** Return the current frame size. */ - void getFrameSize(int16 &width, int16 &height) const; + /** Return the frame position after another n frames. */ + void getFramePosition(int16 &x, int16 &y, uint16 n = 0) const; + /** Return the current frame size after another n frames. */ + void getFrameSize(int16 &width, int16 &height, uint16 n = 0) const; /** Are there coordinates within the animation sprite? */ bool isIn(int16 x, int16 y) const; @@ -118,13 +120,6 @@ private: int16 _x; ///< The current X position. int16 _y; ///< The current Y position. - Surface *_background; ///< The saved background. - bool _drawn; ///< Was the animation drawn? - - int16 _backgroundLeft; ///< The left position of the saved background. - int16 _backgroundTop; ///< The top of the saved background. - int16 _backgroundRight; ///< The right position of the saved background. - int16 _backgroundBottom; ///< The bottom position of the saved background. bool drawCMP(Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom); bool drawANI(Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom); diff --git a/engines/gob/backbuffer.cpp b/engines/gob/backbuffer.cpp new file mode 100644 index 0000000000..752042d46e --- /dev/null +++ b/engines/gob/backbuffer.cpp @@ -0,0 +1,100 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public 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/util.h" + +#include "gob/backbuffer.h" +#include "gob/surface.h" + +namespace Gob { + +BackBuffer::BackBuffer() : _background(0), _saved(false) { +} + +BackBuffer::~BackBuffer() { + delete _background; +} + +bool BackBuffer::hasBuffer() const { + return _background != 0; +} + +bool BackBuffer::hasSavedBackground() const { + return _saved; +} + +void BackBuffer::trashBuffer() { + _saved = false; +} + +void BackBuffer::resizeBuffer(uint16 width, uint16 height) { + trashBuffer(); + + if (_background && (_background->getWidth() == width) && (_background->getHeight() == height)) + return; + + delete _background; + + _background = new Surface(width, height, 1); +} + +bool BackBuffer::saveScreen(const Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom) { + if (!_background) + return false; + + const int16 width = MIN<int16>(right - left + 1, _background->getWidth ()); + const int16 height = MIN<int16>(bottom - top + 1, _background->getHeight()); + if ((width <= 0) || (height <= 0)) + return false; + + right = left + width - 1; + bottom = top + height - 1; + + _saveLeft = left; + _saveTop = top; + _saveRight = right; + _saveBottom = bottom; + + _background->blit(dest, _saveLeft, _saveTop, _saveRight, _saveBottom, 0, 0); + + _saved = true; + + return true; +} + +bool BackBuffer::restoreScreen(Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom) { + if (!_saved) + return false; + + left = _saveLeft; + top = _saveTop; + right = _saveRight; + bottom = _saveBottom; + + dest.blit(*_background, 0, 0, right - left, bottom - top, left, top); + + _saved = false; + + return true; +} + +} // End of namespace Gob diff --git a/engines/gob/backbuffer.h b/engines/gob/backbuffer.h new file mode 100644 index 0000000000..c978689e9f --- /dev/null +++ b/engines/gob/backbuffer.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. + * + */ + +#ifndef GOB_BACKBUFFER_H +#define GOB_BACKBUFFER_H + +#include "common/system.h" + +namespace Gob { + +class Surface; + +class BackBuffer { +public: + BackBuffer(); + ~BackBuffer(); + +protected: + void trashBuffer(); + void resizeBuffer(uint16 width, uint16 height); + + bool saveScreen (const Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom); + bool restoreScreen( Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom); + + bool hasBuffer() const; + bool hasSavedBackground() const; + +private: + Surface *_background; ///< The saved background. + bool _saved; ///< Was the background saved? + + int16 _saveLeft; ///< The left position of the saved background. + int16 _saveTop; ///< The top of the saved background. + int16 _saveRight; ///< The right position of the saved background. + int16 _saveBottom; ///< The bottom position of the saved background. +}; + +} // End of namespace Gob + +#endif // GOB_BACKBUFFER_H diff --git a/engines/gob/cmpfile.cpp b/engines/gob/cmpfile.cpp index 7b21c4c835..d304958f76 100644 --- a/engines/gob/cmpfile.cpp +++ b/engines/gob/cmpfile.cpp @@ -21,6 +21,7 @@ */ #include "common/stream.h" +#include "common/substream.h" #include "common/str.h" #include "gob/gob.h" @@ -143,7 +144,13 @@ void CMPFile::loadCMP(Common::SeekableReadStream &cmp) { } void CMPFile::loadRXY(Common::SeekableReadStream &rxy) { - _coordinates = new RXYFile(rxy); + bool bigEndian = (_vm->getEndiannessMethod() == kEndiannessMethodBE) || + ((_vm->getEndiannessMethod() == kEndiannessMethodSystem) && + (_vm->getEndianness() == kEndiannessBE)); + + Common::SeekableSubReadStreamEndian sub(&rxy, 0, rxy.size(), bigEndian, DisposeAfterUse::NO); + + _coordinates = new RXYFile(sub); for (uint i = 0; i < _coordinates->size(); i++) { const RXYFile::Coordinates &c = (*_coordinates)[i]; @@ -243,4 +250,9 @@ uint16 CMPFile::addSprite(uint16 left, uint16 top, uint16 right, uint16 bottom) return _coordinates->add(left, top, right, bottom); } +void CMPFile::recolor(uint8 from, uint8 to) { + if (_surface) + _surface->recolor(from, to); +} + } // End of namespace Gob diff --git a/engines/gob/cmpfile.h b/engines/gob/cmpfile.h index 2b669e4d38..9c858238af 100644 --- a/engines/gob/cmpfile.h +++ b/engines/gob/cmpfile.h @@ -70,6 +70,8 @@ public: uint16 addSprite(uint16 left, uint16 top, uint16 right, uint16 bottom); + void recolor(uint8 from, uint8 to); + private: GobEngine *_vm; diff --git a/engines/gob/decfile.cpp b/engines/gob/decfile.cpp index fb67c52627..85b4c09ca3 100644 --- a/engines/gob/decfile.cpp +++ b/engines/gob/decfile.cpp @@ -38,30 +38,38 @@ DECFile::DECFile(GobEngine *vm, const Common::String &fileName, uint16 width, uint16 height, uint8 bpp) : _vm(vm), _width(width), _height(height), _bpp(bpp), _hasPadding(false), _backdrop(0) { - Common::SeekableReadStream *dec = _vm->_dataIO->getFile(fileName); - if (dec) { - Common::SeekableSubReadStreamEndian sub(dec, 0, dec->size(), false, DisposeAfterUse::YES); - - load(sub, fileName); - return; - } - - // File doesn't exist, try to open the big-endian'd alternate file - Common::String alternateFileName = fileName; - alternateFileName.setChar('_', 0); - - dec = _vm->_dataIO->getFile(alternateFileName); - if (dec) { - Common::SeekableSubReadStreamEndian sub(dec, 0, dec->size(), true, DisposeAfterUse::YES); + bool bigEndian = false; + Common::String endianFileName = fileName; + + if ((_vm->getEndiannessMethod() == kEndiannessMethodAltFile) && + !_vm->_dataIO->hasFile(fileName)) { + // If the game has alternate big-endian files, look if one exist + + Common::String alternateFileName = fileName; + alternateFileName.setChar('_', 0); + + if (_vm->_dataIO->hasFile(alternateFileName)) { + bigEndian = true; + endianFileName = alternateFileName; + } + } else if ((_vm->getEndiannessMethod() == kEndiannessMethodBE) || + ((_vm->getEndiannessMethod() == kEndiannessMethodSystem) && + (_vm->getEndianness() == kEndiannessBE))) + // Game always little endian or it follows the system and it is big endian + bigEndian = true; + + Common::SeekableReadStream *ani = _vm->_dataIO->getFile(endianFileName); + if (ani) { + Common::SeekableSubReadStreamEndian sub(ani, 0, ani->size(), bigEndian, DisposeAfterUse::YES); // The big endian version pads a few fields to even size - _hasPadding = true; + _hasPadding = bigEndian; load(sub, fileName); return; } - warning("DECFile::DECFile(): No such file \"%s\"", fileName.c_str()); + warning("DECFile::DECFile(): No such file \"%s\" (\"%s\")", endianFileName.c_str(), fileName.c_str()); } DECFile::~DECFile() { diff --git a/engines/gob/detection/detection.cpp b/engines/gob/detection/detection.cpp index bcfd5dacfa..8fb0052a5b 100644 --- a/engines/gob/detection/detection.cpp +++ b/engines/gob/detection/detection.cpp @@ -25,40 +25,146 @@ #include "engines/obsolete.h" #include "gob/gob.h" +#include "gob/dataio.h" #include "gob/detection/tables.h" class GobMetaEngine : public AdvancedMetaEngine { public: - GobMetaEngine() : AdvancedMetaEngine(Gob::gameDescriptions, sizeof(Gob::GOBGameDescription), gobGames) { - _singleid = "gob"; - _guioptions = GUIO1(GUIO_NOLAUNCHLOAD); - } + GobMetaEngine(); - virtual GameDescriptor findGame(const char *gameid) const { - return Engines::findGameID(gameid, _gameids, obsoleteGameIDsTable); - } + virtual GameDescriptor findGame(const char *gameid) const; + + virtual const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const; + + virtual const char *getName() const; + virtual const char *getOriginalCopyright() const; + + virtual bool hasFeature(MetaEngineFeature f) const; + + virtual Common::Error createInstance(OSystem *syst, Engine **engine) const; + virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const; + +private: + /** + * Inspect the game archives to detect which Once Upon A Time game this is. + */ + static const Gob::GOBGameDescription *detectOnceUponATime(const Common::FSList &fslist); +}; + +GobMetaEngine::GobMetaEngine() : + AdvancedMetaEngine(Gob::gameDescriptions, sizeof(Gob::GOBGameDescription), gobGames) { + + _singleid = "gob"; + _guioptions = GUIO1(GUIO_NOLAUNCHLOAD); +} + +GameDescriptor GobMetaEngine::findGame(const char *gameid) const { + return Engines::findGameID(gameid, _gameids, obsoleteGameIDsTable); +} + +const ADGameDescription *GobMetaEngine::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const { + ADFilePropertiesMap filesProps; + + const Gob::GOBGameDescription *game; + game = (const Gob::GOBGameDescription *)detectGameFilebased(allFiles, fslist, Gob::fileBased, &filesProps); + if (!game) + return 0; - virtual const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const { - return detectGameFilebased(allFiles, Gob::fileBased); + if (game->gameType == Gob::kGameTypeOnceUponATime) { + game = detectOnceUponATime(fslist); + if (!game) + return 0; } - virtual const char *getName() const { - return "Gob"; + reportUnknown(fslist.begin()->getParent(), filesProps); + return (const ADGameDescription *)game; +} + +const Gob::GOBGameDescription *GobMetaEngine::detectOnceUponATime(const Common::FSList &fslist) { + // Add the game path to the search manager + SearchMan.clear(); + SearchMan.addDirectory(fslist.begin()->getParent().getPath(), fslist.begin()->getParent()); + + // Open the archives + Gob::DataIO dataIO; + if (!dataIO.openArchive("stk1.stk", true) || + !dataIO.openArchive("stk2.stk", true) || + !dataIO.openArchive("stk3.stk", true)) { + + SearchMan.clear(); + return 0; } - virtual const char *getOriginalCopyright() const { - return "Goblins Games (C) Coktel Vision"; + Gob::OnceUponATime gameType = Gob::kOnceUponATimeInvalid; + Gob::OnceUponATimePlatform platform = Gob::kOnceUponATimePlatformInvalid; + + // If these animal files are present, it's Abracadabra + if (dataIO.hasFile("arai.anm") && + dataIO.hasFile("crab.anm") && + dataIO.hasFile("crap.anm") && + dataIO.hasFile("drag.anm") && + dataIO.hasFile("guep.anm") && + dataIO.hasFile("loup.anm") && + dataIO.hasFile("mous.anm") && + dataIO.hasFile("rhin.anm") && + dataIO.hasFile("saut.anm") && + dataIO.hasFile("scor.anm")) + gameType = Gob::kOnceUponATimeAbracadabra; + + // If these animal files are present, it's Baba Yaga + if (dataIO.hasFile("abei.anm") && + dataIO.hasFile("arai.anm") && + dataIO.hasFile("drag.anm") && + dataIO.hasFile("fauc.anm") && + dataIO.hasFile("gren.anm") && + dataIO.hasFile("rena.anm") && + dataIO.hasFile("sang.anm") && + dataIO.hasFile("serp.anm") && + dataIO.hasFile("tort.anm") && + dataIO.hasFile("vaut.anm")) + gameType = Gob::kOnceUponATimeBabaYaga; + + // Detect the platform by endianness and existence of a MOD file + Common::SeekableReadStream *villeDEC = dataIO.getFile("ville.dec"); + if (villeDEC && (villeDEC->size() > 6)) { + byte data[6]; + + if (villeDEC->read(data, 6) == 6) { + if (!memcmp(data, "\000\000\000\001\000\007", 6)) { + // Big endian -> Amiga or Atari ST + + if (dataIO.hasFile("mod.babayaga")) + platform = Gob::kOnceUponATimePlatformAmiga; + else + platform = Gob::kOnceUponATimePlatformAtariST; + + } else if (!memcmp(data, "\000\000\001\000\007\000", 6)) + // Little endian -> DOS + platform = Gob::kOnceUponATimePlatformDOS; + } + + delete villeDEC; } - virtual bool hasFeature(MetaEngineFeature f) const; + SearchMan.clear(); - virtual Common::Error createInstance(OSystem *syst, Engine **engine) const { - Engines::upgradeTargetIfNecessary(obsoleteGameIDsTable); - return AdvancedMetaEngine::createInstance(syst, engine); + if ((gameType == Gob::kOnceUponATimeInvalid) || (platform == Gob::kOnceUponATimePlatformInvalid)) { + warning("GobMetaEngine::detectOnceUponATime(): Detection failed (%d, %d)", + (int) gameType, (int) platform); + return 0; } - virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const; -}; + + return &Gob::fallbackOnceUpon[gameType][platform]; +} + +const char *GobMetaEngine::getName() const { + return "Gob"; +} + +const char *GobMetaEngine::getOriginalCopyright() const { + return "Goblins Games (C) Coktel Vision"; +} bool GobMetaEngine::hasFeature(MetaEngineFeature f) const { return false; @@ -68,6 +174,12 @@ bool Gob::GobEngine::hasFeature(EngineFeature f) const { return (f == kSupportsRTL); } + +Common::Error GobMetaEngine::createInstance(OSystem *syst, Engine **engine) const { + Engines::upgradeTargetIfNecessary(obsoleteGameIDsTable); + return AdvancedMetaEngine::createInstance(syst, engine); +} + bool GobMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const { const Gob::GOBGameDescription *gd = (const Gob::GOBGameDescription *)desc; if (gd) { @@ -77,6 +189,7 @@ bool GobMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameD return gd != 0; } + #if PLUGIN_ENABLED_DYNAMIC(GOB) REGISTER_PLUGIN_DYNAMIC(GOB, PLUGIN_TYPE_ENGINE, GobMetaEngine); #else diff --git a/engines/gob/detection/tables.h b/engines/gob/detection/tables.h index 5d211ac7d8..271f75af79 100644 --- a/engines/gob/detection/tables.h +++ b/engines/gob/detection/tables.h @@ -48,7 +48,10 @@ static const PlainGameDescriptor gobGames[] = { {"gob2cd", "Gobliins 2 CD"}, {"ween", "Ween: The Prophecy"}, {"bargon", "Bargon Attack"}, + {"babayaga", "Once Upon A Time: Baba Yaga"}, + {"abracadabra", "Once Upon A Time: Abracadabra"}, {"littlered", "Once Upon A Time: Little Red Riding Hood"}, + {"onceupon", "Once Upon A Time"}, {"ajworld", "A.J.'s World of Discovery"}, {"gob3", "Goblins Quest 3"}, {"gob3cd", "Goblins Quest 3 CD"}, @@ -94,6 +97,7 @@ static const GOBGameDescription gameDescriptions[] = { #include "gob/detection/tables_ween.h" // Ween: The Prophecy #include "gob/detection/tables_bargon.h" // Bargon Attack #include "gob/detection/tables_littlered.h" // Once Upon A Time: Little Red Riding Hood + #include "gob/detection/tables_onceupon.h" // Once Upon A Time: Baba Yaga and Abracadabra #include "gob/detection/tables_lit.h" // Lost in Time #include "gob/detection/tables_fascin.h" // Fascination #include "gob/detection/tables_geisha.h" // Geisha diff --git a/engines/gob/detection/tables_ajworld.h b/engines/gob/detection/tables_ajworld.h index c78d11a6ad..d86bdb16be 100644 --- a/engines/gob/detection/tables_ajworld.h +++ b/engines/gob/detection/tables_ajworld.h @@ -1,4 +1,3 @@ - /* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names diff --git a/engines/gob/detection/tables_fallback.h b/engines/gob/detection/tables_fallback.h index 2853ee7b4f..05f579c08c 100644 --- a/engines/gob/detection/tables_fallback.h +++ b/engines/gob/detection/tables_fallback.h @@ -23,6 +23,8 @@ #ifndef GOB_DETECTION_TABLES_FALLBACK_H #define GOB_DETECTION_TABLES_FALLBACK_H +// -- Tables for the filename-based fallback -- + static const GOBGameDescription fallbackDescs[] = { { //0 { @@ -362,6 +364,20 @@ static const GOBGameDescription fallbackDescs[] = { }, { //24 { + "onceupon", + "unknown", + AD_ENTRY1(0, 0), + UNK_LANG, + kPlatformUnknown, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeOnceUponATime, + kFeaturesEGA, + 0, 0, 0 + }, + { //25 + { "adi2", "", AD_ENTRY1(0, 0), @@ -374,7 +390,7 @@ static const GOBGameDescription fallbackDescs[] = { kFeatures640x480, "adi2.stk", 0, 0 }, - { //25 + { //26 { "adi4", "", @@ -388,7 +404,7 @@ static const GOBGameDescription fallbackDescs[] = { kFeatures640x480, "adif41.stk", 0, 0 }, - { //26 + { //27 { "coktelplayer", "unknown", @@ -430,10 +446,119 @@ static const ADFileBasedFallback fileBased[] = { { &fallbackDescs[21].desc, { "disk1.stk", "disk2.stk", "disk3.stk", 0 } }, { &fallbackDescs[22].desc, { "intro.stk", "stk2.stk", "stk3.stk", 0 } }, { &fallbackDescs[23].desc, { "intro.stk", "stk2.stk", "stk3.stk", "mod.babayaga", 0 } }, - { &fallbackDescs[24].desc, { "adi2.stk", 0 } }, - { &fallbackDescs[25].desc, { "adif41.stk", "adim41.stk", 0 } }, - { &fallbackDescs[26].desc, { "coktelplayer.scn", 0 } }, + { &fallbackDescs[24].desc, { "stk1.stk", "stk2.stk", "stk3.stk", 0 } }, + { &fallbackDescs[25].desc, { "adi2.stk", 0 } }, + { &fallbackDescs[26].desc, { "adif41.stk", "adim41.stk", 0 } }, + { &fallbackDescs[27].desc, { "coktelplayer.scn", 0 } }, { 0, { 0 } } }; +// -- Tables for detecting the specific Once Upon A Time game -- + +enum OnceUponATime { + kOnceUponATimeInvalid = -1, + kOnceUponATimeAbracadabra = 0, + kOnceUponATimeBabaYaga = 1, + kOnceUponATimeMAX +}; + +enum OnceUponATimePlatform { + kOnceUponATimePlatformInvalid = -1, + kOnceUponATimePlatformDOS = 0, + kOnceUponATimePlatformAmiga = 1, + kOnceUponATimePlatformAtariST = 2, + kOnceUponATimePlatformMAX +}; + +static const GOBGameDescription fallbackOnceUpon[kOnceUponATimeMAX][kOnceUponATimePlatformMAX] = { + { // kOnceUponATimeAbracadabra + { // kOnceUponATimePlatformDOS + { + "abracadabra", + "", + AD_ENTRY1(0, 0), + UNK_LANG, + kPlatformPC, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeAbracadabra, + kFeaturesAdLib | kFeaturesEGA, + 0, 0, 0 + }, + { // kOnceUponATimePlatformAmiga + { + "abracadabra", + "", + AD_ENTRY1(0, 0), + UNK_LANG, + kPlatformAmiga, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeAbracadabra, + kFeaturesEGA, + 0, 0, 0 + }, + { // kOnceUponATimePlatformAtariST + { + "abracadabra", + "", + AD_ENTRY1(0, 0), + UNK_LANG, + kPlatformAtariST, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeAbracadabra, + kFeaturesEGA, + 0, 0, 0 + } + }, + { // kOnceUponATimeBabaYaga + { // kOnceUponATimePlatformDOS + { + "babayaga", + "", + AD_ENTRY1(0, 0), + UNK_LANG, + kPlatformPC, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeBabaYaga, + kFeaturesAdLib | kFeaturesEGA, + 0, 0, 0 + }, + { // kOnceUponATimePlatformAmiga + { + "babayaga", + "", + AD_ENTRY1(0, 0), + UNK_LANG, + kPlatformAmiga, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeBabaYaga, + kFeaturesEGA, + 0, 0, 0 + }, + { // kOnceUponATimePlatformAtariST + { + "babayaga", + "", + AD_ENTRY1(0, 0), + UNK_LANG, + kPlatformAtariST, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeBabaYaga, + kFeaturesEGA, + 0, 0, 0 + } + } +}; + #endif // GOB_DETECTION_TABLES_FALLBACK_H diff --git a/engines/gob/detection/tables_geisha.h b/engines/gob/detection/tables_geisha.h index 331e17e31d..a32d1ebf81 100644 --- a/engines/gob/detection/tables_geisha.h +++ b/engines/gob/detection/tables_geisha.h @@ -32,7 +32,63 @@ "geisha", "", AD_ENTRY1s("disk1.stk", "6eebbb98ad90cd3c44549fc2ab30f632", 212153), - UNK_LANG, + EN_ANY, + kPlatformPC, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeGeisha, + kFeaturesEGA | kFeaturesAdLib, + "disk1.stk", "intro.tot", 0 +}, +{ + { + "geisha", + "", + AD_ENTRY1s("disk1.stk", "6eebbb98ad90cd3c44549fc2ab30f632", 212153), + DE_DEU, + kPlatformPC, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeGeisha, + kFeaturesEGA | kFeaturesAdLib, + "disk1.stk", "intro.tot", 0 +}, +{ // Supplied by misterhands in bug report #3539797 + { + "geisha", + "", + AD_ENTRY1s("disk1.stk", "0c4c16090921664f50baefdfd24d7f5d", 211889), + FR_FRA, + kPlatformPC, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeGeisha, + kFeaturesEGA | kFeaturesAdLib, + "disk1.stk", "intro.tot", 0 +}, +{ // Supplied by einstein95 in bug report #3544449 + { + "geisha", + "", + AD_ENTRY1s("disk1.stk", "49107ac897e7c00af6c4ecd78a74a710", 212169), + ES_ESP, + kPlatformPC, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeGeisha, + kFeaturesEGA | kFeaturesAdLib, + "disk1.stk", "intro.tot", 0 +}, +{ // Supplied by einstein95 in bug report #3544449 + { + "geisha", + "", + AD_ENTRY1s("disk1.stk", "49107ac897e7c00af6c4ecd78a74a710", 212169), + IT_ITA, kPlatformPC, ADGF_NO_FLAGS, GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) @@ -63,7 +119,7 @@ "geisha", "", AD_ENTRY1s("disk1.stk", "e5892f00917c62423e93f5fd9920cf47", 208120), - UNK_LANG, + EN_ANY, kPlatformAmiga, ADGF_NO_FLAGS, GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) diff --git a/engines/gob/detection/tables_onceupon.h b/engines/gob/detection/tables_onceupon.h new file mode 100644 index 0000000000..366024d43c --- /dev/null +++ b/engines/gob/detection/tables_onceupon.h @@ -0,0 +1,518 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* Detection tables for Once Upon A Time: Baba Yaga and Abracadabra. */ + +#ifndef GOB_DETECTION_TABLES_ONCEUPON_H +#define GOB_DETECTION_TABLES_ONCEUPON_H + +// -- Once Upon A Time: Abracadabra, Amiga -- + +{ + { + "abracadabra", + "", + { + {"stk1.stk", 0, "a8e963eea170155548e5bc1d0f07d50d", 209806}, + {"stk2.stk", 0, "e4b21818af03930dc9cab2ad4c93cb5b", 362106}, + {"stk3.stk", 0, "76874ad92782f9b2de57beafc05ec877", 353482}, + {0, 0, 0, 0} + }, + FR_FRA, + kPlatformAmiga, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeAbracadabra, + kFeaturesEGA, + 0, 0, 0 +}, +{ + { + "abracadabra", + "", + { + {"stk1.stk", 0, "a8e963eea170155548e5bc1d0f07d50d", 209806}, + {"stk2.stk", 0, "e4b21818af03930dc9cab2ad4c93cb5b", 362106}, + {"stk3.stk", 0, "76874ad92782f9b2de57beafc05ec877", 353482}, + {0, 0, 0, 0} + }, + DE_DEU, + kPlatformAmiga, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeAbracadabra, + kFeaturesEGA, + 0, 0, 0 +}, +{ + { + "abracadabra", + "", + { + {"stk1.stk", 0, "a8e963eea170155548e5bc1d0f07d50d", 209806}, + {"stk2.stk", 0, "e4b21818af03930dc9cab2ad4c93cb5b", 362106}, + {"stk3.stk", 0, "76874ad92782f9b2de57beafc05ec877", 353482}, + {0, 0, 0, 0} + }, + EN_ANY, + kPlatformAmiga, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeAbracadabra, + kFeaturesEGA, + 0, 0, 0 +}, +{ + { + "abracadabra", + "", + { + {"stk1.stk", 0, "a8e963eea170155548e5bc1d0f07d50d", 209806}, + {"stk2.stk", 0, "e4b21818af03930dc9cab2ad4c93cb5b", 362106}, + {"stk3.stk", 0, "76874ad92782f9b2de57beafc05ec877", 353482}, + {0, 0, 0, 0} + }, + IT_ITA, + kPlatformAmiga, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeAbracadabra, + kFeaturesEGA, + 0, 0, 0 +}, +{ + { + "abracadabra", + "", + { + {"stk1.stk", 0, "a8e963eea170155548e5bc1d0f07d50d", 209806}, + {"stk2.stk", 0, "e4b21818af03930dc9cab2ad4c93cb5b", 362106}, + {"stk3.stk", 0, "76874ad92782f9b2de57beafc05ec877", 353482}, + {0, 0, 0, 0} + }, + ES_ESP, + kPlatformAmiga, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeAbracadabra, + kFeaturesEGA, + 0, 0, 0 +}, + +// -- Once Upon A Time: Abracadabra, Atari ST -- + +{ + { + "abracadabra", + "", + { + {"stk1.stk", 0, "a8e963eea170155548e5bc1d0f07d50d", 209806}, + {"stk2.stk", 0, "c6440aaf068ec3149ae89bc5c41ebf02", 362123}, + {"stk3.stk", 0, "5af3c1202ba6fcf8dad2b2125e1c1383", 353257}, + {0, 0, 0, 0} + }, + FR_FRA, + kPlatformAtariST, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeAbracadabra, + kFeaturesEGA, + 0, 0, 0 +}, +{ + { + "abracadabra", + "", + { + {"stk1.stk", 0, "a8e963eea170155548e5bc1d0f07d50d", 209806}, + {"stk2.stk", 0, "c6440aaf068ec3149ae89bc5c41ebf02", 362123}, + {"stk3.stk", 0, "5af3c1202ba6fcf8dad2b2125e1c1383", 353257}, + {0, 0, 0, 0} + }, + DE_DEU, + kPlatformAtariST, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeAbracadabra, + kFeaturesEGA, + 0, 0, 0 +}, +{ + { + "abracadabra", + "", + { + {"stk1.stk", 0, "a8e963eea170155548e5bc1d0f07d50d", 209806}, + {"stk2.stk", 0, "c6440aaf068ec3149ae89bc5c41ebf02", 362123}, + {"stk3.stk", 0, "5af3c1202ba6fcf8dad2b2125e1c1383", 353257}, + {0, 0, 0, 0} + }, + EN_ANY, + kPlatformAtariST, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeAbracadabra, + kFeaturesEGA, + 0, 0, 0 +}, +{ + { + "abracadabra", + "", + { + {"stk1.stk", 0, "a8e963eea170155548e5bc1d0f07d50d", 209806}, + {"stk2.stk", 0, "c6440aaf068ec3149ae89bc5c41ebf02", 362123}, + {"stk3.stk", 0, "5af3c1202ba6fcf8dad2b2125e1c1383", 353257}, + {0, 0, 0, 0} + }, + IT_ITA, + kPlatformAtariST, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeAbracadabra, + kFeaturesEGA, + 0, 0, 0 +}, +{ + { + "abracadabra", + "", + { + {"stk1.stk", 0, "a8e963eea170155548e5bc1d0f07d50d", 209806}, + {"stk2.stk", 0, "c6440aaf068ec3149ae89bc5c41ebf02", 362123}, + {"stk3.stk", 0, "5af3c1202ba6fcf8dad2b2125e1c1383", 353257}, + {0, 0, 0, 0} + }, + ES_ESP, + kPlatformAtariST, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeAbracadabra, + kFeaturesEGA, + 0, 0, 0 +}, + +// -- Once Upon A Time: Baba Yaga, DOS EGA Floppy -- + +{ + { + "babayaga", + "", + { + {"stk1.stk", 0, "3c777f43e6fb49fde9222543447e135a", 204813}, + {"stk2.stk", 0, "6cf0b009dd185a8f589e91a1f9c33df5", 361582}, + {"stk3.stk", 0, "6473183ca4db1b5b5cea047f9af59a26", 328925}, + {0, 0, 0, 0} + }, + FR_FRA, + kPlatformPC, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeBabaYaga, + kFeaturesAdLib | kFeaturesEGA, + 0, 0, 0 +}, +{ + { + "babayaga", + "", + { + {"stk1.stk", 0, "3c777f43e6fb49fde9222543447e135a", 204813}, + {"stk2.stk", 0, "6cf0b009dd185a8f589e91a1f9c33df5", 361582}, + {"stk3.stk", 0, "6473183ca4db1b5b5cea047f9af59a26", 328925}, + {0, 0, 0, 0} + }, + DE_DEU, + kPlatformPC, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeBabaYaga, + kFeaturesAdLib | kFeaturesEGA, + 0, 0, 0 +}, +{ + { + "babayaga", + "", + { + {"stk1.stk", 0, "3c777f43e6fb49fde9222543447e135a", 204813}, + {"stk2.stk", 0, "6cf0b009dd185a8f589e91a1f9c33df5", 361582}, + {"stk3.stk", 0, "6473183ca4db1b5b5cea047f9af59a26", 328925}, + {0, 0, 0, 0} + }, + EN_ANY, + kPlatformPC, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeBabaYaga, + kFeaturesAdLib | kFeaturesEGA, + 0, 0, 0 +}, +{ + { + "babayaga", + "", + { + {"stk1.stk", 0, "3c777f43e6fb49fde9222543447e135a", 204813}, + {"stk2.stk", 0, "6cf0b009dd185a8f589e91a1f9c33df5", 361582}, + {"stk3.stk", 0, "6473183ca4db1b5b5cea047f9af59a26", 328925}, + {0, 0, 0, 0} + }, + IT_ITA, + kPlatformPC, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeBabaYaga, + kFeaturesAdLib | kFeaturesEGA, + 0, 0, 0 +}, +{ + { + "babayaga", + "", + { + {"stk1.stk", 0, "3c777f43e6fb49fde9222543447e135a", 204813}, + {"stk2.stk", 0, "6cf0b009dd185a8f589e91a1f9c33df5", 361582}, + {"stk3.stk", 0, "6473183ca4db1b5b5cea047f9af59a26", 328925}, + {0, 0, 0, 0} + }, + ES_ESP, + kPlatformPC, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeBabaYaga, + kFeaturesAdLib | kFeaturesEGA, + 0, 0, 0 +}, + +// -- Once Upon A Time: Baba Yaga, Amiga -- + +{ + { + "babayaga", + "", + { + {"stk1.stk", 0, "bcc823d2888057031e54716ed1b3c80e", 205090}, + {"stk2.stk", 0, "f76bf7c2ff60d816d69962d1a593207c", 362122}, + {"stk3.stk", 0, "6227d1aefdf39d88dcf83e38bea2a9af", 328922}, + {0, 0, 0, 0} + }, + FR_FRA, + kPlatformAmiga, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeBabaYaga, + kFeaturesEGA, + 0, 0, 0 +}, +{ + { + "babayaga", + "", + { + {"stk1.stk", 0, "bcc823d2888057031e54716ed1b3c80e", 205090}, + {"stk2.stk", 0, "f76bf7c2ff60d816d69962d1a593207c", 362122}, + {"stk3.stk", 0, "6227d1aefdf39d88dcf83e38bea2a9af", 328922}, + {0, 0, 0, 0} + }, + DE_DEU, + kPlatformAmiga, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeBabaYaga, + kFeaturesEGA, + 0, 0, 0 +}, +{ + { + "babayaga", + "", + { + {"stk1.stk", 0, "bcc823d2888057031e54716ed1b3c80e", 205090}, + {"stk2.stk", 0, "f76bf7c2ff60d816d69962d1a593207c", 362122}, + {"stk3.stk", 0, "6227d1aefdf39d88dcf83e38bea2a9af", 328922}, + {0, 0, 0, 0} + }, + EN_ANY, + kPlatformAmiga, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeBabaYaga, + kFeaturesEGA, + 0, 0, 0 +}, +{ + { + "babayaga", + "", + { + {"stk1.stk", 0, "bcc823d2888057031e54716ed1b3c80e", 205090}, + {"stk2.stk", 0, "f76bf7c2ff60d816d69962d1a593207c", 362122}, + {"stk3.stk", 0, "6227d1aefdf39d88dcf83e38bea2a9af", 328922}, + {0, 0, 0, 0} + }, + IT_ITA, + kPlatformAmiga, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeBabaYaga, + kFeaturesEGA, + 0, 0, 0 +}, +{ + { + "babayaga", + "", + { + {"stk1.stk", 0, "bcc823d2888057031e54716ed1b3c80e", 205090}, + {"stk2.stk", 0, "f76bf7c2ff60d816d69962d1a593207c", 362122}, + {"stk3.stk", 0, "6227d1aefdf39d88dcf83e38bea2a9af", 328922}, + {0, 0, 0, 0} + }, + ES_ESP, + kPlatformAmiga, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeBabaYaga, + kFeaturesEGA, + 0, 0, 0 +}, + +// -- Once Upon A Time: Baba Yaga, Atari ST -- + +{ + { + "babayaga", + "", + { + {"stk1.stk", 0, "17a4e3e7a18cc97231c92d280c7878a1", 205095}, + {"stk2.stk", 0, "bfbc380e5461f63af28e9e6b10f334b5", 362128}, + {"stk3.stk", 0, "6227d1aefdf39d88dcf83e38bea2a9af", 328922}, + {0, 0, 0, 0} + }, + FR_FRA, + kPlatformAtariST, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeBabaYaga, + kFeaturesEGA, + 0, 0, 0 +}, +{ + { + "babayaga", + "", + { + {"stk1.stk", 0, "17a4e3e7a18cc97231c92d280c7878a1", 205095}, + {"stk2.stk", 0, "bfbc380e5461f63af28e9e6b10f334b5", 362128}, + {"stk3.stk", 0, "6227d1aefdf39d88dcf83e38bea2a9af", 328922}, + {0, 0, 0, 0} + }, + DE_DEU, + kPlatformAtariST, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeBabaYaga, + kFeaturesEGA, + 0, 0, 0 +}, +{ + { + "babayaga", + "", + { + {"stk1.stk", 0, "17a4e3e7a18cc97231c92d280c7878a1", 205095}, + {"stk2.stk", 0, "bfbc380e5461f63af28e9e6b10f334b5", 362128}, + {"stk3.stk", 0, "6227d1aefdf39d88dcf83e38bea2a9af", 328922}, + {0, 0, 0, 0} + }, + EN_ANY, + kPlatformAtariST, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeBabaYaga, + kFeaturesEGA, + 0, 0, 0 +}, +{ + { + "babayaga", + "", + { + {"stk1.stk", 0, "17a4e3e7a18cc97231c92d280c7878a1", 205095}, + {"stk2.stk", 0, "bfbc380e5461f63af28e9e6b10f334b5", 362128}, + {"stk3.stk", 0, "6227d1aefdf39d88dcf83e38bea2a9af", 328922}, + {0, 0, 0, 0} + }, + IT_ITA, + kPlatformAtariST, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeBabaYaga, + kFeaturesEGA, + 0, 0, 0 +}, +{ + { + "babayaga", + "", + { + {"stk1.stk", 0, "17a4e3e7a18cc97231c92d280c7878a1", 205095}, + {"stk2.stk", 0, "bfbc380e5461f63af28e9e6b10f334b5", 362128}, + {"stk3.stk", 0, "6227d1aefdf39d88dcf83e38bea2a9af", 328922}, + {0, 0, 0, 0} + }, + ES_ESP, + kPlatformAtariST, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeBabaYaga, + kFeaturesEGA, + 0, 0, 0 +}, + +#endif // GOB_DETECTION_TABLES_ONCEUPON_H diff --git a/engines/gob/draw.cpp b/engines/gob/draw.cpp index fe59b11f76..8c6919416d 100644 --- a/engines/gob/draw.cpp +++ b/engines/gob/draw.cpp @@ -256,7 +256,7 @@ void Draw::blitInvalidated() { if (_cursorIndex == 4) blitCursor(); - if (_vm->_inter->_terminate) + if (_vm->_inter && _vm->_inter->_terminate) return; if (_noInvalidated && !_applyPal) @@ -271,7 +271,9 @@ void Draw::blitInvalidated() { return; } - _showCursor = (_showCursor & ~2) | ((_showCursor & 1) << 1); + if (_cursorSprites) + _showCursor = (_showCursor & ~2) | ((_showCursor & 1) << 1); + if (_applyPal) { clearPalette(); forceBlit(); @@ -425,28 +427,13 @@ int Draw::stringLength(const char *str, uint16 fontIndex) { return len; } -void Draw::drawString(const char *str, int16 x, int16 y, int16 color1, int16 color2, - int16 transp, Surface &dest, const Font &font) { - - while (*str != '\0') { - const int16 charRight = x + font.getCharWidth(*str); - const int16 charBottom = y + font.getCharHeight(); - - if ((charRight <= dest.getWidth()) && (charBottom <= dest.getHeight())) - font.drawLetter(dest, *str, x, y, color1, color2, transp); - - x += font.getCharWidth(*str); - str++; - } -} - void Draw::printTextCentered(int16 id, int16 left, int16 top, int16 right, int16 bottom, const char *str, int16 fontIndex, int16 color) { adjustCoords(1, &left, &top); adjustCoords(1, &right, &bottom); - uint16 centerOffset = _vm->_game->_script->getFunctionOffset(TOTFile::kFunctionCenter); + uint16 centerOffset = _vm->_game->_script ? _vm->_game->_script->getFunctionOffset(TOTFile::kFunctionCenter) : 0; if (centerOffset != 0) { _vm->_game->_script->call(centerOffset); @@ -505,7 +492,7 @@ void Draw::oPlaytoons_sub_F_1B(uint16 id, int16 left, int16 top, int16 right, in adjustCoords(1, &left, &top); adjustCoords(1, &right, &bottom); - uint16 centerOffset = _vm->_game->_script->getFunctionOffset(TOTFile::kFunctionCenter); + uint16 centerOffset = _vm->_game->_script ? _vm->_game->_script->getFunctionOffset(TOTFile::kFunctionCenter) : 0; if (centerOffset != 0) { _vm->_game->_script->call(centerOffset); diff --git a/engines/gob/draw.h b/engines/gob/draw.h index e7af1f9af3..b51c6466e0 100644 --- a/engines/gob/draw.h +++ b/engines/gob/draw.h @@ -194,8 +194,6 @@ public: adjustCoords(adjust, (int16 *)coord1, (int16 *)coord2); } int stringLength(const char *str, uint16 fontIndex); - void drawString(const char *str, int16 x, int16 y, int16 color1, int16 color2, - int16 transp, Surface &dest, const Font &font); void printTextCentered(int16 id, int16 left, int16 top, int16 right, int16 bottom, const char *str, int16 fontIndex, int16 color); void oPlaytoons_sub_F_1B( uint16 id, int16 left, int16 top, int16 right, int16 bottom, char *paramStr, int16 var3, int16 var4, int16 shortId); diff --git a/engines/gob/draw_fascin.cpp b/engines/gob/draw_fascin.cpp index 54cd52b660..12009d7ee5 100644 --- a/engines/gob/draw_fascin.cpp +++ b/engines/gob/draw_fascin.cpp @@ -222,8 +222,8 @@ void Draw_Fascination::spriteOperation(int16 operation) { _destSpriteX, _destSpriteY, _frontColor, _backColor, _transparency); } } else { - drawString(_textToPrint, _destSpriteX, _destSpriteY, _frontColor, - _backColor, _transparency, *_spritesArray[_destSurface], *font); + font->drawString(_textToPrint, _destSpriteX, _destSpriteY, _frontColor, + _backColor, _transparency, *_spritesArray[_destSurface]); _destSpriteX += len * font->getCharWidth(); } } else { diff --git a/engines/gob/draw_playtoons.cpp b/engines/gob/draw_playtoons.cpp index a443f81ccf..76e2ae591c 100644 --- a/engines/gob/draw_playtoons.cpp +++ b/engines/gob/draw_playtoons.cpp @@ -283,8 +283,8 @@ void Draw_Playtoons::spriteOperation(int16 operation) { _destSpriteX, _destSpriteY, _frontColor, _backColor, _transparency); } } else { - drawString(_textToPrint, _destSpriteX, _destSpriteY, _frontColor, - _backColor, _transparency, *_spritesArray[_destSurface], *font); + font->drawString(_textToPrint, _destSpriteX, _destSpriteY, _frontColor, + _backColor, _transparency, *_spritesArray[_destSurface]); _destSpriteX += len * font->getCharWidth(); } } else { diff --git a/engines/gob/draw_v2.cpp b/engines/gob/draw_v2.cpp index b637ecbd2b..f5475278c4 100644 --- a/engines/gob/draw_v2.cpp +++ b/engines/gob/draw_v2.cpp @@ -74,13 +74,16 @@ void Draw_v2::closeScreen() { } void Draw_v2::blitCursor() { - if (_cursorIndex == -1) + if (!_cursorSprites || (_cursorIndex == -1)) return; _showCursor = (_showCursor & ~2) | ((_showCursor & 1) << 1); } void Draw_v2::animateCursor(int16 cursor) { + if (!_cursorSprites) + return; + int16 cursorIndex = cursor; int16 newX = 0, newY = 0; uint16 hotspotX, hotspotY; @@ -831,8 +834,8 @@ void Draw_v2::spriteOperation(int16 operation) { getColor(_backColor), _transparency); } } else { - drawString(_textToPrint, _destSpriteX, _destSpriteY, getColor(_frontColor), - getColor(_backColor), _transparency, *_spritesArray[_destSurface], *font); + font->drawString(_textToPrint, _destSpriteX, _destSpriteY, getColor(_frontColor), + getColor(_backColor), _transparency, *_spritesArray[_destSurface]); _destSpriteX += len * font->getCharWidth(); } } else { diff --git a/engines/gob/game.cpp b/engines/gob/game.cpp index 0d1953322f..de0c3f2d5c 100644 --- a/engines/gob/game.cpp +++ b/engines/gob/game.cpp @@ -64,7 +64,7 @@ void Environments::clear() { // Deleting unique variables, script and resources for (uint i = 0; i < kEnvironmentCount; i++) { - if (_environments[i].variables == _vm->_inter->_variables) + if (_vm->_inter && (_environments[i].variables == _vm->_inter->_variables)) continue; if (!has(_environments[i].variables, i + 1)) diff --git a/engines/gob/gob.cpp b/engines/gob/gob.cpp index 3d8a18ed38..fcf98f0355 100644 --- a/engines/gob/gob.cpp +++ b/engines/gob/gob.cpp @@ -48,6 +48,10 @@ #include "gob/videoplayer.h" #include "gob/save/saveload.h" +#include "gob/pregob/pregob.h" +#include "gob/pregob/onceupon/abracadabra.h" +#include "gob/pregob/onceupon/babayaga.h" + namespace Gob { #define MAX_TIME_DELTA 100 @@ -115,7 +119,7 @@ GobEngine::GobEngine(OSystem *syst) : Engine(syst), _rnd("gob") { _vidPlayer = 0; _init = 0; _inter = 0; _map = 0; _palAnim = 0; _scenery = 0; _draw = 0; _util = 0; _video = 0; - _saveLoad = 0; + _saveLoad = 0; _preGob = 0; _pauseStart = 0; @@ -180,6 +184,10 @@ void GobEngine::validateVideoMode(int16 videoMode) { error("Video mode 0x%X is not supported", videoMode); } +EndiannessMethod GobEngine::getEndiannessMethod() const { + return _endiannessMethod; +} + Endianness GobEngine::getEndianness() const { if ((getPlatform() == Common::kPlatformAmiga) || (getPlatform() == Common::kPlatformMacintosh) || @@ -274,15 +282,15 @@ void GobEngine::setTrueColor(bool trueColor) { } Common::Error GobEngine::run() { - if (!initGameParts()) { - GUIErrorMessage("GobEngine::init(): Unknown version of game engine"); - return Common::kUnknownError; - } + Common::Error err; - if (!initGraphics()) { - GUIErrorMessage("GobEngine::init(): Failed to set up graphics"); - return Common::kUnknownError; - } + err = initGameParts(); + if (err.getCode() != Common::kNoError) + return err; + + err = initGraphics(); + if (err.getCode() != Common::kNoError) + return err; // On some systems it's not safe to run CD audio games from the CD. if (isCD()) @@ -368,11 +376,12 @@ void GobEngine::pauseEngineIntern(bool pause) { _game->_startTimeKey += duration; _draw->_cursorTimeKey += duration; - if (_inter->_soundEndTimeKey != 0) + if (_inter && (_inter->_soundEndTimeKey != 0)) _inter->_soundEndTimeKey += duration; } - _vidPlayer->pauseAll(pause); + if (_vidPlayer) + _vidPlayer->pauseAll(pause); _mixer->pauseAll(pause); } @@ -392,12 +401,13 @@ void GobEngine::pauseGame() { pauseEngineIntern(false); } -bool GobEngine::initGameParts() { +Common::Error GobEngine::initGameParts() { _resourceSizeWorkaround = false; // just detect some devices some of which will be always there if the music is not disabled _noMusic = MidiDriver::getMusicType(MidiDriver::detectDevice(MDT_PCSPK | MDT_MIDI | MDT_ADLIB)) == MT_NULL ? true : false; - _saveLoad = 0; + + _endiannessMethod = kEndiannessMethodSystem; _global = new Global(this); _util = new Util(this); @@ -429,6 +439,8 @@ bool GobEngine::initGameParts() { _goblin = new Goblin_v1(this); _scenery = new Scenery_v1(this); _saveLoad = new SaveLoad_Geisha(this, _targetName.c_str()); + + _endiannessMethod = kEndiannessMethodAltFile; break; case kGameTypeFascination: @@ -605,20 +617,45 @@ bool GobEngine::initGameParts() { _scenery = new Scenery_v2(this); _saveLoad = new SaveLoad_v2(this, _targetName.c_str()); break; + + case kGameTypeAbracadabra: + _init = new Init_v2(this); + _video = new Video_v2(this); + _mult = new Mult_v2(this); + _draw = new Draw_v2(this); + _map = new Map_v2(this); + _goblin = new Goblin_v2(this); + _scenery = new Scenery_v2(this); + _preGob = new OnceUpon::Abracadabra(this); + break; + + case kGameTypeBabaYaga: + _init = new Init_v2(this); + _video = new Video_v2(this); + _mult = new Mult_v2(this); + _draw = new Draw_v2(this); + _map = new Map_v2(this); + _goblin = new Goblin_v2(this); + _scenery = new Scenery_v2(this); + _preGob = new OnceUpon::BabaYaga(this); + break; + default: deinitGameParts(); - return false; + return Common::kUnsupportedGameidError; } // Setup mixer syncSoundSettings(); - _inter->setupOpcodes(); + if (_inter) + _inter->setupOpcodes(); - return true; + return Common::kNoError; } void GobEngine::deinitGameParts() { + delete _preGob; _preGob = 0; delete _saveLoad; _saveLoad = 0; delete _mult; _mult = 0; delete _vidPlayer; _vidPlayer = 0; @@ -637,10 +674,10 @@ void GobEngine::deinitGameParts() { delete _dataIO; _dataIO = 0; } -bool GobEngine::initGraphics() { +Common::Error GobEngine::initGraphics() { if (is800x600()) { warning("GobEngine::initGraphics(): 800x600 games currently unsupported"); - return false; + return Common::kUnsupportedGameidError; } else if (is640x480()) { _width = 640; _height = 480; @@ -664,7 +701,7 @@ bool GobEngine::initGraphics() { _global->_primarySurfDesc = SurfacePtr(new Surface(_width, _height, _pixelFormat.bytesPerPixel)); - return true; + return Common::kNoError; } } // End of namespace Gob diff --git a/engines/gob/gob.h b/engines/gob/gob.h index 52f3ba8f2d..df73404596 100644 --- a/engines/gob/gob.h +++ b/engines/gob/gob.h @@ -75,6 +75,7 @@ class Scenery; class Util; class SaveLoad; class GobConsole; +class PreGob; #define WRITE_VAR_UINT32(var, val) _vm->_inter->_variables->writeVar32(var, val) #define WRITE_VAR_UINT16(var, val) _vm->_inter->_variables->writeVar16(var, val) @@ -129,7 +130,10 @@ enum GameType { kGameTypeAdi4, kGameTypeAdibou2, kGameTypeAdibou1, + kGameTypeAbracadabra, + kGameTypeBabaYaga, kGameTypeLittleRed, + kGameTypeOnceUponATime, // Need more inspection to see if Baba Yaga or Abracadabra kGameTypeAJWorld }; @@ -145,6 +149,13 @@ enum Features { kFeaturesTrueColor = 1 << 7 }; +enum EndiannessMethod { + kEndiannessMethodLE, ///< Always little endian. + kEndiannessMethodBE, ///< Always big endian. + kEndiannessMethodSystem, ///< Follows system endianness. + kEndiannessMethodAltFile ///< Different endianness in alternate file. +}; + enum { kDebugFuncOp = 1 << 0, kDebugDrawOp = 1 << 1, @@ -168,6 +179,8 @@ private: int32 _features; Common::Platform _platform; + EndiannessMethod _endiannessMethod; + uint32 _pauseStart; // Engine APIs @@ -176,10 +189,10 @@ private: virtual void pauseEngineIntern(bool pause); virtual void syncSoundSettings(); - bool initGameParts(); - void deinitGameParts(); + Common::Error initGameParts(); + Common::Error initGraphics(); - bool initGraphics(); + void deinitGameParts(); public: static const Common::Language _gobToScummVMLang[]; @@ -220,6 +233,7 @@ public: Inter *_inter; SaveLoad *_saveLoad; VideoPlayer *_vidPlayer; + PreGob *_preGob; const char *getLangDesc(int16 language) const; void validateLanguage(); @@ -227,6 +241,7 @@ public: void pauseGame(); + EndiannessMethod getEndiannessMethod() const; Endianness getEndianness() const; Common::Platform getPlatform() const; GameType getGameType() const; diff --git a/engines/gob/init.cpp b/engines/gob/init.cpp index a61261f355..814d4d1821 100644 --- a/engines/gob/init.cpp +++ b/engines/gob/init.cpp @@ -34,9 +34,13 @@ #include "gob/inter.h" #include "gob/video.h" #include "gob/videoplayer.h" + +#include "gob/sound/sound.h" + #include "gob/demos/scnplayer.h" #include "gob/demos/batplayer.h" -#include "gob/sound/sound.h" + +#include "gob/pregob/pregob.h" namespace Gob { @@ -118,6 +122,14 @@ void Init::initGame() { return; } + if (_vm->_preGob) { + _vm->_preGob->run(); + delete _palDesc; + _vm->_video->initPrimary(-1); + cleanup(); + return; + } + Common::SeekableReadStream *infFile = _vm->_dataIO->getFile("intro.inf"); if (!infFile) { diff --git a/engines/gob/inter_bargon.cpp b/engines/gob/inter_bargon.cpp index 134203fa9d..029f7c697b 100644 --- a/engines/gob/inter_bargon.cpp +++ b/engines/gob/inter_bargon.cpp @@ -119,7 +119,7 @@ void Inter_Bargon::oBargon_intro2(OpGobParams ¶ms) { MouseButtons buttons; SurfacePtr surface; SoundDesc samples[4]; - int16 comp[5] = { 0, 1, 2, 3, -1 }; + static const int16 comp[5] = { 0, 1, 2, 3, -1 }; static const char *const sndFiles[] = {"1INTROII.snd", "2INTROII.snd", "1INTRO3.snd", "2INTRO3.snd"}; surface = _vm->_video->initSurfDesc(320, 200); @@ -167,8 +167,8 @@ void Inter_Bargon::oBargon_intro3(OpGobParams ¶ms) { MouseButtons buttons; Video::Color *palBak; SoundDesc samples[2]; - int16 comp[3] = { 0, 1, -1 }; byte *palettes[4]; + static const int16 comp[3] = { 0, 1, -1 }; static const char *const sndFiles[] = {"1INTROIV.snd", "2INTROIV.snd"}; static const char *const palFiles[] = {"2ou2.clt", "2ou3.clt", "2ou4.clt", "2ou5.clt"}; diff --git a/engines/gob/inter_geisha.cpp b/engines/gob/inter_geisha.cpp index 8a4d4246b6..8d05cefa66 100644 --- a/engines/gob/inter_geisha.cpp +++ b/engines/gob/inter_geisha.cpp @@ -200,8 +200,12 @@ void Inter_Geisha::oGeisha_checkData(OpFuncParams ¶ms) { if (mode == SaveLoad::kSaveModeNone) { exists = _vm->_dataIO->hasFile(file); - if (!exists) - warning("File \"%s\" not found", file.c_str()); + if (!exists) { + // NOTE: Geisha looks if fin.tot exists to check if it needs to open disk3.stk. + // This is completely normal, so don't print a warning. + if (file != "fin.tot") + warning("File \"%s\" not found", file.c_str()); + } } else if (mode == SaveLoad::kSaveModeSave) exists = _vm->_saveLoad->getSize(file.c_str()) >= 0; diff --git a/engines/gob/inter_v5.cpp b/engines/gob/inter_v5.cpp index c0e8978afd..24905b08d1 100644 --- a/engines/gob/inter_v5.cpp +++ b/engines/gob/inter_v5.cpp @@ -281,7 +281,7 @@ void Inter_v5::o5_getSystemCDSpeed(OpGobParams ¶ms) { Font *font; if ((font = _vm->_draw->loadFont("SPEED.LET"))) { - _vm->_draw->drawString("100 %", 402, 89, 112, 144, 0, *_vm->_draw->_backSurface, *font); + font->drawString("100 %", 402, 89, 112, 144, 0, *_vm->_draw->_backSurface); _vm->_draw->forceBlit(); delete font; @@ -293,7 +293,7 @@ void Inter_v5::o5_getSystemRAM(OpGobParams ¶ms) { Font *font; if ((font = _vm->_draw->loadFont("SPEED.LET"))) { - _vm->_draw->drawString("100 %", 402, 168, 112, 144, 0, *_vm->_draw->_backSurface, *font); + font->drawString("100 %", 402, 168, 112, 144, 0, *_vm->_draw->_backSurface); _vm->_draw->forceBlit(); delete font; @@ -305,7 +305,7 @@ void Inter_v5::o5_getSystemCPUSpeed(OpGobParams ¶ms) { Font *font; if ((font = _vm->_draw->loadFont("SPEED.LET"))) { - _vm->_draw->drawString("100 %", 402, 248, 112, 144, 0, *_vm->_draw->_backSurface, *font); + font->drawString("100 %", 402, 248, 112, 144, 0, *_vm->_draw->_backSurface); _vm->_draw->forceBlit(); delete font; @@ -317,7 +317,7 @@ void Inter_v5::o5_getSystemDrawSpeed(OpGobParams ¶ms) { Font *font; if ((font = _vm->_draw->loadFont("SPEED.LET"))) { - _vm->_draw->drawString("100 %", 402, 326, 112, 144, 0, *_vm->_draw->_backSurface, *font); + font->drawString("100 %", 402, 326, 112, 144, 0, *_vm->_draw->_backSurface); _vm->_draw->forceBlit(); delete font; @@ -329,7 +329,7 @@ void Inter_v5::o5_totalSystemSpecs(OpGobParams ¶ms) { Font *font; if ((font = _vm->_draw->loadFont("SPEED.LET"))) { - _vm->_draw->drawString("100 %", 402, 405, 112, 144, 0, *_vm->_draw->_backSurface, *font); + font->drawString("100 %", 402, 405, 112, 144, 0, *_vm->_draw->_backSurface); _vm->_draw->forceBlit(); delete font; diff --git a/engines/gob/minigames/geisha/penetration.cpp b/engines/gob/minigames/geisha/penetration.cpp index 3be9f1f651..c8c4f2bba7 100644 --- a/engines/gob/minigames/geisha/penetration.cpp +++ b/engines/gob/minigames/geisha/penetration.cpp @@ -505,7 +505,7 @@ bool Penetration::play(bool hasAccessPass, bool hasMaxEnergy, bool testMode) { // Draw, fade in if necessary and wait for the end of the frame _vm->_draw->blitInvalidated(); fadeIn(); - _vm->_util->waitEndFrame(); + _vm->_util->waitEndFrame(false); // Handle the input checkInput(); @@ -778,29 +778,24 @@ void Penetration::drawFloorText() { else if (_floor == 2) floorString = strings[kString1stBasement]; + Surface &surface = *_vm->_draw->_backSurface; + if (floorString) - _vm->_draw->drawString(floorString, 10, 15, kColorFloorText, kColorBlack, 1, - *_vm->_draw->_backSurface, *font); + font->drawString(floorString, 10, 15, kColorFloorText, kColorBlack, 1, surface); if (_exits.size() > 0) { int exitCount = kString2Exits; if (_exits.size() == 1) exitCount = kString1Exit; - _vm->_draw->drawString(strings[kStringYouHave] , 10, 38, kColorExitText, kColorBlack, 1, - *_vm->_draw->_backSurface, *font); - _vm->_draw->drawString(strings[exitCount] , 10, 53, kColorExitText, kColorBlack, 1, - *_vm->_draw->_backSurface, *font); - _vm->_draw->drawString(strings[kStringToReach] , 10, 68, kColorExitText, kColorBlack, 1, - *_vm->_draw->_backSurface, *font); - _vm->_draw->drawString(strings[kStringUpperLevel1], 10, 84, kColorExitText, kColorBlack, 1, - *_vm->_draw->_backSurface, *font); - _vm->_draw->drawString(strings[kStringUpperLevel2], 10, 98, kColorExitText, kColorBlack, 1, - *_vm->_draw->_backSurface, *font); + font->drawString(strings[kStringYouHave] , 10, 38, kColorExitText, kColorBlack, 1, surface); + font->drawString(strings[exitCount] , 10, 53, kColorExitText, kColorBlack, 1, surface); + font->drawString(strings[kStringToReach] , 10, 68, kColorExitText, kColorBlack, 1, surface); + font->drawString(strings[kStringUpperLevel1], 10, 84, kColorExitText, kColorBlack, 1, surface); + font->drawString(strings[kStringUpperLevel2], 10, 98, kColorExitText, kColorBlack, 1, surface); } else - _vm->_draw->drawString(strings[kStringNoExit], 10, 53, kColorExitText, kColorBlack, 1, - *_vm->_draw->_backSurface, *font); + font->drawString(strings[kStringNoExit], 10, 53, kColorExitText, kColorBlack, 1, surface); } void Penetration::drawEndText() { @@ -814,21 +809,17 @@ void Penetration::drawEndText() { if (!font) return; + Surface &surface = *_vm->_draw->_backSurface; + const char **strings = kStrings[getLanguage()]; - _vm->_draw->drawString(strings[kStringLevel0] , 11, 21, kColorExitText, kColorBlack, 1, - *_vm->_draw->_backSurface, *font); - _vm->_draw->drawString(strings[kStringPenetration], 11, 42, kColorExitText, kColorBlack, 1, - *_vm->_draw->_backSurface, *font); - _vm->_draw->drawString(strings[kStringSuccessful] , 11, 58, kColorExitText, kColorBlack, 1, - *_vm->_draw->_backSurface, *font); - - _vm->_draw->drawString(strings[kStringDanger] , 11, 82, kColorFloorText, kColorBlack, 1, - *_vm->_draw->_backSurface, *font); - _vm->_draw->drawString(strings[kStringGynoides] , 11, 98, kColorFloorText, kColorBlack, 1, - *_vm->_draw->_backSurface, *font); - _vm->_draw->drawString(strings[kStringActivated], 11, 113, kColorFloorText, kColorBlack, 1, - *_vm->_draw->_backSurface, *font); + font->drawString(strings[kStringLevel0] , 11, 21, kColorExitText, kColorBlack, 1, surface); + font->drawString(strings[kStringPenetration], 11, 42, kColorExitText, kColorBlack, 1, surface); + font->drawString(strings[kStringSuccessful] , 11, 58, kColorExitText, kColorBlack, 1, surface); + + font->drawString(strings[kStringDanger] , 11, 82, kColorFloorText, kColorBlack, 1, surface); + font->drawString(strings[kStringGynoides] , 11, 98, kColorFloorText, kColorBlack, 1, surface); + font->drawString(strings[kStringActivated], 11, 113, kColorFloorText, kColorBlack, 1, surface); _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, kTextAreaLeft, kTextAreaTop, kTextAreaRight, kTextAreaBigBottom); _vm->_draw->blitInvalidated(); diff --git a/engines/gob/module.mk b/engines/gob/module.mk index 3395046b6c..d5ee6478be 100644 --- a/engines/gob/module.mk +++ b/engines/gob/module.mk @@ -3,6 +3,7 @@ MODULE := engines/gob MODULE_OBJS := \ anifile.o \ aniobject.o \ + backbuffer.o \ cheater.o \ cheater_geisha.o \ cmpfile.o \ @@ -77,6 +78,17 @@ MODULE_OBJS := \ demos/scnplayer.o \ demos/batplayer.o \ detection/detection.o \ + pregob/pregob.o \ + pregob/txtfile.o \ + pregob/gctfile.o \ + pregob/seqfile.o \ + pregob/onceupon/onceupon.o \ + pregob/onceupon/abracadabra.o \ + pregob/onceupon/babayaga.o \ + pregob/onceupon/title.o \ + pregob/onceupon/parents.o \ + pregob/onceupon/stork.o \ + pregob/onceupon/chargenchild.o \ minigames/geisha/evilfish.o \ minigames/geisha/oko.o \ minigames/geisha/meter.o \ diff --git a/engines/gob/pregob/gctfile.cpp b/engines/gob/pregob/gctfile.cpp new file mode 100644 index 0000000000..08c32cda76 --- /dev/null +++ b/engines/gob/pregob/gctfile.cpp @@ -0,0 +1,306 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public 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/random.h" +#include "common/stream.h" + +#include "gob/surface.h" +#include "gob/video.h" + +#include "gob/pregob/gctfile.h" + +namespace Gob { + +GCTFile::Chunk::Chunk() : type(kChunkTypeNone) { +} + + +GCTFile::GCTFile(Common::SeekableReadStream &gct, Common::RandomSource &rnd) : _rnd(&rnd), + _areaLeft(0), _areaTop(0), _areaRight(0), _areaBottom(0), _currentItem(0xFFFF) { + + load(gct); +} + +GCTFile::~GCTFile() { +} + +void GCTFile::load(Common::SeekableReadStream &gct) { + gct.skip(4); // Required buffer size + gct.skip(2); // Unknown + + // Read the selector and line counts for each item + const uint16 itemCount = gct.readUint16LE(); + _items.resize(itemCount); + + for (Items::iterator i = _items.begin(); i != _items.end(); ++i) { + const uint16 selector = gct.readUint16LE(); + const uint16 lineCount = gct.readUint16LE(); + + i->selector = selector; + i->lines.resize(lineCount); + } + + // Read all item lines + for (Items::iterator i = _items.begin(); i != _items.end(); ++i) { + for (Lines::iterator l = i->lines.begin(); l != i->lines.end(); ++l) { + const uint16 lineSize = gct.readUint16LE(); + + readLine(gct, *l, lineSize); + } + } + + if (gct.err()) + error("GCTFile::load(): Failed reading GCT"); +} + +void GCTFile::readLine(Common::SeekableReadStream &gct, Line &line, uint16 lineSize) const { + line.chunks.push_back(Chunk()); + + while (lineSize > 0) { + byte c = gct.readByte(); + lineSize--; + + if (c == 0) { + // Command byte + + if (lineSize == 0) + break; + + byte cmd = gct.readByte(); + lineSize--; + + // Line end command + if (cmd == 0) + break; + + // Item reference command + if (cmd == 1) { + if (lineSize < 2) { + warning("GCTFile::readLine(): Item reference command is missing parameters"); + break; + } + + const uint32 itemRef = gct.readUint16LE(); + lineSize -= 2; + + line.chunks.push_back(Chunk()); + line.chunks.back().type = kChunkTypeItem; + line.chunks.back().item = itemRef; + + line.chunks.push_back(Chunk()); + continue; + } + + warning("GCTFile::readLine(): Invalid command 0x%02X", cmd); + break; + } + + // Text + line.chunks.back().type = kChunkTypeString; + line.chunks.back().text += (char)c; + } + + // Skip bytes we didn't read (because of errors) + gct.skip(lineSize); + + // Remove empty chunks from the end of the list + while (!line.chunks.empty() && (line.chunks.back().type == kChunkTypeNone)) + line.chunks.pop_back(); +} + +uint16 GCTFile::getLineCount(uint item) const { + if (item >= _items.size()) + return 0; + + return _items[item].lines.size(); +} + +void GCTFile::selectLine(uint item, uint16 line) { + if ((item >= _items.size()) && (item != kSelectorAll) && (item != kSelectorRandom)) + return; + + _items[item].selector = line; +} + +void GCTFile::setText(uint item, uint16 line, const Common::String &text) { + if ((item >= _items.size()) || (line >= _items[item].lines.size())) + return; + + _items[item].lines[line].chunks.clear(); + _items[item].lines[line].chunks.push_back(Chunk()); + + _items[item].lines[line].chunks.back().type = kChunkTypeString; + _items[item].lines[line].chunks.back().text = text; +} + +void GCTFile::setText(uint item, const Common::String &text) { + if (item >= _items.size()) + return; + + _items[item].selector = 0; + + _items[item].lines.resize(1); + + setText(item, 0, text); +} + +void GCTFile::reset() { + _currentItem = 0xFFFF; + _currentText.clear(); +} + +Common::String GCTFile::getLineText(const Line &line) const { + Common::String text; + + // Go over all chunks in this line + for (Chunks::const_iterator c = line.chunks.begin(); c != line.chunks.end(); ++c) { + // A chunk is either a direct string, or a reference to another item + + if (c->type == kChunkTypeItem) { + Common::List<Common::String> lines; + + getItemText(c->item, lines); + if (lines.empty()) + continue; + + if (lines.size() > 1) + warning("GCTFile::getLineText(): Referenced item has multiple lines"); + + text += lines.front(); + } else if (c->type == kChunkTypeString) + text += c->text; + } + + return text; +} + +void GCTFile::getItemText(uint item, Common::List<Common::String> &text) const { + text.clear(); + + if ((item >= _items.size()) || _items[item].lines.empty()) + return; + + uint16 line = _items[item].selector; + + // Draw all lines + if (line == kSelectorAll) { + for (Lines::const_iterator l = _items[item].lines.begin(); l != _items[item].lines.end(); ++l) + text.push_back(getLineText(*l)); + + return; + } + + // Draw random line + if (line == kSelectorRandom) + line = _rnd->getRandomNumber(_items[item].lines.size() - 1); + + if (line >= _items[item].lines.size()) + return; + + text.push_back(getLineText(_items[item].lines[line])); +} + +void GCTFile::setArea(int16 left, int16 top, int16 right, int16 bottom) { + trashBuffer(); + + _hasArea = false; + + const int16 width = right - left + 1; + const int16 height = bottom - top + 1; + if ((width <= 0) || (height <= 0)) + return; + + _areaLeft = left; + _areaTop = top; + _areaRight = right; + _areaBottom = bottom; + + _hasArea = true; + + resizeBuffer(width, height); +} + +bool GCTFile::clear(Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom) { + return restoreScreen(dest, left, top, right, bottom); +} + +bool GCTFile::fill(Surface &dest, uint8 color, int16 &left, int16 &top, int16 &right, int16 &bottom) { + left = _areaLeft; + top = _areaTop; + right = _areaRight; + bottom = _areaBottom; + + if (!hasSavedBackground()) + saveScreen(dest, left, top, right, bottom); + + dest.fillRect(left, top, right, bottom, color); + + return true; +} + +bool GCTFile::finished() const { + return (_currentItem != 0xFFFF) && _currentText.empty(); +} + +bool GCTFile::draw(Surface &dest, uint16 item, const Font &font, uint8 color, + int16 &left, int16 &top, int16 &right, int16 &bottom) { + + if ((item >= _items.size()) || !_hasArea) + return false; + + left = _areaLeft; + top = _areaTop; + right = _areaRight; + bottom = _areaBottom; + + const int16 width = right - left + 1; + const int16 height = bottom - top + 1; + + const uint lineCount = height / font.getCharHeight(); + if (lineCount == 0) + return false; + + if (!hasSavedBackground()) + saveScreen(dest, left, top, right, bottom); + + if (item != _currentItem) { + _currentItem = item; + + getItemText(_currentItem, _currentText); + } + + if (_currentText.empty()) + return false; + + int16 y = top; + for (uint i = 0; (i < lineCount) && !_currentText.empty(); i++, y += font.getCharHeight()) { + const Common::String &line = _currentText.front(); + const int16 x = left + ((width - (line.size() * font.getCharWidth())) / 2); + + font.drawString(line, x, y, color, 0, true, dest); + _currentText.pop_front(); + } + + return true; +} + +} // End of namespace Gob diff --git a/engines/gob/pregob/gctfile.h b/engines/gob/pregob/gctfile.h new file mode 100644 index 0000000000..ed6351b7a8 --- /dev/null +++ b/engines/gob/pregob/gctfile.h @@ -0,0 +1,149 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public 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 GOB_PREGOB_GCTFILE_H +#define GOB_PREGOB_GCTFILE_H + +#include "common/str.h" +#include "common/array.h" +#include "common/list.h" + +#include "gob/backbuffer.h" + +namespace Common { + class RandomSource; + class SeekableReadStream; +} + +namespace Gob { + +class Surface; +class Font; + +class GCTFile : public BackBuffer { +public: + static const uint16 kSelectorAll = 0xFFFE; ///< Print all lines. + static const uint16 kSelectorRandom = 0xFFFF; ///< Print a random line. + + + GCTFile(Common::SeekableReadStream &gct, Common::RandomSource &rnd); + ~GCTFile(); + + /** Return the number of lines in an item. */ + uint16 getLineCount(uint item) const; + + /** Set the area the text will be printed in. */ + void setArea(int16 left, int16 top, int16 right, int16 bottom); + + /** Set which line of this item should be printed. */ + void selectLine(uint item, uint16 line); + + /** Change the text of an items' line. */ + void setText(uint item, uint16 line, const Common::String &text); + /** Change the item into one one line and set that line's text. */ + void setText(uint item, const Common::String &text); + + /** Reset the item drawing state. */ + void reset(); + + /** Clear the drawn text, restoring the original content. */ + bool clear(Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom); + + /** Fill the text area with a color. */ + bool fill(Surface &dest, uint8 color, int16 &left, int16 &top, int16 &right, int16 &bottom); + + /** Draw an item onto the surface, until all text has been drawn or the area is filled. */ + bool draw(Surface &dest, uint16 item, const Font &font, uint8 color, + int16 &left, int16 &top, int16 &right, int16 &bottom); + + /** Did we draw all text? */ + bool finished() const; + +private: + /** The type of a chunk. */ + enum ChunkType { + kChunkTypeNone = 0, ///< Do nothing. + kChunkTypeString , ///< A direct string. + kChunkTypeItem ///< A reference to an item to print instead. + }; + + /** A chunk in an item text line. */ + struct Chunk { + ChunkType type; ///< The type of the chunk. + + Common::String text; ///< Text to print. + + int item; ///< Item to print instead. + + Chunk(); + }; + + typedef Common::List<Chunk> Chunks; + + /** A line in an item. */ + struct Line { + Chunks chunks; ///< The chunks that make up the line. + }; + + typedef Common::Array<Line> Lines; + + /** A GCT item. */ + struct Item { + Lines lines; ///< The text lines in the item + uint16 selector; ///< Which line to print. + }; + + typedef Common::Array<Item> Items; + + + Common::RandomSource *_rnd; + + Items _items; ///< All GCT items. + + // The area on which to print + bool _hasArea; + int16 _areaLeft; + int16 _areaTop; + int16 _areaRight; + int16 _areaBottom; + + /** Index of the current item we're drawing. */ + uint16 _currentItem; + /** Text left to draw. */ + Common::List<Common::String> _currentText; + + + // -- Loading helpers -- + + void load(Common::SeekableReadStream &gct); + void readLine(Common::SeekableReadStream &gct, Line &line, uint16 lineSize) const; + + + // -- Draw helpers -- + + Common::String getLineText(const Line &line) const; + void getItemText(uint item, Common::List<Common::String> &text) const; +}; + +} // End of namespace Gob + +#endif // GOB_PREGOB_GCTFILE_H diff --git a/engines/gob/pregob/onceupon/abracadabra.cpp b/engines/gob/pregob/onceupon/abracadabra.cpp new file mode 100644 index 0000000000..2cf6855ef8 --- /dev/null +++ b/engines/gob/pregob/onceupon/abracadabra.cpp @@ -0,0 +1,137 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public 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/textconsole.h" + +#include "gob/gob.h" + +#include "gob/pregob/onceupon/abracadabra.h" + +static const uint8 kCopyProtectionColors[7] = { + 14, 11, 13, 1, 7, 12, 2 +}; + +static const uint8 kCopyProtectionShapes[7 * 20] = { + 3, 4, 3, 0, 1, 2, 0, 2, 2, 0, 2, 4, 0, 3, 4, 1, 1, 4, 1, 3, + 0, 2, 0, 4, 2, 4, 4, 2, 3, 0, 1, 1, 1, 1, 3, 0, 4, 2, 3, 4, + 0, 0, 1, 2, 1, 1, 2, 4, 3, 1, 4, 2, 4, 4, 2, 4, 1, 2, 3, 3, + 1, 0, 2, 3, 4, 2, 3, 2, 2, 0, 0, 0, 4, 2, 3, 4, 4, 0, 4, 1, + 4, 2, 1, 1, 1, 1, 4, 3, 4, 2, 3, 0, 0, 3, 0, 2, 3, 0, 2, 4, + 4, 2, 4, 3, 0, 4, 0, 2, 3, 1, 4, 1, 3, 1, 0, 0, 2, 1, 3, 2, + 3, 1, 0, 3, 1, 3, 4, 2, 4, 4, 3, 2, 0, 2, 0, 1, 2, 0, 1, 4 +}; + +static const uint8 kCopyProtectionObfuscate[4] = { + 1, 0, 2, 3 +}; + +namespace Gob { + +namespace OnceUpon { + +const OnceUpon::MenuButton Abracadabra::kAnimalsButtons = { + true, 131, 127, 183, 164, 193, 0, 243, 35, 132, 128, 0 +}; + +const OnceUpon::MenuButton Abracadabra::kAnimalButtons[] = { + {false, 37, 89, 95, 127, 37, 89, 95, 127, 131, 25, 0}, + {false, 114, 65, 172, 111, 114, 65, 172, 111, 131, 25, 1}, + {false, 186, 72, 227, 96, 186, 72, 227, 96, 139, 25, 2}, + {false, 249, 87, 282, 112, 249, 87, 282, 112, 143, 25, 3}, + {false, 180, 102, 234, 138, 180, 102, 234, 138, 133, 25, 4}, + {false, 197, 145, 242, 173, 197, 145, 242, 173, 137, 25, 5}, + {false, 113, 151, 171, 176, 113, 151, 171, 176, 131, 25, 6}, + {false, 114, 122, 151, 150, 114, 122, 151, 150, 141, 25, 7}, + {false, 36, 136, 94, 176, 36, 136, 94, 176, 131, 25, 8}, + {false, 243, 123, 295, 155, 243, 123, 295, 155, 136, 25, 9} +}; + +const char *Abracadabra::kAnimalNames[] = { + "loup", + "drag", + "arai", + "crap", + "crab", + "mous", + "saut", + "guep", + "rhin", + "scor" +}; + +// The houses where the stork can drop a bundle +const OnceUpon::MenuButton Abracadabra::kStorkHouses[] = { + {false, 16, 80, 87, 125, 0, 0, 0, 0, 0, 0, 0}, // Castle , Lord & Lady + {false, 61, 123, 96, 149, 0, 0, 0, 0, 0, 0, 1}, // Cottage, Farmers + {false, 199, 118, 226, 137, 0, 0, 0, 0, 0, 0, 2}, // Hut , Woodcutters + {false, 229, 91, 304, 188, 0, 0, 0, 0, 0, 0, 3} // Palace , King & Queen +}; + +// The stork bundle drop parameters +const Stork::BundleDrop Abracadabra::kStorkBundleDrops[] = { + { 14, 65, 127, true }, + { 14, 76, 152, true }, + { 14, 204, 137, true }, + { 11, 275, 179, false } +}; + +// Parameters for the stork section. +const OnceUpon::StorkParam Abracadabra::kStorkParam = { + "present.cmp", ARRAYSIZE(kStorkHouses), kStorkHouses, kStorkBundleDrops +}; + + +Abracadabra::Abracadabra(GobEngine *vm) : OnceUpon(vm) { +} + +Abracadabra::~Abracadabra() { +} + +void Abracadabra::run() { + init(); + + // Copy protection + bool correctCP = doCopyProtection(kCopyProtectionColors, kCopyProtectionShapes, kCopyProtectionObfuscate); + if (_vm->shouldQuit() || !correctCP) + return; + + // Show the intro + showIntro(); + if (_vm->shouldQuit()) + return; + + // Handle the start menu + doStartMenu(&kAnimalsButtons, ARRAYSIZE(kAnimalButtons), kAnimalButtons, kAnimalNames); + if (_vm->shouldQuit()) + return; + + // Play the actual game + playGame(); +} + +const OnceUpon::StorkParam &Abracadabra::getStorkParameters() const { + return kStorkParam; +} + +} // End of namespace OnceUpon + +} // End of namespace Gob diff --git a/engines/gob/pregob/onceupon/abracadabra.h b/engines/gob/pregob/onceupon/abracadabra.h new file mode 100644 index 0000000000..8048213f5f --- /dev/null +++ b/engines/gob/pregob/onceupon/abracadabra.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 GOB_PREGOB_ONCEUPON_ABRACADABRA_H +#define GOB_PREGOB_ONCEUPON_ABRACADABRA_H + +#include "gob/pregob/onceupon/onceupon.h" + +namespace Gob { + +namespace OnceUpon { + +class Abracadabra : public OnceUpon { +public: + Abracadabra(GobEngine *vm); + ~Abracadabra(); + + void run(); + +protected: + const StorkParam &getStorkParameters() const; + +private: + /** Definition of the menu button that leads to the animal names screen. */ + static const MenuButton kAnimalsButtons; + + /** Definition of the buttons that make up the animals in the animal names screen. */ + static const MenuButton kAnimalButtons[]; + /** File prefixes for the name of each animal. */ + static const char *kAnimalNames[]; + + // Parameters for the stork section. + static const MenuButton kStorkHouses[]; + static const Stork::BundleDrop kStorkBundleDrops[]; + static const struct StorkParam kStorkParam; +}; + +} // End of namespace OnceUpon + +} // End of namespace Gob + +#endif // GOB_PREGOB_ONCEUPON_ABRACADABRA_H diff --git a/engines/gob/pregob/onceupon/babayaga.cpp b/engines/gob/pregob/onceupon/babayaga.cpp new file mode 100644 index 0000000000..ef56b9dd0b --- /dev/null +++ b/engines/gob/pregob/onceupon/babayaga.cpp @@ -0,0 +1,137 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public 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/textconsole.h" + +#include "gob/gob.h" + +#include "gob/pregob/onceupon/babayaga.h" + +static const uint8 kCopyProtectionColors[7] = { + 14, 11, 13, 1, 7, 12, 2 +}; + +static const uint8 kCopyProtectionShapes[7 * 20] = { + 0, 0, 1, 2, 1, 1, 2, 4, 3, 1, 4, 2, 4, 4, 2, 4, 1, 2, 3, 3, + 3, 1, 0, 3, 1, 3, 4, 2, 4, 4, 3, 2, 0, 2, 0, 1, 2, 0, 1, 4, + 1, 0, 2, 3, 4, 2, 3, 2, 2, 0, 0, 0, 4, 2, 3, 4, 4, 0, 4, 1, + 0, 2, 0, 4, 2, 4, 4, 2, 3, 0, 1, 1, 1, 1, 3, 0, 4, 2, 3, 4, + 3, 4, 3, 0, 1, 2, 0, 2, 2, 0, 2, 4, 0, 3, 4, 1, 1, 4, 1, 3, + 4, 2, 1, 1, 1, 1, 4, 3, 4, 2, 3, 0, 0, 3, 0, 2, 3, 0, 2, 4, + 4, 2, 4, 3, 0, 4, 0, 2, 3, 1, 4, 1, 3, 1, 0, 0, 2, 1, 3, 2 +}; + +static const uint8 kCopyProtectionObfuscate[4] = { + 0, 1, 2, 3 +}; + +namespace Gob { + +namespace OnceUpon { + +const OnceUpon::MenuButton BabaYaga::kAnimalsButtons = { + true, 131, 127, 183, 164, 193, 0, 245, 37, 131, 127, 0 +}; + +const OnceUpon::MenuButton BabaYaga::kAnimalButtons[] = { + {false, 34, 84, 92, 127, 34, 84, 92, 127, 131, 25, 0}, + {false, 114, 65, 172, 111, 114, 65, 172, 111, 131, 25, 1}, + {false, 186, 72, 227, 96, 186, 72, 227, 96, 139, 25, 2}, + {false, 249, 87, 282, 112, 249, 87, 282, 112, 143, 25, 3}, + {false, 180, 97, 234, 138, 180, 97, 234, 138, 133, 25, 4}, + {false, 197, 145, 242, 173, 197, 145, 242, 173, 137, 25, 5}, + {false, 113, 156, 171, 176, 113, 156, 171, 176, 131, 25, 6}, + {false, 114, 127, 151, 150, 114, 127, 151, 150, 141, 25, 7}, + {false, 36, 136, 94, 176, 36, 136, 94, 176, 131, 25, 8}, + {false, 245, 123, 293, 155, 245, 123, 293, 155, 136, 25, 9} +}; + +const char *BabaYaga::kAnimalNames[] = { + "vaut", + "drag", + "arai", + "gren", + "fauc", + "abei", + "serp", + "tort", + "sang", + "rena" +}; + +// The houses where the stork can drop a bundle +const OnceUpon::MenuButton BabaYaga::kStorkHouses[] = { + {false, 16, 80, 87, 125, 0, 0, 0, 0, 0, 0, 0}, // Castle , Lord & Lady + {false, 61, 123, 96, 149, 0, 0, 0, 0, 0, 0, 1}, // Cottage, Farmers + {false, 199, 118, 226, 137, 0, 0, 0, 0, 0, 0, 2}, // Hut , Woodcutters + {false, 229, 91, 304, 188, 0, 0, 0, 0, 0, 0, 3} // Palace , King & Queen +}; + +// The stork bundle drop parameters +const Stork::BundleDrop BabaYaga::kStorkBundleDrops[] = { + { 14, 35, 129, true }, + { 14, 70, 148, true }, + { 14, 206, 136, true }, + { 11, 260, 225, false } +}; + +// Parameters for the stork section. +const OnceUpon::StorkParam BabaYaga::kStorkParam = { + "present2.cmp", ARRAYSIZE(kStorkHouses), kStorkHouses, kStorkBundleDrops +}; + + +BabaYaga::BabaYaga(GobEngine *vm) : OnceUpon(vm) { +} + +BabaYaga::~BabaYaga() { +} + +void BabaYaga::run() { + init(); + + // Copy protection + bool correctCP = doCopyProtection(kCopyProtectionColors, kCopyProtectionShapes, kCopyProtectionObfuscate); + if (_vm->shouldQuit() || !correctCP) + return; + + // Show the intro + showIntro(); + if (_vm->shouldQuit()) + return; + + // Handle the start menu + doStartMenu(&kAnimalsButtons, ARRAYSIZE(kAnimalButtons), kAnimalButtons, kAnimalNames); + if (_vm->shouldQuit()) + return; + + // Play the actual game + playGame(); +} + +const OnceUpon::StorkParam &BabaYaga::getStorkParameters() const { + return kStorkParam; +} + +} // End of namespace OnceUpon + +} // End of namespace Gob diff --git a/engines/gob/pregob/onceupon/babayaga.h b/engines/gob/pregob/onceupon/babayaga.h new file mode 100644 index 0000000000..0241f78f4e --- /dev/null +++ b/engines/gob/pregob/onceupon/babayaga.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 GOB_PREGOB_ONCEUPON_BABAYAGA_H +#define GOB_PREGOB_ONCEUPON_BABAYAGA_H + +#include "gob/pregob/onceupon/onceupon.h" + +namespace Gob { + +namespace OnceUpon { + +class BabaYaga : public OnceUpon { +public: + BabaYaga(GobEngine *vm); + ~BabaYaga(); + + void run(); + +protected: + const StorkParam &getStorkParameters() const; + +private: + /** Definition of the menu button that leads to the animal names screen. */ + static const MenuButton kAnimalsButtons; + + /** Definition of the buttons that make up the animals in the animal names screen. */ + static const MenuButton kAnimalButtons[]; + /** File prefixes for the name of each animal. */ + static const char *kAnimalNames[]; + + // Parameters for the stork section. + static const MenuButton kStorkHouses[]; + static const Stork::BundleDrop kStorkBundleDrops[]; + static const struct StorkParam kStorkParam; +}; + +} // End of namespace OnceUpon + +} // End of namespace Gob + +#endif // GOB_PREGOB_ONCEUPON_BABAYAGA_H diff --git a/engines/gob/pregob/onceupon/brokenstrings.h b/engines/gob/pregob/onceupon/brokenstrings.h new file mode 100644 index 0000000000..89acb1c6bd --- /dev/null +++ b/engines/gob/pregob/onceupon/brokenstrings.h @@ -0,0 +1,60 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GOB_PREGOB_ONCEUPON_BROKENSTRINGS_H +#define GOB_PREGOB_ONCEUPON_BROKENSTRINGS_H + +struct BrokenString { + const char *wrong; + const char *correct; +}; + +struct BrokenStringLanguage { + const BrokenString *strings; + uint count; +}; + +static const BrokenString kBrokenStringsGerman[] = { + { "Zeichungen von Kaki," , "Zeichnungen von Kaki," }, + { "die es in seine Wachtr\204ume", "die es in seine Tagtr\204ume" }, + { " Spielerfahrung" , " Spielerfahren" }, + { " Fortgeschrittene" , " Fortgeschritten" }, + { "die Vespe" , "die Wespe" }, + { "das Rhinoceros" , "das Rhinozeros" }, + { "die Heusschrecke" , "die Heuschrecke" }, + { "Das, von Drachen gebrachte" , "Das vom Drachen gebrachte" }, + { "Am Waldesrand es sieht" , "Am Waldesrand sieht es" }, + { " das Kind den Palast." , "das Kind den Palast." }, + { "Am Waldessaum sieht" , "Am Waldesrand sieht" }, + { "tipp auf ESC!" , "dr\201cke ESC!" }, + { "Wohin fliegt der Drachen?" , "Wohin fliegt der Drache?" } +}; + +static const BrokenStringLanguage kBrokenStrings[kLanguageCount] = { + { 0, 0 }, // French + { kBrokenStringsGerman, ARRAYSIZE(kBrokenStringsGerman) }, // German + { 0, 0 }, // English + { 0, 0 }, // Spanish + { 0, 0 }, // Italian +}; + +#endif // GOB_PREGOB_ONCEUPON_BROKENSTRINGS_H diff --git a/engines/gob/pregob/onceupon/chargenchild.cpp b/engines/gob/pregob/onceupon/chargenchild.cpp new file mode 100644 index 0000000000..ba099e4937 --- /dev/null +++ b/engines/gob/pregob/onceupon/chargenchild.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 "gob/surface.h" +#include "gob/anifile.h" + +#include "gob/pregob/onceupon/chargenchild.h" + +enum Animation { + kAnimWalkLeft = 0, + kAnimWalkRight = 1, + kAnimJumpLeft = 2, + kAnimJumpRight = 3, + kAnimTapFoot = 14 +}; + +namespace Gob { + +namespace OnceUpon { + +CharGenChild::CharGenChild(const ANIFile &ani) : ANIObject(ani) { + setPosition(265, 110); + setAnimation(kAnimWalkLeft); + setVisible(true); + setPause(false); +} + +CharGenChild::~CharGenChild() { +} + +void CharGenChild::advance() { + bool wasLastFrame = lastFrame(); + + ANIObject::advance(); + + int16 x, y, left, top, width, height; + getPosition(x, y); + getFramePosition(left, top); + getFrameSize(width, height); + + const int16 right = left + width - 1; + + switch (getAnimation()) { + case kAnimWalkLeft: + if (left <= 147) + setAnimation(kAnimWalkRight); + break; + + case kAnimWalkRight: + if (right >= 290) { + setAnimation(kAnimJumpLeft); + + setPosition(x, y - 14); + } + break; + + case kAnimJumpLeft: + if (wasLastFrame) { + setAnimation(kAnimTapFoot); + + setPosition(x, y - 10); + } + break; + + case kAnimTapFoot: + if (wasLastFrame) { + setAnimation(kAnimJumpRight); + + setPosition(x, y + 10); + } + break; + + case kAnimJumpRight: + if (wasLastFrame) { + setAnimation(kAnimWalkLeft); + + setPosition(x, y + 14); + } + break; + } +} + +CharGenChild::Sound CharGenChild::shouldPlaySound() const { + const uint16 anim = getAnimation(); + const uint16 frame = getFrame(); + + if (((anim == kAnimWalkLeft) || (anim == kAnimWalkRight)) && ((frame == 1) || (frame == 6))) + return kSoundWalk; + + if (((anim == kAnimJumpLeft) || (anim == kAnimJumpRight)) && (frame == 0)) + return kSoundJump; + + return kSoundNone; +} + +} // End of namespace OnceUpon + +} // End of namespace Gob diff --git a/engines/gob/pregob/onceupon/chargenchild.h b/engines/gob/pregob/onceupon/chargenchild.h new file mode 100644 index 0000000000..3b09ef112a --- /dev/null +++ b/engines/gob/pregob/onceupon/chargenchild.h @@ -0,0 +1,60 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GOB_PREGOB_ONCEUPON_CHARGENCHILD_H +#define GOB_PREGOB_ONCEUPON_CHARGENCHILD_H + +#include "common/system.h" + +#include "gob/aniobject.h" + +namespace Gob { + +class Surface; +class ANIFile; + +namespace OnceUpon { + +/** The child running around on the character generator screen. */ +class CharGenChild : public ANIObject { +public: + enum Sound { + kSoundNone = 0, + kSoundWalk , + kSoundJump + }; + + CharGenChild(const ANIFile &ani); + ~CharGenChild(); + + /** Advance the animation to the next frame. */ + void advance(); + + /** Should we play a sound right now? */ + Sound shouldPlaySound() const; +}; + +} // End of namespace OnceUpon + +} // End of namespace Gob + +#endif // GOB_PREGOB_ONCEUPON_CHARGENCHILD_H diff --git a/engines/gob/pregob/onceupon/onceupon.cpp b/engines/gob/pregob/onceupon/onceupon.cpp new file mode 100644 index 0000000000..e4c2df34c0 --- /dev/null +++ b/engines/gob/pregob/onceupon/onceupon.cpp @@ -0,0 +1,1904 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public 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 "gob/gob.h" +#include "gob/global.h" +#include "gob/util.h" +#include "gob/dataio.h" +#include "gob/surface.h" +#include "gob/draw.h" +#include "gob/video.h" +#include "gob/anifile.h" +#include "gob/aniobject.h" + +#include "gob/sound/sound.h" + +#include "gob/pregob/txtfile.h" +#include "gob/pregob/gctfile.h" + +#include "gob/pregob/onceupon/onceupon.h" +#include "gob/pregob/onceupon/palettes.h" +#include "gob/pregob/onceupon/title.h" +#include "gob/pregob/onceupon/parents.h" +#include "gob/pregob/onceupon/chargenchild.h" + +static const uint kLanguageCount = 5; + +static const uint kCopyProtectionHelpStringCount = 3; + +static const char *kCopyProtectionHelpStrings[Gob::OnceUpon::OnceUpon::kLanguageCount][kCopyProtectionHelpStringCount] = { + { // French + "Consulte le livret des animaux, rep\212re la", + "page correspondant \205 la couleur de l\'\202cran", + "et clique le symbole associ\202 \205 l\'animal affich\202.", + }, + { // German + "Suche im Tieralbum die Seite, die der Farbe auf", + "dem Bildschirm entspricht und klicke auf das", + "Tiersymbol.", + }, + { // English + "Consult the book of animals, find the page", + "corresponding to the colour of screen and click", + "the symbol associated with the animal displayed.", + }, + { // Spanish + "Consulta el libro de los animales, localiza la ", + "p\240gina que corresponde al color de la pantalla.", + "Cliquea el s\241mbolo asociado al animal que aparece.", + }, + { // Italian + "Guarda il libretto degli animali, trova la", + "pagina che corrisponde al colore dello schermo,", + "clicca il simbolo associato all\'animale presentato", + } +}; + +static const char *kCopyProtectionWrongStrings[Gob::OnceUpon::OnceUpon::kLanguageCount] = { + "Tu t\'es tromp\202, dommage...", // French + "Schade, du hast dich geirrt." , // German + "You are wrong, what a pity!" , // English + "Te equivocas, l\240stima..." , // Spanish + "Sei Sbagliato, peccato..." // Italian +}; + +static const uint kCopyProtectionShapeCount = 5; + +static const int16 kCopyProtectionShapeCoords[kCopyProtectionShapeCount][6] = { + { 0, 51, 26, 75, 60, 154}, + { 28, 51, 58, 81, 96, 151}, + { 60, 51, 94, 79, 136, 152}, + { 96, 51, 136, 71, 180, 155}, + {140, 51, 170, 77, 228, 153} +}; + +enum ClownAnimation { + kClownAnimationClownCheer = 0, + kClownAnimationClownStand = 1, + kClownAnimationClownCry = 6 +}; + +// 12 seconds delay for one area full of GCT text +static const uint32 kGCTDelay = 12000; + +namespace Gob { + +namespace OnceUpon { + +const OnceUpon::MenuButton OnceUpon::kMainMenuDifficultyButton[] = { + {false, 29, 18, 77, 57, 0, 0, 0, 0, 0, 0, (int)kDifficultyBeginner}, + {false, 133, 18, 181, 57, 0, 0, 0, 0, 0, 0, (int)kDifficultyIntermediate}, + {false, 241, 18, 289, 57, 0, 0, 0, 0, 0, 0, (int)kDifficultyAdvanced}, +}; + +const OnceUpon::MenuButton OnceUpon::kSectionButtons[] = { + {false, 27, 121, 91, 179, 0, 0, 0, 0, 0, 0, 0}, + { true, 95, 121, 159, 179, 4, 1, 56, 49, 100, 126, 2}, + { true, 163, 121, 227, 179, 64, 1, 120, 49, 168, 126, 6}, + { true, 231, 121, 295, 179, 128, 1, 184, 49, 236, 126, 10} +}; + +const OnceUpon::MenuButton OnceUpon::kIngameButtons[] = { + {true, 108, 83, 139, 116, 0, 0, 31, 34, 108, 83, 0}, + {true, 144, 83, 175, 116, 36, 0, 67, 34, 144, 83, 1}, + {true, 180, 83, 211, 116, 72, 0, 103, 34, 180, 83, 2} +}; + +const OnceUpon::MenuButton OnceUpon::kAnimalNamesBack = { + true, 19, 13, 50, 46, 36, 0, 67, 34, 19, 13, 1 +}; + +const OnceUpon::MenuButton OnceUpon::kLanguageButtons[] = { + {true, 43, 80, 93, 115, 0, 55, 50, 90, 43, 80, 0}, + {true, 132, 80, 182, 115, 53, 55, 103, 90, 132, 80, 1}, + {true, 234, 80, 284, 115, 106, 55, 156, 90, 234, 80, 2}, + {true, 43, 138, 93, 173, 159, 55, 209, 90, 43, 138, 3}, + {true, 132, 138, 182, 173, 212, 55, 262, 90, 132, 138, 4}, + {true, 234, 138, 284, 173, 265, 55, 315, 90, 234, 138, 2} +}; + +const char *OnceUpon::kSound[kSoundCount] = { + "diamant.snd", // kSoundClick + "cigogne.snd", // kSoundStork + "saute.snd" // kSoundJump +}; + +const OnceUpon::SectionFunc OnceUpon::kSectionFuncs[kSectionCount] = { + &OnceUpon::sectionStork, + &OnceUpon::sectionChapter1, + &OnceUpon::sectionParents, + &OnceUpon::sectionChapter2, + &OnceUpon::sectionForest0, + &OnceUpon::sectionChapter3, + &OnceUpon::sectionEvilCastle, + &OnceUpon::sectionChapter4, + &OnceUpon::sectionForest1, + &OnceUpon::sectionChapter5, + &OnceUpon::sectionBossFight, + &OnceUpon::sectionChapter6, + &OnceUpon::sectionForest2, + &OnceUpon::sectionChapter7, + &OnceUpon::sectionEnd +}; + + +OnceUpon::ScreenBackup::ScreenBackup() : palette(-1), changedCursor(false), cursorVisible(false) { + screen = new Surface(320, 200, 1); +} + +OnceUpon::ScreenBackup::~ScreenBackup() { + delete screen; +} + + +OnceUpon::OnceUpon(GobEngine *vm) : PreGob(vm), _openedArchives(false), + _jeudak(0), _lettre(0), _plettre(0), _glettre(0) { + +} + +OnceUpon::~OnceUpon() { + deinit(); +} + +void OnceUpon::init() { + deinit(); + + // Open data files + + bool hasSTK1 = _vm->_dataIO->openArchive("stk1.stk", true); + bool hasSTK2 = _vm->_dataIO->openArchive("stk2.stk", true); + bool hasSTK3 = _vm->_dataIO->openArchive("stk3.stk", true); + + if (!hasSTK1 || !hasSTK2 || !hasSTK3) + error("OnceUpon::OnceUpon(): Failed to open archives"); + + _openedArchives = true; + + // Open fonts + + _jeudak = _vm->_draw->loadFont("jeudak.let"); + _lettre = _vm->_draw->loadFont("lettre.let"); + _plettre = _vm->_draw->loadFont("plettre.let"); + _glettre = _vm->_draw->loadFont("glettre.let"); + + if (!_jeudak || !_lettre || !_plettre || !_glettre) + error("OnceUpon::OnceUpon(): Failed to fonts (%d, %d, %d, %d)", + _jeudak != 0, _lettre != 0, _plettre != 0, _glettre != 0); + + // Verify the language + + if (_vm->_global->_language == kLanguageAmerican) + _vm->_global->_language = kLanguageBritish; + + if ((_vm->_global->_language >= kLanguageCount)) + error("We do not support the language \"%s\".\n" + "If you are certain that your game copy includes this language,\n" + "please contact the ScummVM team with details about this version.\n" + "Thanks", _vm->getLangDesc(_vm->_global->_language)); + + // Load all our sounds and init the screen + + loadSounds(kSound, kSoundCount); + initScreen(); + + // We start with an invalid palette + _palette = -1; + + // No quit requested at start + _quit = false; + + // We start with no selected difficulty and at section 0 + _difficulty = kDifficultyCount; + _section = 0; + + // Default name + _name = "Nemo"; + + // Default character properties + _house = 0; + _head = 0; + _colorHair = 0; + _colorJacket = 0; + _colorTrousers = 0; +} + +void OnceUpon::deinit() { + // Free sounds + freeSounds(); + + // Free fonts + + delete _jeudak; + delete _lettre; + delete _plettre; + delete _glettre; + + _jeudak = 0; + _lettre = 0; + _plettre = 0; + _glettre = 0; + + // Close archives + + if (_openedArchives) { + _vm->_dataIO->closeArchive(true); + _vm->_dataIO->closeArchive(true); + _vm->_dataIO->closeArchive(true); + } + + _openedArchives = false; +} + +void OnceUpon::setGamePalette(uint palette) { + if (palette >= kPaletteCount) + return; + + _palette = palette; + + setPalette(kGamePalettes[palette], kPaletteSize); +} + +void OnceUpon::setGameCursor() { + Surface cursor(320, 16, 1); + + // Set the default game cursor + _vm->_video->drawPackedSprite("icon.cmp", cursor); + setCursor(cursor, 105, 0, 120, 15, 0, 0); +} + +void OnceUpon::drawLineByLine(const Surface &src, int16 left, int16 top, int16 right, int16 bottom, + int16 x, int16 y) const { + + // A special way of drawing something: + // Draw every other line "downwards", wait a bit after each line + // Then, draw the remaining lines "upwards" and again wait a bit after each line. + + if (_vm->shouldQuit()) + return; + + const int16 width = right - left + 1; + const int16 height = bottom - top + 1; + + if ((width <= 0) || (height <= 0)) + return; + + // Draw the even lines downwards + for (int16 i = 0; i < height; i += 2) { + if (_vm->shouldQuit()) + return; + + _vm->_draw->_backSurface->blit(src, left, top + i, right, top + i, x, y + i); + + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, x, y + i, x + width - 1, y + 1); + _vm->_draw->blitInvalidated(); + + _vm->_util->longDelay(1); + } + + // Draw the odd lines upwards + for (int16 i = (height & 1) ? height : (height - 1); i >= 0; i -= 2) { + if (_vm->shouldQuit()) + return; + + _vm->_draw->_backSurface->blit(src, left, top + i, right, top + i, x, y + i); + + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, x, y + i, x + width - 1, y + 1); + _vm->_draw->blitInvalidated(); + + _vm->_util->longDelay(1); + } +} + +void OnceUpon::backupScreen(ScreenBackup &backup, bool setDefaultCursor) { + // Backup the screen and palette + backup.screen->blit(*_vm->_draw->_backSurface); + backup.palette = _palette; + + // Backup the cursor + + backup.cursorVisible = isCursorVisible(); + + backup.changedCursor = false; + if (setDefaultCursor) { + backup.changedCursor = true; + + addCursor(); + setGameCursor(); + } +} + +void OnceUpon::restoreScreen(ScreenBackup &backup) { + if (_vm->shouldQuit()) + return; + + // Restore the screen + _vm->_draw->_backSurface->blit(*backup.screen); + _vm->_draw->forceBlit(); + + // Restore the palette + if (backup.palette >= 0) + setGamePalette(backup.palette); + + // Restore the cursor + + if (!backup.cursorVisible) + hideCursor(); + + if (backup.changedCursor) + removeCursor(); + + backup.changedCursor = false; +} + +void OnceUpon::fixTXTStrings(TXTFile &txt) const { + TXTFile::LineArray &lines = txt.getLines(); + for (uint i = 0; i < lines.size(); i++) + lines[i].text = fixString(lines[i].text); +} + +#include "gob/pregob/onceupon/brokenstrings.h" +Common::String OnceUpon::fixString(const Common::String &str) const { + const BrokenStringLanguage &broken = kBrokenStrings[_vm->_global->_language]; + + for (uint i = 0; i < broken.count; i++) { + if (str == broken.strings[i].wrong) + return broken.strings[i].correct; + } + + return str; +} + +enum ClownAnimation { + kClownAnimationStand = 0, + kClownAnimationCheer = 1, + kClownAnimationCry = 2 +}; + +const PreGob::AnimProperties OnceUpon::kClownAnimations[] = { + { 1, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, + { 0, 0, ANIObject::kModeOnce , true, false, false, 0, 0}, + { 6, 0, ANIObject::kModeOnce , true, false, false, 0, 0} +}; + +enum CopyProtectionState { + kCPStateSetup, // Set up the screen + kCPStateWaitUser, // Waiting for the user to pick a shape + kCPStateWaitClown, // Waiting for the clown animation to finish + kCPStateFinish // Finishing +}; + +bool OnceUpon::doCopyProtection(const uint8 colors[7], const uint8 shapes[7 * 20], const uint8 obfuscate[4]) { + fadeOut(); + setPalette(kCopyProtectionPalette, kPaletteSize); + + // Load the copy protection sprites + Surface sprites[2] = {Surface(320, 200, 1), Surface(320, 200, 1)}; + + _vm->_video->drawPackedSprite("grille1.cmp", sprites[0]); + _vm->_video->drawPackedSprite("grille2.cmp", sprites[1]); + + // Load the clown animation + ANIFile ani (_vm, "grille.ani", 320); + ANIList anims; + + loadAnims(anims, ani, 1, &kClownAnimations[kClownAnimationStand]); + + // Set the copy protection cursor + setCursor(sprites[1], 5, 110, 20, 134, 3, 0); + + // We start with 2 tries left, not having a correct answer and the copy protection not set up yet + CopyProtectionState state = kCPStateSetup; + + uint8 triesLeft = 2; + int8 animalShape = -1; + bool hasCorrect = false; + + while (!_vm->shouldQuit() && (state != kCPStateFinish)) { + clearAnim(anims); + + // Set up the screen + if (state == kCPStateSetup) { + animalShape = cpSetup(colors, shapes, obfuscate, sprites); + + setAnim(*anims[0], kClownAnimations[kClownAnimationStand]); + state = kCPStateWaitUser; + } + + drawAnim(anims); + + // If we're waiting for the clown and he finished, evaluate if we're finished + if (!anims[0]->isVisible() && (state == kCPStateWaitClown)) + state = (hasCorrect || (--triesLeft == 0)) ? kCPStateFinish : kCPStateSetup; + + showCursor(); + fadeIn(); + + endFrame(true); + + int16 mouseX, mouseY; + MouseButtons mouseButtons; + + checkInput(mouseX, mouseY, mouseButtons); + + if (state == kCPStateWaitUser) { + // Look if we clicked a shaped and got it right + + int8 guessedShape = -1; + if (mouseButtons == kMouseButtonsLeft) + guessedShape = cpFindShape(mouseX, mouseY); + + if (guessedShape >= 0) { + hasCorrect = guessedShape == animalShape; + animalShape = -1; + + setAnim(*anims[0], kClownAnimations[hasCorrect ? kClownAnimationCheer : kClownAnimationCry]); + state = kCPStateWaitClown; + } + } + } + + freeAnims(anims); + + fadeOut(); + hideCursor(); + clearScreen(); + + // Display the "You are wrong" screen + if (!hasCorrect) + cpWrong(); + + return hasCorrect; +} + +int8 OnceUpon::cpSetup(const uint8 colors[7], const uint8 shapes[7 * 20], const uint8 obfuscate[4], + const Surface sprites[2]) { + + fadeOut(); + hideCursor(); + + // Get a random animal and animal color + int8 animalColor = _vm->_util->getRandom(7); + while ((colors[animalColor] == 1) || (colors[animalColor] == 7) || (colors[animalColor] == 11)) + animalColor = _vm->_util->getRandom(7); + + int8 animal = _vm->_util->getRandom(20); + + int8 animalShape = shapes[animalColor * 20 + animal]; + if (animal < 4) + animal = obfuscate[animal]; + + // Get the position of the animal sprite + int16 animalLeft = (animal % 4) * 80; + int16 animalTop = (animal / 4) * 50; + + uint8 sprite = 0; + if (animalTop >= 200) { + animalTop -= 200; + sprite = 1; + } + + int16 animalRight = animalLeft + 80 - 1; + int16 animalBottom = animalTop + 50 - 1; + + // Fill with the animal color + _vm->_draw->_backSurface->fill(colors[animalColor]); + + // Print the help line strings + for (uint i = 0; i < kCopyProtectionHelpStringCount; i++) { + const char * const helpString = kCopyProtectionHelpStrings[_vm->_global->_language][i]; + + const int x = 160 - (strlen(helpString) * _plettre->getCharWidth()) / 2; + const int y = i * 10 + 5; + + _plettre->drawString(helpString, x, y, 8, 0, true, *_vm->_draw->_backSurface); + } + + // White rectangle with black border + _vm->_draw->_backSurface->fillRect( 93, 43, 226, 134, 15); + _vm->_draw->_backSurface->drawRect( 92, 42, 227, 135, 0); + + // Draw the animal in the animal color + _vm->_draw->_backSurface->fillRect(120, 63, 199, 112, colors[animalColor]); + _vm->_draw->_backSurface->blit(sprites[sprite], animalLeft, animalTop, animalRight, animalBottom, 120, 63, 0); + + // Draw the shapes + for (uint i = 0; i < kCopyProtectionShapeCount; i++) { + const int16 * const coords = kCopyProtectionShapeCoords[i]; + + _vm->_draw->_backSurface->blit(sprites[1], coords[0], coords[1], coords[2], coords[3], coords[4], coords[5], 0); + } + + _vm->_draw->forceBlit(); + + return animalShape; +} + +int8 OnceUpon::cpFindShape(int16 x, int16 y) const { + // Look through all shapes and check if the coordinates are inside one of them + for (uint i = 0; i < kCopyProtectionShapeCount; i++) { + const int16 * const coords = kCopyProtectionShapeCoords[i]; + + const int16 left = coords[4]; + const int16 top = coords[5]; + const int16 right = coords[4] + (coords[2] - coords[0] + 1) - 1; + const int16 bottom = coords[5] + (coords[3] - coords[1] + 1) - 1; + + if ((x >= left) && (x <= right) && (y >= top) && (y <= bottom)) + return i; + } + + return -1; +} + +void OnceUpon::cpWrong() { + // Display the "You are wrong" string, centered + + const char * const wrongString = kCopyProtectionWrongStrings[_vm->_global->_language]; + const int wrongX = 160 - (strlen(wrongString) * _plettre->getCharWidth()) / 2; + + _vm->_draw->_backSurface->clear(); + _plettre->drawString(wrongString, wrongX, 100, 15, 0, true, *_vm->_draw->_backSurface); + + _vm->_draw->forceBlit(); + + fadeIn(); + + waitInput(); + + fadeOut(); + clearScreen(); +} + +void OnceUpon::showIntro() { + // Show all intro parts + + // "Loading" + showWait(10); + if (_vm->shouldQuit()) + return; + + // Quote about fairy tales + showQuote(); + if (_vm->shouldQuit()) + return; + + // Once Upon A Time title + showTitle(); + if (_vm->shouldQuit()) + return; + + // Game title screen + showChapter(0); + if (_vm->shouldQuit()) + return; + + // "Loading" + showWait(17); +} + +void OnceUpon::showWait(uint palette) { + // Show the loading floppy + + fadeOut(); + clearScreen(); + setGamePalette(palette); + + Surface wait(320, 43, 1); + + _vm->_video->drawPackedSprite("wait.cmp", wait); + _vm->_draw->_backSurface->blit(wait, 0, 0, 72, 33, 122, 84); + + _vm->_draw->forceBlit(); + + fadeIn(); +} + +void OnceUpon::showQuote() { + // Show the quote about fairytales + + fadeOut(); + clearScreen(); + setGamePalette(11); + + static const Font *fonts[3] = { _plettre, _glettre, _plettre }; + + TXTFile *quote = loadTXT(getLocFile("gene.tx"), TXTFile::kFormatStringPositionColorFont); + quote->draw(*_vm->_draw->_backSurface, fonts, ARRAYSIZE(fonts)); + delete quote; + + _vm->_draw->forceBlit(); + + fadeIn(); + + waitInput(); + + fadeOut(); +} + +const PreGob::AnimProperties OnceUpon::kTitleAnimation = { + 8, 0, ANIObject::kModeContinuous, true, false, false, 0, 0 +}; + +void OnceUpon::showTitle() { + fadeOut(); + setGamePalette(10); + + Title title(_vm); + title.play(); +} + +void OnceUpon::showChapter(int chapter) { + // Display the intro text to a chapter + + fadeOut(); + clearScreen(); + setGamePalette(11); + + // Parchment background + _vm->_video->drawPackedSprite("parch.cmp", *_vm->_draw->_backSurface); + + static const Font *fonts[3] = { _plettre, _glettre, _plettre }; + + const Common::String chapterFile = getLocFile(Common::String::format("gene%d.tx", chapter)); + + TXTFile *gameTitle = loadTXT(chapterFile, TXTFile::kFormatStringPositionColorFont); + gameTitle->draw(*_vm->_draw->_backSurface, fonts, ARRAYSIZE(fonts)); + delete gameTitle; + + _vm->_draw->forceBlit(); + + fadeIn(); + + waitInput(); + + fadeOut(); +} + +void OnceUpon::showByeBye() { + fadeOut(); + hideCursor(); + clearScreen(); + setGamePalette(1); + + _plettre->drawString("Bye Bye....", 140, 80, 2, 0, true, *_vm->_draw->_backSurface); + _vm->_draw->forceBlit(); + + fadeIn(); + + _vm->_util->longDelay(1000); + + fadeOut(); +} + +void OnceUpon::doStartMenu(const MenuButton *animalsButton, uint animalCount, + const MenuButton *animalButtons, const char * const *animalNames) { + clearScreen(); + + // Wait until we clicked on of the difficulty buttons and are ready to start playing + while (!_vm->shouldQuit()) { + MenuAction action = handleStartMenu(animalsButton); + if (action == kMenuActionPlay) + break; + + // If we pressed the "listen to animal names" button, handle that screen + if (action == kMenuActionAnimals) + handleAnimalNames(animalCount, animalButtons, animalNames); + } +} + +OnceUpon::MenuAction OnceUpon::handleStartMenu(const MenuButton *animalsButton) { + ScreenBackup screenBackup; + backupScreen(screenBackup, true); + + fadeOut(); + setGamePalette(17); + drawStartMenu(animalsButton); + showCursor(); + fadeIn(); + + MenuAction action = kMenuActionNone; + while (!_vm->shouldQuit() && (action == kMenuActionNone)) { + endFrame(true); + + // Check user input + + int16 mouseX, mouseY; + MouseButtons mouseButtons; + + int16 key = checkInput(mouseX, mouseY, mouseButtons); + if (key == kKeyEscape) + // ESC -> Quit + return kMenuActionQuit; + + if (mouseButtons != kMouseButtonsLeft) + continue; + + playSound(kSoundClick); + + // If we clicked on a difficulty button, show the selected difficulty and start the game + int diff = checkButton(kMainMenuDifficultyButton, ARRAYSIZE(kMainMenuDifficultyButton), mouseX, mouseY); + if (diff >= 0) { + _difficulty = (Difficulty)diff; + action = kMenuActionPlay; + + drawStartMenu(animalsButton); + _vm->_util->longDelay(1000); + } + + if (animalsButton && (checkButton(animalsButton, 1, mouseX, mouseY) != -1)) + action = kMenuActionAnimals; + + } + + fadeOut(); + restoreScreen(screenBackup); + + return action; +} + +OnceUpon::MenuAction OnceUpon::handleMainMenu() { + ScreenBackup screenBackup; + backupScreen(screenBackup, true); + + fadeOut(); + setGamePalette(17); + drawMainMenu(); + showCursor(); + fadeIn(); + + MenuAction action = kMenuActionNone; + while (!_vm->shouldQuit() && (action == kMenuActionNone)) { + endFrame(true); + + // Check user input + + int16 mouseX, mouseY; + MouseButtons mouseButtons; + + int16 key = checkInput(mouseX, mouseY, mouseButtons); + if (key == kKeyEscape) + // ESC -> Quit + return kMenuActionQuit; + + if (mouseButtons != kMouseButtonsLeft) + continue; + + playSound(kSoundClick); + + // If we clicked on a difficulty button, change the current difficulty level + int diff = checkButton(kMainMenuDifficultyButton, ARRAYSIZE(kMainMenuDifficultyButton), mouseX, mouseY); + if ((diff >= 0) && (diff != (int)_difficulty)) { + _difficulty = (Difficulty)diff; + + drawMainMenu(); + } + + // If we clicked on a section button, restart the game from this section + int section = checkButton(kSectionButtons, ARRAYSIZE(kSectionButtons), mouseX, mouseY); + if ((section >= 0) && (section <= _section)) { + _section = section; + action = kMenuActionRestart; + } + + } + + fadeOut(); + restoreScreen(screenBackup); + + return action; +} + +OnceUpon::MenuAction OnceUpon::handleIngameMenu() { + ScreenBackup screenBackup; + backupScreen(screenBackup, true); + + drawIngameMenu(); + showCursor(); + + MenuAction action = kMenuActionNone; + while (!_vm->shouldQuit() && (action == kMenuActionNone)) { + endFrame(true); + + // Check user input + + int16 mouseX, mouseY; + MouseButtons mouseButtons; + + int16 key = checkInput(mouseX, mouseY, mouseButtons); + if ((key == kKeyEscape) || (mouseButtons == kMouseButtonsRight)) + // ESC or right mouse button -> Dismiss the menu + action = kMenuActionPlay; + + if (mouseButtons != kMouseButtonsLeft) + continue; + + int button = checkButton(kIngameButtons, ARRAYSIZE(kIngameButtons), mouseX, mouseY); + if (button == 0) + action = kMenuActionQuit; + else if (button == 1) + action = kMenuActionMainMenu; + else if (button == 2) + action = kMenuActionPlay; + + } + + clearIngameMenu(*screenBackup.screen); + restoreScreen(screenBackup); + + return action; +} + +void OnceUpon::drawStartMenu(const MenuButton *animalsButton) { + // Draw the background + _vm->_video->drawPackedSprite("menu2.cmp", *_vm->_draw->_backSurface); + + // Draw the "Listen to animal names" button + if (animalsButton) { + Surface elements(320, 38, 1); + _vm->_video->drawPackedSprite("elemenu.cmp", elements); + _vm->_draw->_backSurface->fillRect(animalsButton->left , animalsButton->top, + animalsButton->right, animalsButton->bottom, 0); + drawButton(*_vm->_draw->_backSurface, elements, *animalsButton); + } + + // Highlight the current difficulty + drawMenuDifficulty(); + + _vm->_draw->forceBlit(); +} + +void OnceUpon::drawMainMenu() { + // Draw the background + _vm->_video->drawPackedSprite("menu.cmp", *_vm->_draw->_backSurface); + + // Highlight the current difficulty + drawMenuDifficulty(); + + // Draw the section buttons + Surface elements(320, 200, 1); + _vm->_video->drawPackedSprite("elemenu.cmp", elements); + + for (uint i = 0; i < ARRAYSIZE(kSectionButtons); i++) { + const MenuButton &button = kSectionButtons[i]; + + if (!button.needDraw) + continue; + + if (_section >= (int)button.id) + drawButton(*_vm->_draw->_backSurface, elements, button); + } + + _vm->_draw->forceBlit(); +} + +void OnceUpon::drawIngameMenu() { + Surface menu(320, 34, 1); + _vm->_video->drawPackedSprite("icon.cmp", menu); + + // Draw the menu in a special way, button by button + for (uint i = 0; i < ARRAYSIZE(kIngameButtons); i++) { + const MenuButton &button = kIngameButtons[i]; + + drawLineByLine(menu, button.srcLeft, button.srcTop, button.srcRight, button.srcBottom, + button.dstX, button.dstY); + } + + _vm->_draw->forceBlit(); + _vm->_video->retrace(); +} + +void OnceUpon::drawMenuDifficulty() { + if (_difficulty == kDifficultyCount) + return; + + TXTFile *difficulties = loadTXT(getLocFile("diffic.tx"), TXTFile::kFormatStringPositionColor); + + // Draw the difficulty name + difficulties->draw((uint) _difficulty, *_vm->_draw->_backSurface, &_plettre, 1); + + // Draw a border around the current difficulty + drawButtonBorder(kMainMenuDifficultyButton[_difficulty], difficulties->getLines()[_difficulty].color); + + delete difficulties; +} + +void OnceUpon::clearIngameMenu(const Surface &background) { + if (_vm->shouldQuit()) + return; + + // Find the area encompassing the whole ingame menu + + int16 left = 0x7FFF; + int16 top = 0x7FFF; + int16 right = 0x0000; + int16 bottom = 0x0000; + + for (uint i = 0; i < ARRAYSIZE(kIngameButtons); i++) { + const MenuButton &button = kIngameButtons[i]; + + if (!button.needDraw) + continue; + + left = MIN<int16>(left , button.dstX); + top = MIN<int16>(top , button.dstY); + right = MAX<int16>(right , button.dstX + (button.srcRight - button.srcLeft + 1) - 1); + bottom = MAX<int16>(bottom, button.dstY + (button.srcBottom - button.srcTop + 1) - 1); + } + + if ((left > right) || (top > bottom)) + return; + + // Clear it line by line + drawLineByLine(background, left, top, right, bottom, left, top); +} + +OnceUpon::MenuAction OnceUpon::doIngameMenu() { + // Show the ingame menu + MenuAction action = handleIngameMenu(); + + if ((action == kMenuActionQuit) || _vm->shouldQuit()) { + + // User pressed the quit button, or quit ScummVM + _quit = true; + action = kMenuActionQuit; + + } else if (action == kMenuActionPlay) { + + // User pressed the return to game button + action = kMenuActionPlay; + + } else if (kMenuActionMainMenu) { + + // User pressed the return to main menu button + action = handleMainMenu(); + } + + return action; +} + +OnceUpon::MenuAction OnceUpon::doIngameMenu(int16 &key, MouseButtons &mouseButtons) { + if ((key != kKeyEscape) && (mouseButtons != kMouseButtonsRight)) + return kMenuActionNone; + + key = 0; + mouseButtons = kMouseButtonsNone; + + MenuAction action = doIngameMenu(); + if (action == kMenuActionPlay) + action = kMenuActionNone; + + return action; +} + +int OnceUpon::checkButton(const MenuButton *buttons, uint count, int16 x, int16 y, int failValue) const { + // Look through all buttons, and return the ID of the button we're in + + for (uint i = 0; i < count; i++) { + const MenuButton &button = buttons[i]; + + if ((x >= button.left) && (x <= button.right) && (y >= button.top) && (y <= button.bottom)) + return (int)button.id; + } + + // We're in none of these buttons, return the fail value + return failValue; +} + +void OnceUpon::drawButton(Surface &dest, const Surface &src, const MenuButton &button, int transp) const { + dest.blit(src, button.srcLeft, button.srcTop, button.srcRight, button.srcBottom, button.dstX, button.dstY, transp); +} + +void OnceUpon::drawButtons(Surface &dest, const Surface &src, const MenuButton *buttons, uint count, int transp) const { + for (uint i = 0; i < count; i++) { + const MenuButton &button = buttons[i]; + + if (!button.needDraw) + continue; + + drawButton(dest, src, button, transp); + } +} + +void OnceUpon::drawButtonBorder(const MenuButton &button, uint8 color) { + _vm->_draw->_backSurface->drawRect(button.left, button.top, button.right, button.bottom, color); + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, button.left, button.top, button.right, button.bottom); +} + +enum AnimalNamesState { + kANStateChoose, // We're in the animal chooser + kANStateNames, // We're in the language chooser + kANStateFinish // We're finished +}; + +void OnceUpon::handleAnimalNames(uint count, const MenuButton *buttons, const char * const *names) { + fadeOut(); + clearScreen(); + setGamePalette(19); + + bool cursorVisible = isCursorVisible(); + + // Set the cursor + addCursor(); + setGameCursor(); + + anSetupChooser(); + + int8 _animal = -1; + + AnimalNamesState state = kANStateChoose; + while (!_vm->shouldQuit() && (state != kANStateFinish)) { + showCursor(); + fadeIn(); + + endFrame(true); + + // Check user input + + int16 mouseX, mouseY; + MouseButtons mouseButtons; + + checkInput(mouseX, mouseY, mouseButtons); + + // If we moused over an animal button, draw a border around it + int animal = checkButton(buttons, count, mouseX, mouseY); + if ((state == kANStateChoose) && (animal != _animal)) { + // Erase the old border + if (_animal >= 0) + drawButtonBorder(buttons[_animal], 15); + + _animal = animal; + + // Draw the new border + if (_animal >= 0) + drawButtonBorder(buttons[_animal], 10); + } + + if (mouseButtons != kMouseButtonsLeft) + continue; + + playSound(kSoundClick); + + // We clicked on a language button, play the animal name + int language = checkButton(kLanguageButtons, ARRAYSIZE(kLanguageButtons), mouseX, mouseY); + if ((state == kANStateNames) && (language >= 0)) + anPlayAnimalName(names[_animal], language); + + // We clicked on an animal + if ((state == kANStateChoose) && (_animal >= 0)) { + anSetupNames(buttons[_animal]); + + state = kANStateNames; + } + + // If we clicked on the back button, go back + if (checkButton(&kAnimalNamesBack, 1, mouseX, mouseY) != -1) { + if (state == kANStateNames) { + anSetupChooser(); + + state = kANStateChoose; + } else if (state == kANStateChoose) + state = kANStateFinish; + } + } + + fadeOut(); + + // Restore the cursor + if (!cursorVisible) + hideCursor(); + removeCursor(); +} + +void OnceUpon::anSetupChooser() { + fadeOut(); + + _vm->_video->drawPackedSprite("dico.cmp", *_vm->_draw->_backSurface); + + // Draw the back button + Surface menu(320, 34, 1); + _vm->_video->drawPackedSprite("icon.cmp", menu); + drawButton(*_vm->_draw->_backSurface, menu, kAnimalNamesBack); + + // "Choose an animal" + TXTFile *choose = loadTXT(getLocFile("choisi.tx"), TXTFile::kFormatStringPosition); + choose->draw(*_vm->_draw->_backSurface, &_plettre, 1, 14); + delete choose; + + _vm->_draw->forceBlit(); +} + +void OnceUpon::anSetupNames(const MenuButton &animal) { + fadeOut(); + + Surface background(320, 200, 1); + + _vm->_video->drawPackedSprite("dico.cmp", background); + + // Draw the background and clear what we don't need + _vm->_draw->_backSurface->blit(background); + _vm->_draw->_backSurface->fillRect(19, 19, 302, 186, 15); + + // Draw the back button + Surface menu(320, 34, 1); + _vm->_video->drawPackedSprite("icon.cmp", menu); + drawButton(*_vm->_draw->_backSurface, menu, kAnimalNamesBack); + + // Draw the animal + drawButton(*_vm->_draw->_backSurface, background, animal); + + // Draw the language buttons + Surface elements(320, 200, 1); + _vm->_video->drawPackedSprite("elemenu.cmp", elements); + drawButtons(*_vm->_draw->_backSurface, elements, kLanguageButtons, ARRAYSIZE(kLanguageButtons)); + + // Draw the language names + _plettre->drawString("Fran\207ais", 43, 70, 10, 15, true, *_vm->_draw->_backSurface); + _plettre->drawString("Deutsch" , 136, 70, 10, 15, true, *_vm->_draw->_backSurface); + _plettre->drawString("English" , 238, 70, 10, 15, true, *_vm->_draw->_backSurface); + _plettre->drawString("Italiano" , 43, 128, 10, 15, true, *_vm->_draw->_backSurface); + _plettre->drawString("Espa\244ol" , 136, 128, 10, 15, true, *_vm->_draw->_backSurface); + _plettre->drawString("English" , 238, 128, 10, 15, true, *_vm->_draw->_backSurface); + + _vm->_draw->forceBlit(); +} + +void OnceUpon::anPlayAnimalName(const Common::String &animal, uint language) { + // Sound file to play + Common::String soundFile = animal + "_" + kLanguageSuffixLong[language] + ".snd"; + + // Get the name of the animal + TXTFile *names = loadTXT(animal + ".anm", TXTFile::kFormatString); + Common::String name = names->getLines()[language].text; + delete names; + + // It should be centered on the screen + const int nameX = 160 - (name.size() * _plettre->getCharWidth()) / 2; + + // Backup the screen surface + Surface backup(162, 23, 1); + backup.blit(*_vm->_draw->_backSurface, 78, 123, 239, 145, 0, 0); + + // Draw the name border + Surface nameBorder(162, 23, 1); + _vm->_video->drawPackedSprite("mot.cmp", nameBorder); + _vm->_draw->_backSurface->blit(nameBorder, 0, 0, 161, 22, 78, 123); + + // Print the animal name + _plettre->drawString(name, nameX, 129, 10, 0, true, *_vm->_draw->_backSurface); + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, 78, 123, 239, 145); + + playSoundFile(soundFile); + + // Restore the screen + _vm->_draw->_backSurface->blit(backup, 0, 0, 161, 22, 78, 123); + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, 78, 123, 239, 145); +} + +void OnceUpon::playGame() { + while (!_vm->shouldQuit() && !_quit) { + // Play a section and advance to the next section if we finished it + if (playSection()) + _section = MIN(_section + 1, kSectionCount - 1); + } + + // If we quit through the game and not through ScummVM, show the "Bye Bye" screen + if (!_vm->shouldQuit()) + showByeBye(); +} + +bool OnceUpon::playSection() { + if ((_section < 0) || (_section >= ARRAYSIZE(kSectionFuncs))) { + _quit = true; + return false; + } + + return (this->*kSectionFuncs[_section])(); +} + +const PreGob::AnimProperties OnceUpon::kSectionStorkAnimations[] = { + { 0, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, + { 1, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, + { 2, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, + { 3, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, + { 4, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, + { 5, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, + { 6, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, + { 7, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, + { 8, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, + {17, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, + {16, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, + {15, 0, ANIObject::kModeContinuous, true, false, false, 0, 0} +}; + +enum StorkState { + kStorkStateWaitUser, + kStorkStateWaitBundle, + kStorkStateFinish +}; + +bool OnceUpon::sectionStork() { + fadeOut(); + hideCursor(); + setGamePalette(0); + setGameCursor(); + + const StorkParam ¶m = getStorkParameters(); + + Surface backdrop(320, 200, 1); + + // Draw the frame + _vm->_video->drawPackedSprite("cadre.cmp", *_vm->_draw->_backSurface); + + // Draw the backdrop + _vm->_video->drawPackedSprite(param.backdrop, backdrop); + _vm->_draw->_backSurface->blit(backdrop, 0, 0, 288, 175, 16, 12); + + // "Where does the stork go?" + TXTFile *whereStork = loadTXT(getLocFile("ouva.tx"), TXTFile::kFormatStringPositionColor); + whereStork->draw(*_vm->_draw->_backSurface, &_plettre, 1); + + // Where the stork actually goes + GCTFile *thereStork = loadGCT(getLocFile("choix.gc")); + thereStork->setArea(17, 18, 303, 41); + + ANIFile ani(_vm, "present.ani", 320); + ANIList anims; + + Stork *stork = new Stork(_vm, ani); + + loadAnims(anims, ani, ARRAYSIZE(kSectionStorkAnimations), kSectionStorkAnimations); + anims.push_back(stork); + + drawAnim(anims); + + _vm->_draw->forceBlit(); + + int8 storkSoundWait = 0; + + StorkState state = kStorkStateWaitUser; + MenuAction action = kMenuActionNone; + while (!_vm->shouldQuit() && (state != kStorkStateFinish)) { + // Play the stork sound + if (--storkSoundWait == 0) + playSound(kSoundStork); + if (storkSoundWait <= 0) + storkSoundWait = 50 - _vm->_util->getRandom(30); + + // Check if the bundle landed + if ((state == kStorkStateWaitBundle) && stork->hasBundleLanded()) + state = kStorkStateFinish; + + // Check user input + + int16 mouseX, mouseY; + MouseButtons mouseButtons; + + int16 key = checkInput(mouseX, mouseY, mouseButtons); + + action = doIngameMenu(key, mouseButtons); + if (action != kMenuActionNone) { + state = kStorkStateFinish; + break; + } + + clearAnim(anims); + + if (mouseButtons == kMouseButtonsLeft) { + stopSound(); + playSound(kSoundClick); + + int house = checkButton(param.houses, param.houseCount, mouseX, mouseY); + if ((state == kStorkStateWaitUser) && (house >= 0)) { + + _house = house; + + stork->dropBundle(param.drops[house]); + state = kStorkStateWaitBundle; + + // Remove the "Where does the stork go?" text + int16 left, top, right, bottom; + if (whereStork->clear(*_vm->_draw->_backSurface, left, top, right, bottom)) + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom); + + // Print the text where the stork actually goes + thereStork->selectLine(3, house); // The house + thereStork->selectLine(4, house); // The house's inhabitants + if (thereStork->draw(*_vm->_draw->_backSurface, 2, *_plettre, 10, left, top, right, bottom)) + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom); + } + } + + drawAnim(anims); + showCursor(); + fadeIn(); + + endFrame(true); + } + + freeAnims(anims); + delete thereStork; + delete whereStork; + + fadeOut(); + hideCursor(); + + // Didn't complete the section + if (action != kMenuActionNone) + return false; + + // Move on to the character generator + + CharGenAction charGenAction = kCharGenRestart; + while (charGenAction == kCharGenRestart) + charGenAction = characterGenerator(); + + // Did we successfully create a character? + return charGenAction == kCharGenDone; +} + +const OnceUpon::MenuButton OnceUpon::kCharGenHeadButtons[] = { + {true, 106, 146, 152, 180, 0, 0, 47, 34, 106, 146, 0}, + {true, 155, 146, 201, 180, 49, 0, 96, 34, 155, 146, 1}, + {true, 204, 146, 250, 180, 98, 0, 145, 34, 204, 146, 2}, + {true, 253, 146, 299, 180, 147, 0, 194, 34, 253, 146, 3} +}; + +const OnceUpon::MenuButton OnceUpon::kCharGenHeads[] = { + {true, 0, 0, 0, 0, 29, 4, 68, 31, 40, 51, 0}, + {true, 0, 0, 0, 0, 83, 4, 113, 31, 45, 51, 1}, + {true, 0, 0, 0, 0, 132, 4, 162, 31, 45, 51, 2}, + {true, 0, 0, 0, 0, 182, 4, 211, 31, 45, 51, 3} +}; + +const OnceUpon::MenuButton OnceUpon::kCharGenHairButtons[] = { + {true, 105, 55, 124, 70, 271, 1, 289, 15, 105, 55, 0x04}, + {true, 105, 74, 124, 89, 271, 20, 289, 34, 105, 74, 0x07} +}; + +const OnceUpon::MenuButton OnceUpon::kCharGenJacketButtons[] = { + {true, 105, 90, 124, 105, 271, 39, 289, 53, 105, 90, 0x06}, + {true, 105, 109, 124, 124, 271, 58, 289, 72, 105, 109, 0x02} +}; + +const OnceUpon::MenuButton OnceUpon::kCharGenTrousersButtons[] = { + {true, 105, 140, 124, 155, 271, 77, 289, 91, 105, 140, 0x01}, + {true, 105, 159, 124, 174, 271, 96, 289, 110, 105, 159, 0x03} +}; + +const OnceUpon::MenuButton OnceUpon::kCharGenNameEntry[] = { + {true, 0, 0, 0, 0, 0, 38, 54, 48, 140, 145, 0}, + {true, 0, 0, 0, 0, 106, 38, 159, 48, 195, 145, 0}, + {true, 0, 0, 0, 0, 0, 105, 54, 121, 140, 156, 0}, + {true, 0, 0, 0, 0, 106, 105, 159, 121, 195, 156, 0} +}; + +enum CharGenState { + kCharGenStateHead = 0, // Choose a head + kCharGenStateHair , // Choose hair color + kCharGenStateJacket , // Choose jacket color + kCharGenStateTrousers , // Choose trousers color + kCharGenStateName , // Choose name + kCharGenStateSure , // "Are you sure?" + kCharGenStateStoryName , // "We're going to tell the story of $NAME" + kCharGenStateFinish // Finished +}; + +void OnceUpon::charGenSetup(uint stage) { + Surface choix(320, 200, 1), elchoix(320, 200, 1), paperDoll(65, 137, 1); + + _vm->_video->drawPackedSprite("choix.cmp" , choix); + _vm->_video->drawPackedSprite("elchoix.cmp", elchoix); + + paperDoll.blit(choix, 200, 0, 264, 136, 0, 0); + + GCTFile *text = loadGCT(getLocFile("choix.gc")); + text->setArea(17, 18, 303, 41); + text->setText(9, _name); + + // Background + _vm->_video->drawPackedSprite("cadre.cmp", *_vm->_draw->_backSurface); + _vm->_draw->_backSurface->fillRect(16, 50, 303, 187, 5); + + // Character sprite frame + _vm->_draw->_backSurface->blit(choix, 0, 38, 159, 121, 140, 54); + + // Recolor the paper doll parts + if (_colorHair != 0xFF) + elchoix.recolor(0x0C, _colorHair); + + if (_colorJacket != 0xFF) + paperDoll.recolor(0x0A, _colorJacket); + + if (_colorTrousers != 0xFF) + paperDoll.recolor(0x09, _colorTrousers); + + _vm->_draw->_backSurface->blit(paperDoll, 32, 51); + + // Paper doll head + if (_head != 0xFF) + drawButton(*_vm->_draw->_backSurface, elchoix, kCharGenHeads[_head], 0); + + if (stage == kCharGenStateHead) { + // Head buttons + drawButtons(*_vm->_draw->_backSurface, choix, kCharGenHeadButtons, ARRAYSIZE(kCharGenHeadButtons)); + + // "Choose a head" + int16 left, top, right, bottom; + text->draw(*_vm->_draw->_backSurface, 5, *_plettre, 10, left, top, right, bottom); + + } else if (stage == kCharGenStateHair) { + // Hair color buttons + drawButtons(*_vm->_draw->_backSurface, choix, kCharGenHairButtons, ARRAYSIZE(kCharGenHairButtons)); + + // "What color is the hair?" + int16 left, top, right, bottom; + text->draw(*_vm->_draw->_backSurface, 6, *_plettre, 10, left, top, right, bottom); + + } else if (stage == kCharGenStateJacket) { + // Jacket color buttons + drawButtons(*_vm->_draw->_backSurface, choix, kCharGenJacketButtons, ARRAYSIZE(kCharGenJacketButtons)); + + // "What color is the jacket?" + int16 left, top, right, bottom; + text->draw(*_vm->_draw->_backSurface, 7, *_plettre, 10, left, top, right, bottom); + + } else if (stage == kCharGenStateTrousers) { + // Trousers color buttons + drawButtons(*_vm->_draw->_backSurface, choix, kCharGenTrousersButtons, ARRAYSIZE(kCharGenTrousersButtons)); + + // "What color are the trousers?" + int16 left, top, right, bottom; + text->draw(*_vm->_draw->_backSurface, 8, *_plettre, 10, left, top, right, bottom); + + } else if (stage == kCharGenStateName) { + // Name entry field + drawButtons(*_vm->_draw->_backSurface, choix, kCharGenNameEntry, ARRAYSIZE(kCharGenNameEntry)); + + // "Enter name" + int16 left, top, right, bottom; + text->draw(*_vm->_draw->_backSurface, 10, *_plettre, 10, left, top, right, bottom); + + charGenDrawName(); + } else if (stage == kCharGenStateSure) { + // Name entry field + drawButtons(*_vm->_draw->_backSurface, choix, kCharGenNameEntry, ARRAYSIZE(kCharGenNameEntry)); + + // "Are you sure?" + TXTFile *sure = loadTXT(getLocFile("estu.tx"), TXTFile::kFormatStringPositionColor); + sure->draw(*_vm->_draw->_backSurface, &_plettre, 1); + delete sure; + + charGenDrawName(); + } else if (stage == kCharGenStateStoryName) { + + // "We're going to tell the story of $NAME" + int16 left, top, right, bottom; + text->draw(*_vm->_draw->_backSurface, 11, *_plettre, 10, left, top, right, bottom); + } + + delete text; +} + +bool OnceUpon::enterString(Common::String &name, int16 key, uint maxLength, const Font &font) { + if (key == 0) + return true; + + if (key == kKeyBackspace) { + name.deleteLastChar(); + return true; + } + + if (key == kKeySpace) + key = ' '; + + if ((key >= ' ') && (key <= 0xFF)) { + if (name.size() >= maxLength) + return false; + + if (!font.hasChar(key)) + return false; + + name += (char) key; + return true; + } + + return false; +} + +void OnceUpon::charGenDrawName() { + _vm->_draw->_backSurface->fillRect(147, 151, 243, 166, 1); + + const int16 nameY = 151 + ((166 - 151 + 1 - _plettre->getCharHeight()) / 2); + const int16 nameX = 147 + ((243 - 147 + 1 - (15 * _plettre->getCharWidth ())) / 2); + + _plettre->drawString(_name, nameX, nameY, 10, 0, true, *_vm->_draw->_backSurface); + + const int16 cursorLeft = nameX + _name.size() * _plettre->getCharWidth(); + const int16 cursorTop = nameY; + const int16 cursorRight = cursorLeft + _plettre->getCharWidth() - 1; + const int16 cursorBottom = cursorTop + _plettre->getCharHeight() - 1; + + _vm->_draw->_backSurface->fillRect(cursorLeft, cursorTop, cursorRight, cursorBottom, 10); + + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, 147, 151, 243, 166); +} + +OnceUpon::CharGenAction OnceUpon::characterGenerator() { + fadeOut(); + hideCursor(); + setGameCursor(); + + showWait(1); + + _name.clear(); + + _head = 0xFF; + _colorHair = 0xFF; + _colorJacket = 0xFF; + _colorTrousers = 0xFF; + + CharGenState state = kCharGenStateHead; + charGenSetup(state); + + ANIFile ani(_vm, "ba.ani", 320); + + ani.recolor(0x0F, 0x0C); + ani.recolor(0x0E, 0x0A); + ani.recolor(0x08, 0x09); + + CharGenChild *child = new CharGenChild(ani); + + ANIList anims; + anims.push_back(child); + + fadeOut(); + _vm->_draw->forceBlit(); + + CharGenAction action = kCharGenRestart; + while (!_vm->shouldQuit() && (state != kCharGenStateFinish)) { + // Check user input + + int16 mouseX, mouseY; + MouseButtons mouseButtons; + + int16 key = checkInput(mouseX, mouseY, mouseButtons); + + MenuAction menuAction = doIngameMenu(key, mouseButtons); + if (menuAction != kMenuActionNone) { + state = kCharGenStateFinish; + action = kCharGenAbort; + break; + } + + clearAnim(anims); + + if (state == kCharGenStateStoryName) { + if ((mouseButtons != kMouseButtonsNone) || (key != 0)) { + state = kCharGenStateFinish; + action = kCharGenDone; + break; + } + } + + if (state == kCharGenStateSure) { + // Not sure => restart + if ((key == 'N') || (key == 'n')) { // No / Nein / Non + state = kCharGenStateFinish; + action = kCharGenRestart; + break; + } + + if ((key == 'Y') || (key == 'y') || // Yes + (key == 'J') || (key == 'j') || // Ja + (key == 'S') || (key == 's') || // Si + (key == 'O') || (key == 'o')) { // Oui + + state = kCharGenStateStoryName; + charGenSetup(state); + _vm->_draw->forceBlit(); + } + } + + if (state == kCharGenStateName) { + if (enterString(_name, key, 14, *_plettre)) { + _vm->_draw->_backSurface->fillRect(147, 151, 243, 166, 1); + + const int16 nameY = 151 + ((166 - 151 + 1 - _plettre->getCharHeight()) / 2); + const int16 nameX = 147 + ((243 - 147 + 1 - (15 * _plettre->getCharWidth ())) / 2); + + _plettre->drawString(_name, nameX, nameY, 10, 0, true, *_vm->_draw->_backSurface); + + const int16 cursorLeft = nameX + _name.size() * _plettre->getCharWidth(); + const int16 cursorTop = nameY; + const int16 cursorRight = cursorLeft + _plettre->getCharWidth() - 1; + const int16 cursorBottom = cursorTop + _plettre->getCharHeight() - 1; + + _vm->_draw->_backSurface->fillRect(cursorLeft, cursorTop, cursorRight, cursorBottom, 10); + + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, 147, 151, 243, 166); + } + + if ((key == kKeyReturn) && !_name.empty()) { + _name.trim(); + _name.setChar(Util::toCP850Upper(_name[0]), 0); + + state = kCharGenStateSure; + charGenSetup(state); + _vm->_draw->forceBlit(); + } + } + + if (mouseButtons == kMouseButtonsLeft) { + stopSound(); + playSound(kSoundClick); + + int trousers = checkButton(kCharGenTrousersButtons, ARRAYSIZE(kCharGenTrousersButtons), mouseX, mouseY); + if ((state == kCharGenStateTrousers) && (trousers >= 0)) { + _colorTrousers = trousers; + + ani.recolor(0x09, _colorTrousers); + + state = kCharGenStateName; + charGenSetup(state); + _vm->_draw->forceBlit(); + } + + int jacket = checkButton(kCharGenJacketButtons, ARRAYSIZE(kCharGenJacketButtons), mouseX, mouseY); + if ((state == kCharGenStateJacket) && (jacket >= 0)) { + _colorJacket = jacket; + + ani.recolor(0x0A, _colorJacket); + + state = kCharGenStateTrousers; + charGenSetup(state); + _vm->_draw->forceBlit(); + } + + int hair = checkButton(kCharGenHairButtons, ARRAYSIZE(kCharGenHairButtons), mouseX, mouseY); + if ((state == kCharGenStateHair) && (hair >= 0)) { + _colorHair = hair; + + ani.recolor(0x0C, _colorHair); + + state = kCharGenStateJacket; + charGenSetup(state); + _vm->_draw->forceBlit(); + } + + int head = checkButton(kCharGenHeadButtons, ARRAYSIZE(kCharGenHeadButtons), mouseX, mouseY); + if ((state == kCharGenStateHead) && (head >= 0)) { + _head = head; + + state = kCharGenStateHair; + charGenSetup(state); + _vm->_draw->forceBlit(); + } + } + + drawAnim(anims); + + // Play the child sounds + CharGenChild::Sound childSound = child->shouldPlaySound(); + if (childSound == CharGenChild::kSoundWalk) { + beep(50, 10); + } else if (childSound == CharGenChild::kSoundJump) { + stopSound(); + playSound(kSoundJump); + } + + showCursor(); + fadeIn(); + + endFrame(true); + } + + fadeOut(); + hideCursor(); + + freeAnims(anims); + + if (_vm->shouldQuit()) + return kCharGenAbort; + + return action; +} + +bool OnceUpon::sectionChapter1() { + showChapter(1); + return true; +} + +bool OnceUpon::sectionParents() { + fadeOut(); + setGamePalette(14); + clearScreen(); + + const Common::String seq = ((_house == 1) || (_house == 2)) ? "parents.seq" : "parents2.seq"; + const Common::String gct = getLocFile("mefait.gc"); + + Parents parents(_vm, seq, gct, _name, _house, *_plettre, kGamePalettes[14], kGamePalettes[13], kPaletteSize); + parents.play(); + + warning("OnceUpon::sectionParents(): TODO: Item search"); + return true; +} + +bool OnceUpon::sectionChapter2() { + showChapter(2); + return true; +} + +bool OnceUpon::sectionForest0() { + warning("OnceUpon::sectionForest0(): TODO"); + return true; +} + +bool OnceUpon::sectionChapter3() { + showChapter(3); + return true; +} + +bool OnceUpon::sectionEvilCastle() { + warning("OnceUpon::sectionEvilCastle(): TODO"); + return true; +} + +bool OnceUpon::sectionChapter4() { + showChapter(4); + return true; +} + +bool OnceUpon::sectionForest1() { + warning("OnceUpon::sectionForest1(): TODO"); + return true; +} + +bool OnceUpon::sectionChapter5() { + showChapter(5); + return true; +} + +bool OnceUpon::sectionBossFight() { + warning("OnceUpon::sectionBossFight(): TODO"); + return true; +} + +bool OnceUpon::sectionChapter6() { + showChapter(6); + return true; +} + +bool OnceUpon::sectionForest2() { + warning("OnceUpon::sectionForest2(): TODO"); + return true; +} + +bool OnceUpon::sectionChapter7() { + showChapter(7); + return true; +} + +const PreGob::AnimProperties OnceUpon::kSectionEndAnimations[] = { + { 0, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, + { 6, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, + { 9, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, + {11, 0, ANIObject::kModeContinuous, true, false, false, 0, 0} +}; + +bool OnceUpon::sectionEnd() { + fadeOut(); + setGamePalette(9); + + _vm->_video->drawPackedSprite("cadre.cmp", *_vm->_draw->_backSurface); + + Surface endBackground(320, 200, 1); + _vm->_video->drawPackedSprite("fin.cmp", endBackground); + + _vm->_draw->_backSurface->blit(endBackground, 0, 0, 288, 137, 16, 50); + + GCTFile *endText = loadGCT(getLocFile("final.gc")); + endText->setArea(17, 18, 303, 41); + endText->setText(1, _name); + + ANIFile ani(_vm, "fin.ani", 320); + ANIList anims; + + loadAnims(anims, ani, ARRAYSIZE(kSectionEndAnimations), kSectionEndAnimations); + drawAnim(anims); + + _vm->_draw->forceBlit(); + + uint32 textStartTime = 0; + + MenuAction action = kMenuActionNone; + while (!_vm->shouldQuit() && (action == kMenuActionNone)) { + // Check user input + + int16 mouseX, mouseY; + MouseButtons mouseButtons; + + int16 key = checkInput(mouseX, mouseY, mouseButtons); + + action = doIngameMenu(key, mouseButtons); + if (action != kMenuActionNone) + break; + + clearAnim(anims); + + // Pressed a key or mouse button => Skip to next area-full of text + if ((mouseButtons == kMouseButtonsLeft) || (key != 0)) + textStartTime = 0; + + // Draw the next area-full of text + uint32 now = _vm->_util->getTimeKey(); + if (!endText->finished() && ((textStartTime == 0) || (now >= (textStartTime + kGCTDelay)))) { + textStartTime = now; + + int16 left, top, right, bottom; + if (endText->clear(*_vm->_draw->_backSurface, left, top, right, bottom)) + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom); + + if (endText->draw(*_vm->_draw->_backSurface, 0, *_plettre, 10, left, top, right, bottom)) + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom); + } + + drawAnim(anims); + fadeIn(); + + endFrame(true); + } + + freeAnims(anims); + delete endText; + + // Restart requested + if (action == kMenuActionRestart) + return false; + + // Last scene. Even if we didn't explicitly request a quit, the game ends here + _quit = true; + return false; +} + +} // End of namespace OnceUpon + +} // End of namespace Gob diff --git a/engines/gob/pregob/onceupon/onceupon.h b/engines/gob/pregob/onceupon/onceupon.h new file mode 100644 index 0000000000..66ef877618 --- /dev/null +++ b/engines/gob/pregob/onceupon/onceupon.h @@ -0,0 +1,344 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public 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 GOB_PREGOB_ONCEUPON_ONCEUPON_H +#define GOB_PREGOB_ONCEUPON_ONCEUPON_H + +#include "common/system.h" +#include "common/str.h" + +#include "gob/pregob/pregob.h" + +#include "gob/pregob/onceupon/stork.h" + +namespace Gob { + +class Surface; +class Font; + +class ANIObject; + +namespace OnceUpon { + +class OnceUpon : public PreGob { +public: + /** Number of languages we support. */ + static const uint kLanguageCount = 5; + + + OnceUpon(GobEngine *vm); + ~OnceUpon(); + + +protected: + /** A description of a menu button. */ + struct MenuButton { + bool needDraw; ///< Does the button need drawing? + + int16 left; ///< Left coordinate of the button. + int16 top; ///< Top coordinate of the button. + int16 right; ///< Right coordinate of the button. + int16 bottom; ///< Bottom coordinate of the button. + + int16 srcLeft; ///< Left coordinate of the button's sprite. + int16 srcTop; ///< Top coordinate of the button's sprite. + int16 srcRight; ///< Right coordinate of the button's sprite. + int16 srcBottom; ///< Right coordinate of the button's sprite. + + int16 dstX; ///< Destination X coordinate of the button's sprite. + int16 dstY; ///< Destination Y coordinate of the button's sprite. + + uint id; ///< The button's ID. + }; + + /** Parameters for the stork section. */ + struct StorkParam { + const char *backdrop; ///< Backdrop image file. + + uint houseCount; ///< Number of houses. + const MenuButton *houses; ///< House button definitions. + + const Stork::BundleDrop *drops; ///< The bundle drop parameters. + }; + + void init(); + void deinit(); + + /** Handle the copy protection. + * + * @param colors Colors the copy protection animals can be. + * @param shapes The shape that's the correct answer for each animal in each color. + * @param obfuscate Extra obfuscate table. correctShape = shapes[colors][obfuscate[animal]]. + * @return true if the user guessed the correct shape, false otherwise. + */ + bool doCopyProtection(const uint8 colors[7], const uint8 shapes[7 * 20], const uint8 obfuscate[4]); + + /** Show the intro. */ + void showIntro(); + + /** Handle the start menu. + * + * @param animalsButton Definition of the menu button that leads to the animal names screen. Can be 0. + * @param animalCount Number of animals in the animal names screen. + * @param animalButtons Definition of the buttons that make up the animals in the animal names screen. + * @param animalNames File prefixes for the name of each animal. + */ + void doStartMenu(const MenuButton *animalsButton, uint animalCount, + const MenuButton *animalButtons, const char * const *animalNames); + + /** Play the game proper. */ + void playGame(); + + + /** Return the parameters for the stork section. */ + virtual const StorkParam &getStorkParameters() const = 0; + + +private: + /** All actions a user can request in a menu. */ + enum MenuAction { + kMenuActionNone = 0, ///< No action. + kMenuActionAnimals , ///< Do the animal names. + kMenuActionPlay , ///< Play the game. + kMenuActionRestart , ///< Restart the section. + kMenuActionMainMenu, ///< Go to the main menu. + kMenuActionQuit ///< Quit the game. + }; + + /** Difficulty levels. */ + enum Difficulty { + kDifficultyBeginner = 0, + kDifficultyIntermediate = 1, + kDifficultyAdvanced = 2, + kDifficultyCount + }; + + /** The different sounds common in the game. */ + enum Sound { + kSoundClick = 0, + kSoundStork , + kSoundJump , + kSoundCount + }; + + /** Action the character generation wants us to take. */ + enum CharGenAction { + kCharGenDone = 0, ///< Created a character, move on. + kCharGenAbort , ///< Aborted the character generation. + kCharGenRestart ///< Restart the character generation. + }; + + /** A complete screen backup. */ + struct ScreenBackup { + Surface *screen; ///< Screen contents. + int palette; ///< Screen palette. + + bool changedCursor; ///< Did we change the cursor? + bool cursorVisible; ///< Was the cursor visible? + + ScreenBackup(); + ~ScreenBackup(); + }; + + + /** The number of game sections. */ + static const int kSectionCount = 15; + + static const MenuButton kMainMenuDifficultyButton[]; ///< Difficulty buttons. + static const MenuButton kSectionButtons[]; ///< Section buttons. + + static const MenuButton kIngameButtons[]; ///< Ingame menu buttons. + + static const MenuButton kAnimalNamesBack; ///< "Back" button in the animal names screens. + static const MenuButton kLanguageButtons[]; ///< Language buttons in the animal names screen. + + static const MenuButton kSectionStorkHouses[]; + + static const MenuButton kCharGenHeadButtons[]; + static const MenuButton kCharGenHeads[]; + static const MenuButton kCharGenHairButtons[]; + static const MenuButton kCharGenJacketButtons[]; + static const MenuButton kCharGenTrousersButtons[]; + static const MenuButton kCharGenNameEntry[]; + + /** All general game sounds we know about. */ + static const char *kSound[kSoundCount]; + + + static const AnimProperties kClownAnimations[]; + static const AnimProperties kTitleAnimation; + static const AnimProperties kSectionStorkAnimations[]; + static const AnimProperties kSectionEndAnimations[]; + + + /** Function pointer type for a section handler. */ + typedef bool (OnceUpon::*SectionFunc)(); + /** Section handler function. */ + static const SectionFunc kSectionFuncs[kSectionCount]; + + + /** Did we open the game archives? */ + bool _openedArchives; + + // Fonts + Font *_jeudak; + Font *_lettre; + Font *_plettre; + Font *_glettre; + + /** The current palette. */ + int _palette; + + bool _quit; ///< Did the user request a normal game quit? + + Difficulty _difficulty; ///< The current difficulty. + int _section; ///< The current game section. + + Common::String _name; ///< The name of the child. + + uint8 _house; + + uint8 _head; + uint8 _colorHair; + uint8 _colorJacket; + uint8 _colorTrousers; + + + // -- General helpers -- + + void setGamePalette(uint palette); ///< Set a game palette. + void setGameCursor(); ///< Set the default game cursor. + + /** Draw this sprite in a fancy, animated line-by-line way. */ + void drawLineByLine(const Surface &src, int16 left, int16 top, int16 right, int16 bottom, + int16 x, int16 y) const; + + /** Backup the screen contents. */ + void backupScreen(ScreenBackup &backup, bool setDefaultCursor = false); + /** Restore the screen contents with a previously made backup. */ + void restoreScreen(ScreenBackup &backup); + + Common::String fixString(const Common::String &str) const; ///< Fix a string if necessary. + void fixTXTStrings(TXTFile &txt) const; ///< Fix all strings in a TXT. + + + // -- Copy protection helpers -- + + /** Set up the copy protection. */ + int8 cpSetup(const uint8 colors[7], const uint8 shapes[7 * 20], + const uint8 obfuscate[4], const Surface sprites[2]); + /** Find the shape under these coordinates. */ + int8 cpFindShape(int16 x, int16 y) const; + /** Display the "You are wrong" screen. */ + void cpWrong(); + + + // -- Show different game screens -- + + void showWait(uint palette = 0xFFFF); ///< Show the wait / loading screen. + void showQuote(); ///< Show the quote about fairytales. + void showTitle(); ///< Show the Once Upon A Time title. + void showChapter(int chapter); ///< Show a chapter intro text. + void showByeBye(); ///< Show the "bye bye" screen. + + /** Handle the "listen to animal names" part. */ + void handleAnimalNames(uint count, const MenuButton *buttons, const char * const *names); + + + // -- Menu helpers -- + + MenuAction handleStartMenu(const MenuButton *animalsButton); ///< Handle the start menu. + MenuAction handleMainMenu(); ///< Handle the main menu. + MenuAction handleIngameMenu(); ///< Handle the ingame menu. + + void drawStartMenu(const MenuButton *animalsButton); ///< Draw the start menu. + void drawMainMenu(); ///< Draw the main menu. + void drawIngameMenu(); ///< Draw the ingame menu. + + /** Draw the difficulty label. */ + void drawMenuDifficulty(); + + /** Clear the ingame menu in an animated way. */ + void clearIngameMenu(const Surface &background); + + /** Handle the whole ingame menu. */ + MenuAction doIngameMenu(); + /** Handle the whole ingame menu if ESC or right mouse button was pressed. */ + MenuAction doIngameMenu(int16 &key, MouseButtons &mouseButtons); + + + // -- Menu button helpers -- + + /** Find the button under these coordinates. */ + int checkButton(const MenuButton *buttons, uint count, int16 x, int16 y, int failValue = -1) const; + + /** Draw a menu button. */ + void drawButton (Surface &dest, const Surface &src, const MenuButton &button, int transp = -1) const; + /** Draw multiple menu buttons. */ + void drawButtons(Surface &dest, const Surface &src, const MenuButton *buttons, uint count, int transp = -1) const; + + /** Draw a border around a button. */ + void drawButtonBorder(const MenuButton &button, uint8 color); + + + // -- Animal names helpers -- + + /** Set up the animal chooser. */ + void anSetupChooser(); + /** Set up the language chooser for one animal. */ + void anSetupNames(const MenuButton &animal); + /** Play / Display the name of an animal in one language. */ + void anPlayAnimalName(const Common::String &animal, uint language); + + + // -- Game sections -- + + bool playSection(); + + bool sectionStork(); + bool sectionChapter1(); + bool sectionParents(); + bool sectionChapter2(); + bool sectionForest0(); + bool sectionChapter3(); + bool sectionEvilCastle(); + bool sectionChapter4(); + bool sectionForest1(); + bool sectionChapter5(); + bool sectionBossFight(); + bool sectionChapter6(); + bool sectionForest2(); + bool sectionChapter7(); + bool sectionEnd(); + + CharGenAction characterGenerator(); + void charGenSetup(uint stage); + void charGenDrawName(); + + static bool enterString(Common::String &name, int16 key, uint maxLength, const Font &font); +}; + +} // End of namespace OnceUpon + +} // End of namespace Gob + +#endif // GOB_PREGOB_ONCEUPON_ONCEUPON_H diff --git a/engines/gob/pregob/onceupon/palettes.h b/engines/gob/pregob/onceupon/palettes.h new file mode 100644 index 0000000000..952581041c --- /dev/null +++ b/engines/gob/pregob/onceupon/palettes.h @@ -0,0 +1,411 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public 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 GOB_PREGOB_ONCEUPON_PALETTES_H +#define GOB_PREGOB_ONCEUPON_PALETTES_H + +static const int kPaletteSize = 16; +static const uint kPaletteCount = 20; + +static const byte kCopyProtectionPalette[3 * kPaletteSize] = { + 0x00, 0x00, 0x00, + 0x19, 0x00, 0x19, + 0x00, 0x3F, 0x00, + 0x00, 0x2A, 0x2A, + 0x2A, 0x00, 0x00, + 0x2A, 0x00, 0x2A, + 0x2A, 0x15, 0x00, + 0x00, 0x19, 0x12, + 0x00, 0x00, 0x00, + 0x15, 0x15, 0x3F, + 0x15, 0x3F, 0x15, + 0x00, 0x20, 0x3F, + 0x3F, 0x00, 0x00, + 0x3F, 0x00, 0x20, + 0x3F, 0x3F, 0x00, + 0x3F, 0x3F, 0x3F +}; + +static const byte kGamePalettes[kPaletteCount][3 * kPaletteSize] = { + { + 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, + 0x00, 0x00, 0x18, + 0x00, 0x00, 0x3C, + 0x1C, 0x28, 0x00, + 0x10, 0x18, 0x00, + 0x1C, 0x1C, 0x20, + 0x14, 0x14, 0x14, + 0x14, 0x20, 0x04, + 0x00, 0x00, 0x24, + 0x3C, 0x3C, 0x3C, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x00, 0x00, 0x24, + 0x3C, 0x3C, 0x3C, + 0x14, 0x20, 0x04, + 0x3C, 0x2C, 0x00, + 0x02, 0x00, 0x18, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00, + 0x14, 0x20, 0x04, + 0x00, 0x00, 0x24, + 0x3C, 0x3C, 0x3C, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x38, 0x20, 0x3C, + 0x2C, 0x10, 0x30, + 0x20, 0x08, 0x28, + 0x14, 0x00, 0x1C, + 0x20, 0x20, 0x38, + 0x18, 0x18, 0x2C, + 0x10, 0x10, 0x24, + 0x14, 0x20, 0x04, + 0x00, 0x00, 0x24, + 0x3C, 0x3C, 0x3C, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x3C, 0x20, 0x20, + 0x24, 0x14, 0x14, + 0x1C, 0x10, 0x10, + 0x14, 0x0C, 0x0C, + 0x1C, 0x1C, 0x1C, + 0x18, 0x18, 0x18, + 0x10, 0x10, 0x10, + 0x14, 0x20, 0x04, + 0x00, 0x00, 0x24, + 0x3C, 0x3C, 0x3C, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x10, 0x28, 0x1C, + 0x10, 0x1C, 0x10, + 0x10, 0x14, 0x0C, + 0x1C, 0x1C, 0x3C, + 0x24, 0x24, 0x3C, + 0x18, 0x18, 0x24, + 0x10, 0x10, 0x18, + 0x14, 0x20, 0x04, + 0x00, 0x00, 0x24, + 0x3C, 0x3C, 0x3C, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x3F, 0x26, 0x3F, + 0x36, 0x1C, 0x36, + 0x2C, 0x12, 0x2A, + 0x27, 0x0C, 0x24, + 0x22, 0x07, 0x1E, + 0x1D, 0x03, 0x18, + 0x16, 0x00, 0x10, + 0x14, 0x20, 0x04, + 0x00, 0x00, 0x24, + 0x3C, 0x3C, 0x3A, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x3F, 0x39, 0x26, + 0x38, 0x34, 0x1C, + 0x30, 0x2F, 0x13, + 0x27, 0x29, 0x0C, + 0x1D, 0x22, 0x07, + 0x14, 0x1B, 0x03, + 0x0C, 0x14, 0x00, + 0x14, 0x20, 0x04, + 0x00, 0x00, 0x24, + 0x3C, 0x3C, 0x3A, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x24, 0x3C, 0x3C, + 0x1C, 0x34, 0x38, + 0x14, 0x2C, 0x30, + 0x0C, 0x20, 0x2C, + 0x08, 0x18, 0x28, + 0x04, 0x10, 0x20, + 0x00, 0x08, 0x1C, + 0x14, 0x20, 0x04, + 0x00, 0x00, 0x24, + 0x3C, 0x3C, 0x38, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x24, + 0x38, 0x24, 0x1C, + 0x30, 0x1C, 0x14, + 0x28, 0x18, 0x0C, + 0x20, 0x10, 0x04, + 0x1C, 0x0C, 0x00, + 0x14, 0x08, 0x00, + 0x14, 0x20, 0x04, + 0x00, 0x00, 0x24, + 0x3C, 0x3C, 0x38, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x3C, 0x34, 0x24, + 0x38, 0x2C, 0x1C, + 0x30, 0x24, 0x14, + 0x2C, 0x1C, 0x10, + 0x30, 0x30, 0x3C, + 0x1C, 0x1C, 0x38, + 0x0C, 0x0C, 0x38, + 0x14, 0x20, 0x04, + 0x00, 0x00, 0x24, + 0x3C, 0x3C, 0x3C, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0C, + 0x02, 0x03, 0x14, + 0x07, 0x07, 0x1D, + 0x0E, 0x0E, 0x25, + 0x17, 0x17, 0x2E, + 0x21, 0x22, 0x36, + 0x2F, 0x2F, 0x3F, + 0x3F, 0x3F, 0x3F, + 0x3F, 0x3B, 0x0D, + 0x3A, 0x31, 0x0A, + 0x35, 0x28, 0x07, + 0x30, 0x21, 0x04, + 0x2B, 0x19, 0x02, + 0x26, 0x12, 0x01, + 0x16, 0x0B, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, + 0x21, 0x01, 0x00, + 0x2A, 0x02, 0x00, + 0x33, 0x03, 0x00, + 0x3D, 0x06, 0x00, + 0x2A, 0x19, 0x05, + 0x15, 0x14, 0x14, + 0x22, 0x1F, 0x1E, + 0x2F, 0x2C, 0x28, + 0x3F, 0x3C, 0x29, + 0x3F, 0x38, 0x0B, + 0x3B, 0x30, 0x0A, + 0x37, 0x29, 0x08, + 0x33, 0x23, 0x07, + 0x2F, 0x1D, 0x06 + }, + { + 0x00, 0x00, 0x00, + 0x00, 0x1C, 0x38, + 0x34, 0x30, 0x28, + 0x2C, 0x24, 0x1C, + 0x24, 0x18, 0x10, + 0x1C, 0x10, 0x08, + 0x14, 0x04, 0x04, + 0x10, 0x00, 0x00, + 0x14, 0x20, 0x04, + 0x00, 0x00, 0x24, + 0x3C, 0x3C, 0x38, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x00, 0x1C, 0x38, + 0x34, 0x30, 0x28, + 0x2C, 0x24, 0x1C, + 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, + 0x14, 0x20, 0x04, + 0x00, 0x00, 0x24, + 0x3C, 0x3C, 0x38, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x1A, 0x30, 0x37, + 0x14, 0x28, 0x31, + 0x10, 0x20, 0x2C, + 0x0C, 0x19, 0x27, + 0x08, 0x12, 0x21, + 0x05, 0x0C, 0x1C, + 0x03, 0x07, 0x16, + 0x01, 0x03, 0x11, + 0x00, 0x00, 0x0C, + 0x3C, 0x3C, 0x3C, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x34, 0x30, 0x34, + 0x30, 0x24, 0x30, + 0x28, 0x1C, 0x28, + 0x24, 0x14, 0x24, + 0x1C, 0x0C, 0x1C, + 0x18, 0x08, 0x18, + 0x14, 0x04, 0x14, + 0x0C, 0x04, 0x0C, + 0x08, 0x00, 0x08, + 0x3C, 0x3C, 0x3C, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x2C, 0x24, 0x0C, + 0x34, 0x34, 0x28, + 0x2C, 0x2C, 0x1C, + 0x24, 0x24, 0x10, + 0x1C, 0x18, 0x08, + 0x14, 0x14, 0x08, + 0x10, 0x10, 0x04, + 0x0C, 0x0C, 0x04, + 0x00, 0x00, 0x24, + 0x3C, 0x3C, 0x38, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, + 0x14, 0x28, 0x31, + 0x10, 0x20, 0x2C, + 0x0C, 0x19, 0x27, + 0x08, 0x12, 0x21, + 0x05, 0x0C, 0x1C, + 0x03, 0x07, 0x16, + 0x01, 0x03, 0x11, + 0x00, 0x3C, 0x00, + 0x3C, 0x3C, 0x3C, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x10, 0x28, 0x1C, + 0x10, 0x1C, 0x10, + 0x10, 0x14, 0x0C, + 0x1C, 0x1C, 0x3C, + 0x24, 0x24, 0x3C, + 0x18, 0x18, 0x24, + 0x10, 0x10, 0x18, + 0x14, 0x20, 0x04, + 0x00, 0x00, 0x24, + 0x3C, 0x3C, 0x3C, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x10, 0x28, 0x1C, + 0x10, 0x1C, 0x10, + 0x10, 0x14, 0x0C, + 0x1C, 0x1C, 0x3C, + 0x24, 0x24, 0x3C, + 0x18, 0x18, 0x24, + 0x10, 0x10, 0x18, + 0x14, 0x20, 0x04, + 0x00, 0x00, 0x24, + 0x3C, 0x3C, 0x3C, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + } +}; + +#endif // GOB_PREGOB_ONCEUPON_PALETTES_H diff --git a/engines/gob/pregob/onceupon/parents.cpp b/engines/gob/pregob/onceupon/parents.cpp new file mode 100644 index 0000000000..cdaee6a38d --- /dev/null +++ b/engines/gob/pregob/onceupon/parents.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 "gob/gob.h" +#include "gob/global.h" +#include "gob/dataio.h" +#include "gob/palanim.h" +#include "gob/draw.h" +#include "gob/video.h" + +#include "gob/sound/sound.h" + +#include "gob/pregob/gctfile.h" + +#include "gob/pregob/onceupon/palettes.h" +#include "gob/pregob/onceupon/parents.h" + +namespace Gob { + +namespace OnceUpon { + +const char *Parents::kSound[kSoundCount] = { + "rire.snd", // kSoundCackle + "tonn.snd" // kSoundThunder +}; + +// So that every GCT line is displayed for 12 seconds +const uint16 Parents::kLoop[kLoopCount][3] = { + { 72, 77, 33}, + {105, 109, 38}, + {141, 145, 38}, + {446, 454, 23}, + {456, 464, 23}, + {466, 474, 23}, + {476, 484, 23} +}; + + +Parents::Parents(GobEngine *vm, const Common::String &seq, const Common::String &gct, + const Common::String &childName, uint8 house, const Font &font, + const byte *normalPalette, const byte *brightPalette, uint paletteSize) : + SEQFile(vm, seq), + _gct(0), _house(house), _font(&font), + _paletteSize(paletteSize), _normalPalette(normalPalette), _brightPalette(brightPalette) { + + // Load sounds + for (int i = 0; i < kSoundCount; i++) + _vm->_sound->sampleLoad(&_sounds[i], SOUND_SND, kSound[i]); + + // Load GCT + Common::SeekableReadStream *gctStream = _vm->_dataIO->getFile(gct); + if (gctStream) { + _gct = new GCTFile(*gctStream, _vm->_rnd); + + delete gctStream; + } else + error("Parents::Parents(): Failed to open \"%s\"", gct.c_str()); + + _gct->setArea(17, 18, 303, 41); + _gct->setText(1, childName); + + _gct->selectLine(2, _house); + _gct->selectLine(4, _house); + + for (uint i = 0; i < kLoopCount; i++) + _loopID[i] = addLoop(kLoop[i][0], kLoop[i][1], kLoop[i][2]); +} + +Parents::~Parents() { + delete _gct; +} + +void Parents::play() { + _currentLoop = 0; + + SEQFile::play(true, 496, 15); + + // After playback, fade out + if (!_vm->shouldQuit()) + _vm->_palAnim->fade(0, 0, 0); +} + +void Parents::handleFrameEvent() { + switch (getFrame()) { + case 0: + // On fame 0, fade in + _vm->_draw->forceBlit(); + _vm->_palAnim->fade(_vm->_global->_pPaletteDesc, 0, 0); + break; + + case 4: + drawGCT(0); + break; + + case 55: + drawGCT(3, 0); + break; + + case 79: + drawGCT(_house + 5, 1); + break; + + case 110: + drawGCT(_house + 9, 2); + break; + + case 146: + drawGCT(17); + break; + + case 198: + drawGCT(13); + break; + + case 445: + drawGCT(14, 3); + break; + + case 455: + drawGCT(18, 4); + break; + + case 465: + drawGCT(19, 5); + break; + + case 475: + drawGCT(20, 6); + break; + + case 188: + case 228: + case 237: + case 257: + case 275: + case 426: + lightningEffect(); + break; + + case 203: + case 243: + case 252: + case 272: + case 290: + case 441: + playSound(kSoundThunder); + break; + + case 340: + playSound(kSoundCackle); + break; + } +} + +void Parents::handleInput(int16 key, int16 mouseX, int16 mouseY, MouseButtons mouseButtons) { + if ((key == kKeyEscape) || (mouseButtons == kMouseButtonsRight)) + abortPlay(); + + if (((key == kKeySpace) || (mouseButtons == kMouseButtonsLeft)) && (_currentLoop < kLoopCount)) + skipLoop(_loopID[_currentLoop]); +} + +void Parents::playSound(Sound sound) { + _vm->_sound->blasterStop(0); + _vm->_sound->blasterPlay(&_sounds[sound], 0, 0); +} + +void Parents::lightningEffect() { + for (int i = 0; (i < 5) && !_vm->shouldQuit(); i++) { + + setPalette(_brightPalette, _paletteSize); + _vm->_util->delay(5); + + setPalette(_normalPalette, _paletteSize); + _vm->_util->delay(5); + } +} + +void Parents::setPalette(const byte *palette, uint size) { + memcpy(_vm->_draw->_vgaPalette, palette, 3 * size); + + _vm->_video->setFullPalette(_vm->_global->_pPaletteDesc); + _vm->_video->retrace(); +} + +void Parents::drawGCT(uint item, uint loop) { + int16 left, top, right, bottom; + if (_gct->clear(*_vm->_draw->_backSurface, left, top, right, bottom)) + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom); + if (_gct->draw(*_vm->_draw->_backSurface, item, *_font, 10, left, top, right, bottom)) + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom); + + _currentLoop = loop; +} + +} // End of namespace OnceUpon + +} // End of namespace Gob diff --git a/engines/gob/pregob/onceupon/parents.h b/engines/gob/pregob/onceupon/parents.h new file mode 100644 index 0000000000..f5c8307b73 --- /dev/null +++ b/engines/gob/pregob/onceupon/parents.h @@ -0,0 +1,94 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GOB_PREGOB_ONCEUPON_PARENTS_H +#define GOB_PREGOB_ONCEUPON_PARENTS_H + +#include "gob/sound/sounddesc.h" + +#include "gob/pregob/seqfile.h" + +namespace Gob { + +class Font; + +class GCTFile; + +namespace OnceUpon { + +/** The home / parents animation sequence. */ +class Parents : public SEQFile { +public: + Parents(GobEngine *vm, const Common::String &seq, const Common::String &gct, + const Common::String &childName, uint8 house, const Font &font, + const byte *normalPalette, const byte *brightPalette, uint paletteSize); + ~Parents(); + + void play(); + +protected: + void handleFrameEvent(); + void handleInput(int16 key, int16 mouseX, int16 mouseY, MouseButtons mouseButtons); + +private: + static const uint kLoopCount = 7; + + static const uint16 kLoop[kLoopCount][3]; + + enum Sound { + kSoundCackle = 0, + kSoundThunder , + kSoundCount + }; + + static const char *kSound[kSoundCount]; + + + uint8 _house; + + const Font *_font; + + uint _paletteSize; + const byte *_normalPalette; + const byte *_brightPalette; + + SoundDesc _sounds[kSoundCount]; + + GCTFile *_gct; + + uint _loopID[kLoopCount]; + uint _currentLoop; + + + void lightningEffect(); + + void playSound(Sound sound); + void setPalette(const byte *palette, uint size); + + void drawGCT(uint item, uint loop = 0xFFFF); +}; + +} // End of namespace OnceUpon + +} // End of namespace Gob + +#endif // GOB_PREGOB_ONCEUPON_PARENTS_H diff --git a/engines/gob/pregob/onceupon/stork.cpp b/engines/gob/pregob/onceupon/stork.cpp new file mode 100644 index 0000000000..3c38037d08 --- /dev/null +++ b/engines/gob/pregob/onceupon/stork.cpp @@ -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. + * + */ + +#include "common/str.h" + +#include "gob/gob.h" +#include "gob/surface.h" +#include "gob/anifile.h" +#include "gob/video.h" + +#include "gob/pregob/onceupon/stork.h" + +enum Animation { + kAnimFlyNearWithBundle = 9, + kAnimFlyFarWithBundle = 12, + kAnimFlyNearWithoutBundle = 10, + kAnimFlyFarWithoutBundle = 13, + kAnimBundleNear = 11, + kAnimBundleFar = 14 +}; + +namespace Gob { + +namespace OnceUpon { + +Stork::Stork(GobEngine *vm, const ANIFile &ani) : ANIObject(ani), _shouldDrop(false) { + _frame = new Surface(320, 200, 1); + vm->_video->drawPackedSprite("cadre.cmp", *_frame); + + _bundle = new ANIObject(ani); + + _bundle->setVisible(false); + _bundle->setPause(true); + + setState(kStateFlyNearWithBundle, kAnimFlyNearWithBundle, -80); +} + +Stork::~Stork() { + delete _frame; + + delete _bundle; +} + +bool Stork::hasBundleLanded() const { + if (!_shouldDrop || !_bundle->isVisible() || _bundle->isPaused()) + return false; + + int16 x, y, width, height; + _bundle->getFramePosition(x, y); + _bundle->getFrameSize(width, height); + + return (y + height) >= _bundleDrop.landY; +} + +void Stork::dropBundle(const BundleDrop &drop) { + if (_shouldDrop) + return; + + _shouldDrop = true; + _bundleDrop = drop; +} + +bool Stork::draw(Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom) { + left = 0x7FFF; + top = 0x7FFF; + right = 0x0000; + bottom = 0x0000; + + bool drawn = ANIObject::draw(dest, left, top, right, bottom); + if (drawn) { + // Left frame edge + if (left <= 15) + dest.blit(*_frame, left, top, MIN<int16>(15, right), bottom, left, top); + + // Right frame edge + if (right >= 304) + dest.blit(*_frame, MAX<int16>(304, left), top, right, bottom, MAX<int16>(304, left), top); + } + + int16 bLeft, bTop, bRight, bBottom; + if (_bundle->draw(dest, bLeft, bTop, bRight, bBottom)) { + // Bottom frame edge + if (bBottom >= 188) + dest.blit(*_frame, bLeft, MAX<int16>(188, bTop), bRight, bBottom, bLeft, MAX<int16>(188, bTop)); + + left = MIN(left , bLeft ); + top = MIN(top , bTop ); + right = MAX(right , bRight ); + bottom = MAX(bottom, bBottom); + + drawn = true; + } + + return drawn; +} + +bool Stork::clear(Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom) { + left = 0x7FFF; + top = 0x7FFF; + right = 0x0000; + bottom = 0x0000; + + bool cleared = _bundle->clear(dest, left, top, right, bottom); + + int16 sLeft, sTop, sRight, sBottom; + if (ANIObject::clear(dest, sLeft, sTop, sRight, sBottom)) { + left = MIN(left , sLeft ); + top = MIN(top , sTop ); + right = MAX(right , sRight ); + bottom = MAX(bottom, sBottom); + + cleared = true; + } + + return cleared; +} + +void Stork::advance() { + _bundle->advance(); + + ANIObject::advance(); + + int16 curX, curY, curWidth, curHeight; + getFramePosition(curX, curY, 0); + getFrameSize(curWidth, curHeight, 0); + + const int16 curRight = curX + curWidth - 1; + + int16 nextX, nextY, nextWidth, nextHeight; + getFramePosition(nextX, nextY, 1); + getFrameSize(nextWidth, nextHeight, 1); + + const int16 nextRight = nextX + nextWidth - 1; + + switch (_state) { + case kStateFlyNearWithBundle: + if (curX >= 330) + setState(kStateFlyFarWithBundle, kAnimFlyFarWithBundle, 330); + + if ((curRight <= _bundleDrop.dropX) && + (nextRight >= _bundleDrop.dropX) && _shouldDrop && !_bundleDrop.dropWhileFar) + dropBundle(kStateFlyNearWithoutBundle, kAnimFlyNearWithoutBundle); + + break; + + case kStateFlyFarWithBundle: + if (curX <= -80) + setState(kStateFlyNearWithBundle, kAnimFlyNearWithBundle, -80); + + if ((curX >= _bundleDrop.dropX) && + (nextX <= _bundleDrop.dropX) && _shouldDrop && _bundleDrop.dropWhileFar) + dropBundle(kStateFlyFarWithoutBundle, kAnimFlyFarWithoutBundle); + + break; + + case kStateFlyNearWithoutBundle: + if (curX >= 330) + setState(kStateFlyFarWithoutBundle, kAnimFlyFarWithoutBundle, 330); + break; + + case kStateFlyFarWithoutBundle: + if (curX <= -80) + setState(kStateFlyNearWithoutBundle, kAnimFlyNearWithoutBundle, -80); + break; + + default: + break; + } +} + +void Stork::dropBundle(State state, uint16 anim) { + setState(state, anim); + + int16 x, y, width, height; + getFramePosition(x, y); + getFrameSize(width, height); + + _bundle->setAnimation(_bundleDrop.anim); + _bundle->setPause(false); + _bundle->setVisible(true); + + int16 bWidth, bHeight; + _bundle->getFrameSize(bWidth, bHeight); + + // Drop position + x = _bundleDrop.dropX; + y = y + height - bHeight; + + // If the stork is flying near (from left to right), drop the bundle at the right edge + if (!_bundleDrop.dropWhileFar) + x = x - bWidth; + + _bundle->setPosition(x, y); +} + +void Stork::setState(State state, uint16 anim) { + setAnimation(anim); + setVisible(true); + setPause(false); + + _state = state; +} + +void Stork::setState(State state, uint16 anim, int16 x) { + setState(state, anim); + setPosition(); + + int16 pX, pY; + getPosition(pX, pY); + setPosition( x, pY); +} + +} // End of namespace OnceUpon + +} // End of namespace Gob diff --git a/engines/gob/pregob/onceupon/stork.h b/engines/gob/pregob/onceupon/stork.h new file mode 100644 index 0000000000..756f5258c7 --- /dev/null +++ b/engines/gob/pregob/onceupon/stork.h @@ -0,0 +1,103 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public 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 GOB_PREGOB_ONCEUPON_STORK_H +#define GOB_PREGOB_ONCEUPON_STORK_H + +#include "common/system.h" + +#include "gob/aniobject.h" + +namespace Common { + class String; +} + +namespace Gob { + +class GobEngine; + +class Surface; +class ANIFile; + +namespace OnceUpon { + +/** The stork in Baba Yaga / dragon in Abracadabra. */ +class Stork : public ANIObject { +public: + /** Information on how to drop the bundle. */ + struct BundleDrop { + int16 anim; ///< Animation of the bundle floating down + + int16 dropX; ///< X position the stork drops the bundle + int16 landY; ///< Y position the bundle lands + + bool dropWhileFar; ///< Does the stork drop the bundle while far instead of near? + }; + + Stork(GobEngine *vm, const ANIFile &ani); + ~Stork(); + + /** Has the bundle landed? */ + bool hasBundleLanded() const; + + /** Drop the bundle. */ + void dropBundle(const BundleDrop &drop); + + /** Draw the current frame onto the surface and return the affected rectangle. */ + bool draw(Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom); + /** Draw the current frame from the surface and return the affected rectangle. */ + bool clear(Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom); + + /** Advance the animation to the next frame. */ + void advance(); + +private: + enum State { + kStateFlyNearWithBundle = 0, + kStateFlyFarWithBundle , + kStateFlyNearWithoutBundle , + kStateFlyFarWithoutBundle + }; + + + GobEngine *_vm; + + Surface *_frame; + ANIObject *_bundle; + + State _state; + + bool _shouldDrop; + BundleDrop _bundleDrop; + + + void setState(State state, uint16 anim); + void setState(State state, uint16 anim, int16 x); + + void dropBundle(State state, uint16 anim); +}; + +} // End of namespace OnceUpon + +} // End of namespace Gob + +#endif // GOB_PREGOB_ONCEUPON_STORK_H diff --git a/engines/gob/pregob/onceupon/title.cpp b/engines/gob/pregob/onceupon/title.cpp new file mode 100644 index 0000000000..5163ff6822 --- /dev/null +++ b/engines/gob/pregob/onceupon/title.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 "gob/gob.h" +#include "gob/global.h" +#include "gob/palanim.h" +#include "gob/draw.h" + +#include "gob/sound/sound.h" + +#include "gob/pregob/onceupon/title.h" + +namespace Gob { + +namespace OnceUpon { + +Title::Title(GobEngine *vm) : SEQFile(vm, "ville.seq") { +} + +Title::~Title() { +} + +void Title::play() { + SEQFile::play(true, 0xFFFF, 15); + + // After playback, fade out and stop the music + if (!_vm->shouldQuit()) + _vm->_palAnim->fade(0, 0, 0); + + stopMusic(); +} + +void Title::handleFrameEvent() { + // On fame 0, start the music and fade in + if (getFrame() == 0) { + playMusic(); + + _vm->_draw->forceBlit(); + _vm->_palAnim->fade(_vm->_global->_pPaletteDesc, 0, 0); + } +} + +void Title::playMusic() { + // Look at what platform this is and play the appropriate music type + + if (_vm->getPlatform() == Common::kPlatformPC) + playMusicDOS(); + else if (_vm->getPlatform() == Common::kPlatformAmiga) + playMusicAmiga(); + else if (_vm->getPlatform() == Common::kPlatformAtariST) + playMusicAtariST(); +} + +void Title::playMusicDOS() { + // Play an AdLib track + + _vm->_sound->adlibLoadTBR("babayaga.tbr"); + _vm->_sound->adlibLoadMDY("babayaga.mdy"); + _vm->_sound->adlibSetRepeating(-1); + _vm->_sound->adlibPlay(); +} + +void Title::playMusicAmiga() { + // Play a Protracker track + + _vm->_sound->protrackerPlay("mod.babayaga"); +} + +void Title::playMusicAtariST() { + // Play a Soundblaster composition + + static const int16 titleMusic[21] = { 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 2, 0, 1, 0, 0, 0, 0, 0, -1}; + static const char * const titleFiles[ 3] = {"baba1.snd", "baba2.snd", "baba3.snd"}; + + for (uint i = 0; i < ARRAYSIZE(titleFiles); i++) + _vm->_sound->sampleLoad(_vm->_sound->sampleGetBySlot(i), SOUND_SND, titleFiles[i]); + + _vm->_sound->blasterPlayComposition(titleMusic, 0); + _vm->_sound->blasterRepeatComposition(-1); +} + +void Title::stopMusic() { + // Just stop everything + + _vm->_sound->adlibSetRepeating(0); + _vm->_sound->blasterRepeatComposition(0); + + _vm->_sound->adlibStop(); + _vm->_sound->blasterStopComposition(); + _vm->_sound->protrackerStop(); + + for (int i = 0; i < ::Gob::Sound::kSoundsCount; i++) + _vm->_sound->sampleFree(_vm->_sound->sampleGetBySlot(i)); +} + +} // End of namespace OnceUpon + +} // End of namespace Gob diff --git a/engines/gob/pregob/onceupon/title.h b/engines/gob/pregob/onceupon/title.h new file mode 100644 index 0000000000..5e7ef76d40 --- /dev/null +++ b/engines/gob/pregob/onceupon/title.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 GOB_PREGOB_ONCEUPON_TITLE_H +#define GOB_PREGOB_ONCEUPON_TITLE_H + +#include "gob/pregob/seqfile.h" + +namespace Gob { + +namespace OnceUpon { + +/** The Once Upon A Time title animation sequence. */ +class Title : public SEQFile { +public: + Title(GobEngine *vm); + ~Title(); + + void play(); + +protected: + void handleFrameEvent(); + +private: + void playMusic(); ///< Play the title music. + void playMusicDOS(); ///< Play the title music of the DOS version. + void playMusicAmiga(); ///< Play the title music of the Amiga version. + void playMusicAtariST(); ///< Play the title music of the Atari ST version. + void stopMusic(); ///< Stop the title music. +}; + +} // End of namespace OnceUpon + +} // End of namespace Gob + +#endif // GOB_PREGOB_ONCEUPON_TITLE_H diff --git a/engines/gob/pregob/pregob.cpp b/engines/gob/pregob/pregob.cpp new file mode 100644 index 0000000000..54eb3c6795 --- /dev/null +++ b/engines/gob/pregob/pregob.cpp @@ -0,0 +1,351 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public 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/cursorman.h" + +#include "gob/gob.h" +#include "gob/global.h" +#include "gob/util.h" +#include "gob/surface.h" +#include "gob/dataio.h" +#include "gob/palanim.h" +#include "gob/draw.h" +#include "gob/video.h" +#include "gob/aniobject.h" + +#include "gob/sound/sound.h" + +#include "gob/pregob/pregob.h" +#include "gob/pregob/gctfile.h" + + +namespace Gob { + +const char PreGob::kLanguageSuffixShort[5] = { 't', 'g', 'a', 'e', 'i'}; +const char *PreGob::kLanguageSuffixLong [5] = {"fr", "al", "an", "it", "es"}; + + +PreGob::PreGob(GobEngine *vm) : _vm(vm), _fadedOut(false) { +} + +PreGob::~PreGob() { +} + +void PreGob::fadeOut() { + if (_fadedOut || _vm->shouldQuit()) + return; + + // Fade to black + _vm->_palAnim->fade(0, 0, 0); + + _fadedOut = true; +} + +void PreGob::fadeIn() { + if (!_fadedOut || _vm->shouldQuit()) + return; + + // Fade to palette + _vm->_draw->blitInvalidated(); + _vm->_palAnim->fade(_vm->_global->_pPaletteDesc, 0, 0); + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, 0, 0, 319, 199); + + _fadedOut = false; +} + +void PreGob::clearScreen() { + _vm->_draw->_backSurface->clear(); + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, 0, 0, 319, 199); + _vm->_draw->blitInvalidated(); + _vm->_video->retrace(); +} + +void PreGob::initScreen() { + _vm->_util->setFrameRate(15); + + _fadedOut = true; + + _vm->_draw->initScreen(); + + _vm->_draw->_backSurface->clear(); + _vm->_util->clearPalette(); + + _vm->_draw->forceBlit(); + _vm->_video->retrace(); + + _vm->_util->processInput(); +} + +void PreGob::setPalette(const byte *palette, uint16 size) { + memcpy(_vm->_draw->_vgaPalette, palette, 3 * size); + + // If we didn't fade out prior, immediately set the palette + if (!_fadedOut) + _vm->_video->setFullPalette(_vm->_global->_pPaletteDesc); +} + +void PreGob::addCursor() { + CursorMan.pushCursor(0, 0, 0, 0, 0, 0); +} + +void PreGob::removeCursor() { + CursorMan.popCursor(); +} + +void PreGob::setCursor(Surface &sprite, int16 hotspotX, int16 hotspotY) { + CursorMan.replaceCursor(sprite.getData(), sprite.getWidth(), sprite.getHeight(), hotspotX, hotspotY, 0); +} + +void PreGob::setCursor(Surface &sprite, int16 left, int16 top, int16 right, int16 bottom, + int16 hotspotX, int16 hotspotY) { + + const int width = right - left + 1; + const int height = bottom - top + 1; + + if ((width <= 0) || (height <= 0)) + return; + + Surface cursor(width, height, 1); + + cursor.blit(sprite, left, top, right, bottom, 0, 0); + + setCursor(cursor, hotspotX, hotspotX); +} + +void PreGob::showCursor() { + CursorMan.showMouse(true); + + _vm->_draw->_showCursor = 4; +} + +void PreGob::hideCursor() { + CursorMan.showMouse(false); + + _vm->_draw->_showCursor = 0; +} + +bool PreGob::isCursorVisible() const { + return CursorMan.isVisible(); +} + +void PreGob::loadSounds(const char * const *sounds, uint soundCount) { + freeSounds(); + + _sounds.resize(soundCount); + + for (uint i = 0; i < soundCount; i++) + loadSound(_sounds[i], sounds[i]); +} + +void PreGob::freeSounds() { + _sounds.clear(); +} + +bool PreGob::loadSound(SoundDesc &sound, const Common::String &file) const { + return _vm->_sound->sampleLoad(&sound, SOUND_SND, file.c_str()); +} + +void PreGob::playSound(uint sound, int16 frequency, int16 repCount) { + if (sound >= _sounds.size()) + return; + + _vm->_sound->blasterPlay(&_sounds[sound], repCount, frequency); +} + +void PreGob::stopSound() { + _vm->_sound->blasterStop(0); +} + +void PreGob::playSoundFile(const Common::String &file, int16 frequency, int16 repCount, bool interruptible) { + stopSound(); + + SoundDesc sound; + if (!loadSound(sound, file)) + return; + + _vm->_sound->blasterPlay(&sound, repCount, frequency); + + _vm->_util->forceMouseUp(); + + bool finished = false; + while (!_vm->shouldQuit() && !finished && _vm->_sound->blasterPlayingSound()) { + endFrame(true); + + finished = hasInput(); + } + + _vm->_util->forceMouseUp(); + + stopSound(); +} + +void PreGob::beep(int16 frequency, int32 length) { + _vm->_sound->speakerOn(frequency, length); +} + +void PreGob::endFrame(bool doInput) { + _vm->_draw->blitInvalidated(); + _vm->_util->waitEndFrame(); + + if (doInput) + _vm->_util->processInput(); +} + +int16 PreGob::checkInput(int16 &mouseX, int16 &mouseY, MouseButtons &mouseButtons) { + _vm->_util->getMouseState(&mouseX, &mouseY, &mouseButtons); + _vm->_util->forceMouseUp(); + + return _vm->_util->checkKey(); +} + +int16 PreGob::waitInput(int16 &mouseX, int16 &mouseY, MouseButtons &mouseButtons) { + bool finished = false; + + int16 key = 0; + while (!_vm->shouldQuit() && !finished) { + endFrame(true); + + key = checkInput(mouseX, mouseY, mouseButtons); + + finished = (mouseButtons != kMouseButtonsNone) || (key != 0); + } + + return key; +} + +int16 PreGob::waitInput() { + int16 mouseX, mouseY; + MouseButtons mouseButtons; + + return waitInput(mouseX, mouseY, mouseButtons); +} + +bool PreGob::hasInput() { + int16 mouseX, mouseY; + MouseButtons mouseButtons; + + return checkInput(mouseX, mouseY, mouseButtons) || (mouseButtons != kMouseButtonsNone); +} + +void PreGob::clearAnim(ANIObject &anim) { + int16 left, top, right, bottom; + + if (anim.clear(*_vm->_draw->_backSurface, left, top, right, bottom)) + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom); +} + +void PreGob::drawAnim(ANIObject &anim) { + int16 left, top, right, bottom; + + if (anim.draw(*_vm->_draw->_backSurface, left, top, right, bottom)) + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom); + anim.advance(); +} + +void PreGob::redrawAnim(ANIObject &anim) { + clearAnim(anim); + drawAnim(anim); +} + +void PreGob::clearAnim(const ANIList &anims) { + for (int i = (anims.size() - 1); i >= 0; i--) + clearAnim(*anims[i]); +} + +void PreGob::drawAnim(const ANIList &anims) { + for (ANIList::const_iterator a = anims.begin(); a != anims.end(); ++a) + drawAnim(**a); +} + +void PreGob::redrawAnim(const ANIList &anims) { + clearAnim(anims); + drawAnim(anims); +} + +void PreGob::loadAnims(ANIList &anims, ANIFile &ani, uint count, const AnimProperties *props) const { + freeAnims(anims); + + anims.resize(count); + for (uint i = 0; i < count; i++) { + anims[i] = new ANIObject(ani); + + setAnim(*anims[i], props[i]); + } +} + +void PreGob::freeAnims(ANIList &anims) const { + for (ANIList::iterator a = anims.begin(); a != anims.end(); ++a) + delete *a; + + anims.clear(); +} + +void PreGob::setAnim(ANIObject &anim, const AnimProperties &props) const { + anim.setAnimation(props.animation); + anim.setFrame(props.frame); + anim.setMode(props.mode); + anim.setPause(props.paused); + anim.setVisible(props.visible); + + if (props.hasPosition) + anim.setPosition(props.x, props.y); + else + anim.setPosition(); +} + +Common::String PreGob::getLocFile(const Common::String &file) const { + if (_vm->_global->_language >= ARRAYSIZE(kLanguageSuffixShort)) + return file; + + return file + kLanguageSuffixShort[_vm->_global->_language]; +} + +TXTFile *PreGob::loadTXT(const Common::String &txtFile, TXTFile::Format format) const { + Common::SeekableReadStream *txtStream = _vm->_dataIO->getFile(txtFile); + if (!txtStream) + error("PreGob::loadTXT(): Failed to open \"%s\"", txtFile.c_str()); + + TXTFile *txt = new TXTFile(*txtStream, format); + + delete txtStream; + + fixTXTStrings(*txt); + + return txt; +} + +void PreGob::fixTXTStrings(TXTFile &txt) const { +} + +GCTFile *PreGob::loadGCT(const Common::String &gctFile) const { + Common::SeekableReadStream *gctStream = _vm->_dataIO->getFile(gctFile); + if (!gctStream) + error("PreGob::loadGCT(): Failed to open \"%s\"", gctFile.c_str()); + + GCTFile *gct = new GCTFile(*gctStream, _vm->_rnd); + + delete gctStream; + + return gct; +} + +} // End of namespace Gob diff --git a/engines/gob/pregob/pregob.h b/engines/gob/pregob/pregob.h new file mode 100644 index 0000000000..632f85b88e --- /dev/null +++ b/engines/gob/pregob/pregob.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 GOB_PREGOB_PREGOB_H +#define GOB_PREGOB_PREGOB_H + +#include "common/str.h" +#include "common/array.h" + +#include "gob/util.h" +#include "gob/aniobject.h" + +#include "gob/sound/sounddesc.h" + +#include "gob/pregob/txtfile.h" + +namespace Gob { + +class GobEngine; +class Surface; + +class GCTFile; + +class PreGob { +public: + PreGob(GobEngine *vm); + virtual ~PreGob(); + + virtual void run() = 0; + + struct AnimProperties { + uint16 animation; + uint16 frame; + + ANIObject::Mode mode; + + bool visible; + bool paused; + + bool hasPosition; + int16 x; + int16 y; + }; + +protected: + typedef Common::Array<ANIObject *> ANIList; + + static const char kLanguageSuffixShort[5]; + static const char *kLanguageSuffixLong [5]; + + + GobEngine *_vm; + + + // -- Graphics -- + + /** Initialize the game screen. */ + void initScreen(); + + void fadeOut(); ///< Fade to black. + void fadeIn(); ///< Fade to the current palette. + + void clearScreen(); + + /** Change the palette. + * + * @param palette The palette to change to. + * @param size Size of the palette in colors. + */ + void setPalette(const byte *palette, uint16 size); ///< Change the palette + + /** Add a new cursor that can be manipulated to the stack. */ + void addCursor(); + /** Remove the top-most cursor from the stack. */ + void removeCursor(); + + /** Set the current cursor. */ + void setCursor(Surface &sprite, int16 hotspotX, int16 hotspotY); + /** Set the current cursor. */ + void setCursor(Surface &sprite, int16 left, int16 top, int16 right, int16 bottom, + int16 hotspotX, int16 hotspotY); + + /** Show the cursor. */ + void showCursor(); + /** Hide the cursor. */ + void hideCursor(); + + /** Is the cursor currently visible? */ + bool isCursorVisible() const; + + /** Remove an animation from the screen. */ + void clearAnim(ANIObject &anim); + /** Draw an animation to the screen, advancing it. */ + void drawAnim(ANIObject &anim); + /** Clear and draw an animation to the screen, advancing it. */ + void redrawAnim(ANIObject &anim); + + /** Remove animations from the screen. */ + void clearAnim(const ANIList &anims); + /** Draw animations to the screen, advancing them. */ + void drawAnim(const ANIList &anims); + /** Clear and draw animations to the screen, advancing them. */ + void redrawAnim(const ANIList &anims); + + void loadAnims(ANIList &anims, ANIFile &ani, uint count, const AnimProperties *props) const; + void freeAnims(ANIList &anims) const; + + void setAnim(ANIObject &anim, const AnimProperties &props) const; + + /** Wait for the frame to end, handling screen updates and optionally update input. */ + void endFrame(bool doInput); + + + // -- Sound -- + + /** Load all sounds that can be played interactively in the game. */ + void loadSounds(const char * const *sounds, uint soundCount); + /** Free all loaded sound. */ + void freeSounds(); + + /** Play a loaded sound. */ + void playSound(uint sound, int16 frequency = 0, int16 repCount = 0); + /** Stop all sound playback. */ + void stopSound(); + + /** Play a sound until it ends or is interrupted by a keypress. */ + void playSoundFile(const Common::String &file, int16 frequency = 0, int16 repCount = 0, bool interruptible = true); + + /** Beep the PC speaker. */ + void beep(int16 frequency, int32 length); + + + // -- Input -- + + /** Check mouse and keyboard input. */ + int16 checkInput(int16 &mouseX, int16 &mouseY, MouseButtons &mouseButtons); + /** Wait for mouse or keyboard input. */ + int16 waitInput (int16 &mouseX, int16 &mouseY, MouseButtons &mouseButtons); + /** Wait for mouse or keyboard input, but don't care about what was done with the mouse. */ + int16 waitInput(); + /** Did we have mouse or keyboard input? */ + bool hasInput(); + + + // -- TXT helpers -- + + /** Get the name of a localized file. */ + Common::String getLocFile(const Common::String &file) const; + /** Open a TXT file. */ + TXTFile *loadTXT(const Common::String &txtFile, TXTFile::Format format) const; + + /** Called by loadTXT() to fix strings within the TXT file. */ + virtual void fixTXTStrings(TXTFile &txt) const; + + + // -- GCT helpers -- + + GCTFile *loadGCT(const Common::String &gctFile) const; + + +private: + /** Did we fade out? */ + bool _fadedOut; + + /** All loaded sounds. */ + Common::Array<SoundDesc> _sounds; + + + /** Load a sound file. */ + bool loadSound(SoundDesc &sound, const Common::String &file) const; +}; + +} // End of namespace Gob + +#endif // GOB_PREGOB_PREGOB_H diff --git a/engines/gob/pregob/seqfile.cpp b/engines/gob/pregob/seqfile.cpp new file mode 100644 index 0000000000..91973bbb85 --- /dev/null +++ b/engines/gob/pregob/seqfile.cpp @@ -0,0 +1,384 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public 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/str.h" +#include "common/stream.h" + +#include "gob/gob.h" +#include "gob/dataio.h" +#include "gob/draw.h" +#include "gob/decfile.h" +#include "gob/anifile.h" +#include "gob/aniobject.h" + +#include "gob/pregob/seqfile.h" + +namespace Gob { + +SEQFile::SEQFile(GobEngine *vm, const Common::String &fileName) : _vm(vm) { + for (uint i = 0; i < kObjectCount; i++) + _objects[i].object = 0; + + Common::SeekableReadStream *seq = _vm->_dataIO->getFile(Util::setExtension(fileName, ".SEQ")); + if (!seq) { + warning("SEQFile::SEQFile(): No such file \"%s\"", fileName.c_str()); + return; + } + + load(*seq); + + delete seq; +} + +SEQFile::~SEQFile() { + for (uint i = 0; i < kObjectCount; i++) + delete _objects[i].object; + + for (Backgrounds::iterator b = _backgrounds.begin(); b != _backgrounds.end(); ++b) + delete *b; + + for (Animations::iterator a = _animations.begin(); a != _animations.end(); ++a) + delete *a; +} + +void SEQFile::load(Common::SeekableReadStream &seq) { + const uint16 decCount = (uint16)seq.readByte() + 1; + const uint16 aniCount = (uint16)seq.readByte() + 1; + + // Load backgrounds + _backgrounds.reserve(decCount); + for (uint i = 0; i < decCount; i++) { + const Common::String dec = Util::readString(seq, 13); + + if (!_vm->_dataIO->hasFile(dec)) { + warning("SEQFile::load(): No such background \"%s\"", dec.c_str()); + return; + } + + _backgrounds.push_back(new DECFile(_vm, dec, 320, 200)); + } + + // Load animations + _animations.reserve(aniCount); + for (uint i = 0; i < aniCount; i++) { + const Common::String ani = Util::readString(seq, 13); + + if (!_vm->_dataIO->hasFile(ani)) { + warning("SEQFile::load(): No such animation \"%s\"", ani.c_str()); + return; + } + + _animations.push_back(new ANIFile(_vm, ani)); + } + + _frameRate = seq.readUint16LE(); + + // Load background change keys + + const uint16 bgKeyCount = seq.readUint16LE(); + _bgKeys.resize(bgKeyCount); + + for (uint16 i = 0; i < bgKeyCount; i++) { + const uint16 frame = seq.readUint16LE(); + const uint16 index = seq.readUint16LE(); + + _bgKeys[i].frame = frame; + _bgKeys[i].background = index < _backgrounds.size() ? _backgrounds[index] : 0; + } + + // Load animation keys for all 4 objects + + for (uint i = 0; i < kObjectCount; i++) { + const uint16 animKeyCount = seq.readUint16LE(); + _animKeys.reserve(_animKeys.size() + animKeyCount); + + for (uint16 j = 0; j < animKeyCount; j++) { + _animKeys.push_back(AnimationKey()); + + const uint16 frame = seq.readUint16LE(); + const uint16 index = seq.readUint16LE(); + + uint16 animation; + const ANIFile *ani = findANI(index, animation); + + _animKeys.back().object = i; + _animKeys.back().frame = frame; + _animKeys.back().ani = ani; + _animKeys.back().animation = animation; + _animKeys.back().x = seq.readSint16LE(); + _animKeys.back().y = seq.readSint16LE(); + _animKeys.back().order = seq.readSint16LE(); + } + } + +} + +const ANIFile *SEQFile::findANI(uint16 index, uint16 &animation) { + animation = 0xFFFF; + + // 0xFFFF = remove animation + if (index == 0xFFFF) + return 0; + + for (Animations::const_iterator a = _animations.begin(); a != _animations.end(); ++a) { + if (index < (*a)->getAnimationCount()) { + animation = index; + return *a; + } + + index -= (*a)->getAnimationCount(); + } + + return 0; +} + +void SEQFile::play(bool abortable, uint16 endFrame, uint16 frameRate) { + if (_bgKeys.empty() && _animKeys.empty()) + // Nothing to do + return; + + // Init + + _frame = 0; + _abortPlay = false; + + for (uint i = 0; i < kObjectCount; i++) { + delete _objects[i].object; + + _objects[i].object = 0; + _objects[i].order = 0; + } + + for (Loops::iterator l = _loops.begin(); l != _loops.end(); ++l) + l->currentLoop = 0; + + // Set the frame rate + + int16 frameRateBack = _vm->_util->getFrameRate(); + + if (frameRate == 0) + frameRate = _frameRate; + + _vm->_util->setFrameRate(frameRate); + + _abortable = abortable; + + while (!_vm->shouldQuit() && !_abortPlay) { + // Handle the frame contents + playFrame(); + + // Handle extra frame events + handleFrameEvent(); + + // Wait for the frame to end + _vm->_draw->blitInvalidated(); + _vm->_util->waitEndFrame(); + + // Handle input + + _vm->_util->processInput(); + + int16 key = _vm->_util->checkKey(); + + int16 mouseX, mouseY; + MouseButtons mouseButtons; + _vm->_util->getMouseState(&mouseX, &mouseY, &mouseButtons); + _vm->_util->forceMouseUp(); + + handleInput(key, mouseX, mouseY, mouseButtons); + + // Loop + + bool looped = false; + for (Loops::iterator l = _loops.begin(); l != _loops.end(); ++l) { + if ((l->endFrame == _frame) && (l->currentLoop < l->loopCount)) { + _frame = l->startFrame; + + l->currentLoop++; + looped = true; + } + } + + // If we didn't loop, advance the frame and look if we should end here + + if (!looped) { + _frame++; + if (_frame >= endFrame) + break; + } + } + + // Restore the frame rate + _vm->_util->setFrameRate(frameRateBack); +} + +void SEQFile::playFrame() { + // Remove the current animation frames + clearAnims(); + + // Handle background keys, directly updating the background + for (BackgroundKeys::const_iterator b = _bgKeys.begin(); b != _bgKeys.end(); ++b) { + if (!b->background || (b->frame != _frame)) + continue; + + b->background->draw(*_vm->_draw->_backSurface); + + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, 0, 0, 319, 199); + } + + // Handle the animation keys, updating the objects + for (AnimationKeys::const_iterator a = _animKeys.begin(); a != _animKeys.end(); ++a) { + if (a->frame != _frame) + continue; + + Object &object = _objects[a->object]; + + delete object.object; + object.object = 0; + + // No valid animation => remove + if ((a->animation == 0xFFFF) || !a->ani) + continue; + + // Change the animation + + object.object = new ANIObject(*a->ani); + + object.object->setAnimation(a->animation); + object.object->setPosition(a->x, a->y); + object.object->setVisible(true); + object.object->setPause(false); + + object.order = a->order; + } + + // Draw the animations + drawAnims(); +} + +// NOTE: This is really not at all efficient. However, since there's only a +// small number of objects, it should matter. We really do need a stable +// sort, though, so Common::sort() is out. +SEQFile::Objects SEQFile::getOrderedObjects() { + int16 minOrder = (int16)0x7FFF; + int16 maxOrder = (int16)0x8000; + + Objects objects; + + // Find the span of order values + for (uint i = 0; i < kObjectCount; i++) { + if (!_objects[i].object) + continue; + + minOrder = MIN(minOrder, _objects[i].order); + maxOrder = MAX(maxOrder, _objects[i].order); + } + + // Stably sort the objects by order value + for (int16 o = minOrder; o <= maxOrder; o++) + for (uint i = 0; i < kObjectCount; i++) + if (_objects[i].object && (_objects[i].order == o)) + objects.push_back(_objects[i]); + + return objects; +} + +void SEQFile::clearAnims() { + Objects objects = getOrderedObjects(); + + // Remove the animation frames, in reverse drawing order + for (Objects::iterator o = objects.reverse_begin(); o != objects.end(); --o) { + int16 left, top, right, bottom; + + if (o->object->clear(*_vm->_draw->_backSurface, left, top, right, bottom)) + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom); + } +} + +void SEQFile::drawAnims() { + Objects objects = getOrderedObjects(); + + // Draw the animation frames and advance the animation + for (Objects::iterator o = objects.begin(); o != objects.end(); ++o) { + int16 left, top, right, bottom; + + if (o->object->draw(*_vm->_draw->_backSurface, left, top, right, bottom)) + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom); + + o->object->advance(); + } +} + +uint16 SEQFile::getFrame() const { + return _frame; +} + +void SEQFile::seekFrame(uint16 frame) { + _frame = frame; +} + +uint SEQFile::addLoop(uint16 startFrame, uint16 endFrame, uint16 loopCount) { + _loops.resize(_loops.size() + 1); + + _loops.back().startFrame = startFrame; + _loops.back().endFrame = endFrame; + _loops.back().loopCount = loopCount; + _loops.back().currentLoop = 0; + _loops.back().empty = false; + + return _loops.size() - 1; +} + +void SEQFile::skipLoop(uint loopID) { + if (loopID >= _loops.size()) + return; + + _loops[loopID].currentLoop = 0xFFFF; +} + +void SEQFile::delLoop(uint loopID) { + if (loopID >= _loops.size()) + return; + + _loops[loopID].empty = true; + + cleanLoops(); +} + +void SEQFile::cleanLoops() { + while (!_loops.empty() && _loops.back().empty) + _loops.pop_back(); +} + +void SEQFile::abortPlay() { + _abortPlay = true; +} + +void SEQFile::handleFrameEvent() { +} + +void SEQFile::handleInput(int16 key, int16 mouseX, int16 mouseY, MouseButtons mouseButtons) { + if (_abortable && ((key != 0) || (mouseButtons != kMouseButtonsNone))) + abortPlay(); +} + +} // End of namespace Gob diff --git a/engines/gob/pregob/seqfile.h b/engines/gob/pregob/seqfile.h new file mode 100644 index 0000000000..5e12962ef9 --- /dev/null +++ b/engines/gob/pregob/seqfile.h @@ -0,0 +1,193 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GOB_PREGOB_SEQFILE_H +#define GOB_PREGOB_SEQFILE_H + +#include "common/system.h" +#include "common/array.h" +#include "common/list.h" + +#include "gob/util.h" + +namespace Common { + class String; + class SeekableReadStream; +} + +namespace Gob { + +class GobEngine; + +class DECFile; +class ANIFile; +class ANIObject; + +/** A SEQ file, describing a complex animation sequence. + * + * Used in early hardcoded gob games. + * The principle is similar to the Mult class (see mult.h), but instead + * of depending on all the externally loaded animations, backgrounds and + * objects, a SEQ file references animation and background directly by + * filename. + */ +class SEQFile { +public: + SEQFile(GobEngine *vm, const Common::String &fileName); + virtual ~SEQFile(); + + /** Play the SEQ. + * + * @param abortable If true, end playback on any user input. + * @param endFrame The frame on where to end, or 0xFFFF for infinite playback. + * @param frameRate The frame rate at which to play the SEQ, or 0 for playing at + * the speed the SEQ itself wants to. + */ + void play(bool abortable = true, uint16 endFrame = 0xFFFF, uint16 frameRate = 0); + + +protected: + GobEngine *_vm; + + + /** Returns the current frame number. */ + uint16 getFrame() const; + + /** Seek to a specific frame. */ + void seekFrame(uint16 frame); + + /** Add a frame loop. */ + uint addLoop(uint16 startFrame, uint16 endFrame, uint16 loopCount); + + /** Skip a frame loop. */ + void skipLoop(uint loopID); + + /** Delete a frame loop. */ + void delLoop(uint loopID); + + /** Ends SEQ playback. */ + void abortPlay(); + + /** Callback for special frame events. */ + virtual void handleFrameEvent(); + /** Callback for special user input handling. */ + virtual void handleInput(int16 key, int16 mouseX, int16 mouseY, MouseButtons mouseButtons); + + +private: + /** Number of animation objects that are visible at the same time. */ + static const uint kObjectCount = 4; + + /** A key for changing the background. */ + struct BackgroundKey { + uint16 frame; ///< Frame the change is to happen. + + const DECFile *background; ///< The background to use. + }; + + /** A key for playing an object animation. */ + struct AnimationKey { + uint object; ///< The object this key belongs to. + + uint16 frame; ///< Frame the change is to happen. + + const ANIFile *ani; ///< The ANI to use. + + uint16 animation; ///< The animation to use. + + int16 x; ///< X position of the animation. + int16 y; ///< Y position of the animation. + + int16 order; ///< Used to determine in which order to draw the objects. + }; + + /** A managed animation object. */ + struct Object { + ANIObject *object; ///< The actual animation object. + + int16 order; ///< The current drawing order. + }; + + /** A frame loop. */ + struct Loop { + uint16 startFrame; + uint16 endFrame; + + uint16 loopCount; + uint16 currentLoop; + + bool empty; + }; + + typedef Common::Array<DECFile *> Backgrounds; + typedef Common::Array<ANIFile *> Animations; + + typedef Common::Array<BackgroundKey> BackgroundKeys; + typedef Common::Array<AnimationKey> AnimationKeys; + + typedef Common::List<Object> Objects; + + typedef Common::Array<Loop> Loops; + + + uint16 _frame; ///< The current frame. + bool _abortPlay; ///< Was the end of the playback requested? + + uint16 _frameRate; + + Backgrounds _backgrounds; ///< All backgrounds in this SEQ. + Animations _animations; ///< All animations in this SEQ. + + BackgroundKeys _bgKeys; ///< The background change keyframes. + AnimationKeys _animKeys; ///< The animation change keyframes. + + Object _objects[kObjectCount]; ///< The managed animation objects. + + Loops _loops; + + /** Whether the playback should be abortable by user input. */ + bool _abortable; + + + // -- Loading helpers -- + + void load(Common::SeekableReadStream &seq); + + const ANIFile *findANI(uint16 index, uint16 &animation); + + // -- Playback helpers -- + + void playFrame(); + + /** Get a list of objects ordered by drawing order. */ + Objects getOrderedObjects(); + + void clearAnims(); ///< Remove all animation frames. + void drawAnims(); ///< Draw the animation frames. + + /** Look if we can compact the loop array. */ + void cleanLoops(); +}; + +} // End of namespace Gob + +#endif // GOB_PREGOB_SEQFILE_H diff --git a/engines/gob/pregob/txtfile.cpp b/engines/gob/pregob/txtfile.cpp new file mode 100644 index 0000000000..3ff0d4b039 --- /dev/null +++ b/engines/gob/pregob/txtfile.cpp @@ -0,0 +1,232 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public 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/stream.h" + +#include "gob/draw.h" + +#include "gob/pregob/txtfile.h" + +namespace Gob { + +TXTFile::TXTFile(Common::SeekableReadStream &txt, Format format) { + load(txt, format); +} + +TXTFile::~TXTFile() { +} + +TXTFile::LineArray &TXTFile::getLines() { + return _lines; +} + +void TXTFile::load(Common::SeekableReadStream &txt, Format format) { + if (format == kFormatStringPositionColorFont) { + int numLines = getInt(txt); + + _lines.reserve(numLines); + } + + while (!txt.eos()) { + Line line; + + line.text = getStr(txt); + line.x = (format >= kFormatStringPosition) ? getInt(txt) : 0; + line.y = (format >= kFormatStringPosition) ? getInt(txt) : 0; + line.color = (format >= kFormatStringPositionColor) ? getInt(txt) : 0; + line.font = (format >= kFormatStringPositionColorFont) ? getInt(txt) : 0; + + _lines.push_back(line); + } + + while (!_lines.empty() && _lines.back().text.empty()) + _lines.pop_back(); +} + +bool TXTFile::draw(Surface &surface, int16 &left, int16 &top, int16 &right, int16 &bottom, + const Font * const *fonts, uint fontCount, int color) { + + trashBuffer(); + + if (!getArea(left, top, right, bottom, fonts, fontCount)) + return false; + + resizeBuffer(right - left + 1, bottom - top + 1); + saveScreen(surface, left, top, right, bottom); + + for (LineArray::const_iterator l = _lines.begin(); l != _lines.end(); ++l) { + if (l->font >= fontCount) + continue; + + fonts[l->font]->drawString(l->text, l->x, l->y, (color < 0) ? l->color : color, 0, true, surface); + } + + return true; +} + +bool TXTFile::draw(uint line, Surface &surface, int16 &left, int16 &top, int16 &right, int16 &bottom, + const Font * const *fonts, uint fontCount, int color) { + + trashBuffer(); + + if (!getArea(line, left, top, right, bottom, fonts, fontCount)) + return false; + + resizeBuffer(right - left + 1, bottom - top + 1); + saveScreen(surface, left, top, right, bottom); + + const Line &l = _lines[line]; + + fonts[l.font]->drawString(l.text, l.x, l.y, (color < 0) ? l.color : color, 0, true, surface); + + return true; +} + +bool TXTFile::draw(Surface &surface, const Font * const *fonts, uint fontCount, int color) { + int16 left, top, right, bottom; + + return draw(surface, left, top, right, bottom, fonts, fontCount, color); +} + +bool TXTFile::draw(uint line, Surface &surface, const Font * const *fonts, uint fontCount, int color) { + int16 left, top, right, bottom; + + return draw(line, surface, left, top, right, bottom, fonts, fontCount, color); +} + +bool TXTFile::clear(Surface &surface, int16 &left, int16 &top, int16 &right, int16 &bottom) { + return restoreScreen(surface, left, top, right, bottom); +} + +bool TXTFile::getArea(int16 &left, int16 &top, int16 &right, int16 &bottom, + const Font * const *fonts, uint fontCount) const { + + bool hasLine = false; + + left = 0x7FFF; + top = 0x7FFF; + right = 0x0000; + bottom = 0x0000; + + for (uint i = 0; i < _lines.size(); i++) { + int16 lLeft, lTop, lRight, lBottom; + + if (getArea(i, lLeft, lTop, lRight, lBottom, fonts, fontCount)) { + left = MIN(left , lLeft ); + top = MIN(top , lTop ); + right = MAX(right , lRight ); + bottom = MAX(bottom, lBottom); + + hasLine = true; + } + } + + return hasLine; +} + +bool TXTFile::getArea(uint line, int16 &left, int16 &top, int16 &right, int16 &bottom, + const Font * const *fonts, uint fontCount) const { + + + if ((line >= _lines.size()) || (_lines[line].font >= fontCount)) + return false; + + const Line &l = _lines[line]; + + left = l.x; + top = l.y; + right = l.x + l.text.size() * fonts[l.font]->getCharWidth() - 1; + bottom = l.y + fonts[l.font]->getCharHeight() - 1; + + return true; +} + +Common::String TXTFile::getStr(Common::SeekableReadStream &txt) { + // Skip all ' ', '\n' and '\r' + while (!txt.eos()) { + char c = txt.readByte(); + if (txt.eos()) + break; + + if ((c != ' ') && (c != '\n') && (c != '\r')) { + txt.seek(-1, SEEK_CUR); + break; + } + } + + if (txt.eos()) + return ""; + + // Read string until ' ', '\n' or '\r' + Common::String string; + while (!txt.eos()) { + char c = txt.readByte(); + if ((c == ' ') || (c == '\n') || (c == '\r')) + break; + + string += c; + } + + // Replace all '#' with ' ' and throw out non-printables + Common::String cleanString; + + for (uint i = 0; i < string.size(); i++) { + if (string[i] == '#') + cleanString += ' '; + else if ((unsigned char)string[i] >= ' ') + cleanString += string[i]; + } + + return cleanString; +} + +int TXTFile::getInt(Common::SeekableReadStream &txt) { + // Skip all [^-0-9] + while (!txt.eos()) { + char c = txt.readByte(); + if (txt.eos()) + break; + + if ((c == '-') || ((c >= '0') && (c <= '9'))) { + txt.seek(-1, SEEK_CUR); + break; + } + } + + if (txt.eos()) + return 0; + + // Read until [^-0-9] + Common::String string; + while (!txt.eos()) { + char c = txt.readByte(); + if ((c != '-') && ((c < '0') || (c > '9'))) + break; + + string += c; + } + + // Convert to integer + return atoi(string.c_str()); +} + +} // End of namespace Gob diff --git a/engines/gob/pregob/txtfile.h b/engines/gob/pregob/txtfile.h new file mode 100644 index 0000000000..c623b58859 --- /dev/null +++ b/engines/gob/pregob/txtfile.h @@ -0,0 +1,91 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public 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 GOB_PREGOB_TXTFILE_H +#define GOB_PREGOB_TXTFILE_H + +#include "common/system.h" +#include "common/str.h" +#include "common/array.h" + +#include "gob/backbuffer.h" + +namespace Common { + class SeekableReadStream; +} + +namespace Gob { + +class Surface; +class Font; + +class TXTFile : public BackBuffer { +public: + enum Format { + kFormatString, + kFormatStringPosition, + kFormatStringPositionColor, + kFormatStringPositionColorFont + }; + + struct Line { + Common::String text; + int x, y; + int color; + uint font; + }; + + typedef Common::Array<Line> LineArray; + + TXTFile(Common::SeekableReadStream &txt, Format format); + ~TXTFile(); + + LineArray &getLines(); + + bool draw( Surface &surface, const Font * const *fonts, uint fontCount, int color = -1); + bool draw(uint line, Surface &surface, const Font * const *fonts, uint fontCount, int color = -1); + + bool draw( Surface &surface, int16 &left, int16 &top, int16 &right, int16 &bottom, + const Font * const *fonts, uint fontCount, int color = -1); + bool draw(uint line, Surface &surface, int16 &left, int16 &top, int16 &right, int16 &bottom, + const Font * const *fonts, uint fontCount, int color = -1); + + bool clear(Surface &surface, int16 &left, int16 &top, int16 &right, int16 &bottom); + +private: + LineArray _lines; + + void load(Common::SeekableReadStream &txt, Format format); + + Common::String getStr(Common::SeekableReadStream &txt); + int getInt(Common::SeekableReadStream &txt); + + + bool getArea( int16 &left, int16 &top, int16 &right, int16 &bottom, + const Font * const *fonts, uint fontCount) const; + bool getArea(uint line, int16 &left, int16 &top, int16 &right, int16 &bottom, + const Font * const *fonts, uint fontCount) const; +}; + +} // End of namespace Gob + +#endif // GOB_PREGOB_TXTFILE_H diff --git a/engines/gob/rxyfile.cpp b/engines/gob/rxyfile.cpp index 9702dc8c7f..2ff8c121cd 100644 --- a/engines/gob/rxyfile.cpp +++ b/engines/gob/rxyfile.cpp @@ -21,12 +21,19 @@ */ #include "common/stream.h" +#include "common/substream.h" #include "gob/rxyfile.h" namespace Gob { RXYFile::RXYFile(Common::SeekableReadStream &rxy) : _width(0), _height(0) { + Common::SeekableSubReadStreamEndian sub(&rxy, 0, rxy.size(), false, DisposeAfterUse::NO); + + load(sub); +} + +RXYFile::RXYFile(Common::SeekableSubReadStreamEndian &rxy) : _width(0), _height(0) { load(rxy); } @@ -64,22 +71,22 @@ const RXYFile::Coordinates &RXYFile::operator[](uint i) const { return _coords[i]; } -void RXYFile::load(Common::SeekableReadStream &rxy) { +void RXYFile::load(Common::SeekableSubReadStreamEndian &rxy) { if (rxy.size() < 2) return; rxy.seek(0); - _realCount = rxy.readUint16LE(); + _realCount = rxy.readUint16(); uint16 count = (rxy.size() - 2) / 8; _coords.resize(count); for (CoordArray::iterator c = _coords.begin(); c != _coords.end(); ++c) { - c->left = rxy.readUint16LE(); - c->right = rxy.readUint16LE(); - c->top = rxy.readUint16LE(); - c->bottom = rxy.readUint16LE(); + c->left = rxy.readUint16(); + c->right = rxy.readUint16(); + c->top = rxy.readUint16(); + c->bottom = rxy.readUint16(); if (c->left != 0xFFFF) { _width = MAX<uint16>(_width , c->right + 1); diff --git a/engines/gob/rxyfile.h b/engines/gob/rxyfile.h index bc9600b5b0..4fd46c5e40 100644 --- a/engines/gob/rxyfile.h +++ b/engines/gob/rxyfile.h @@ -28,6 +28,7 @@ namespace Common { class SeekableReadStream; + class SeekableSubReadStreamEndian; } namespace Gob { @@ -46,6 +47,7 @@ public: }; RXYFile(Common::SeekableReadStream &rxy); + RXYFile(Common::SeekableSubReadStreamEndian &rxy); RXYFile(uint16 width, uint16 height); ~RXYFile(); @@ -71,7 +73,7 @@ private: uint16 _height; - void load(Common::SeekableReadStream &rxy); + void load(Common::SeekableSubReadStreamEndian &rxy); }; } // End of namespace Gob diff --git a/engines/gob/sound/sound.cpp b/engines/gob/sound/sound.cpp index 403bd632a1..63af6aeef4 100644 --- a/engines/gob/sound/sound.cpp +++ b/engines/gob/sound/sound.cpp @@ -109,7 +109,7 @@ int Sound::sampleGetNextFreeSlot() const { return -1; } -bool Sound::sampleLoad(SoundDesc *sndDesc, SoundType type, const char *fileName, bool tryExist) { +bool Sound::sampleLoad(SoundDesc *sndDesc, SoundType type, const char *fileName) { if (!sndDesc) return false; @@ -117,12 +117,15 @@ bool Sound::sampleLoad(SoundDesc *sndDesc, SoundType type, const char *fileName, int32 size; byte *data = _vm->_dataIO->getFile(fileName, size); - if (!data) { - warning("Can't open sample file \"%s\"", fileName); + + if (!data || !sndDesc->load(type, data, size)) { + delete data; + + warning("Sound::sampleLoad(): Failed to load sound \"%s\"", fileName); return false; } - return sndDesc->load(type, data, size); + return true; } void Sound::sampleFree(SoundDesc *sndDesc, bool noteAdLib, int index) { @@ -458,7 +461,7 @@ void Sound::blasterStop(int16 fadeLength, SoundDesc *sndDesc) { _blaster->stopSound(fadeLength, sndDesc); } -void Sound::blasterPlayComposition(int16 *composition, int16 freqVal, +void Sound::blasterPlayComposition(const int16 *composition, int16 freqVal, SoundDesc *sndDescs, int8 sndCount) { if (!_blaster) return; diff --git a/engines/gob/sound/sound.h b/engines/gob/sound/sound.h index 6ad0ec5483..bbc182d172 100644 --- a/engines/gob/sound/sound.h +++ b/engines/gob/sound/sound.h @@ -51,7 +51,7 @@ public: const SoundDesc *sampleGetBySlot(int slot) const; int sampleGetNextFreeSlot() const; - bool sampleLoad(SoundDesc *sndDesc, SoundType type, const char *fileName, bool tryExist = true); + bool sampleLoad(SoundDesc *sndDesc, SoundType type, const char *fileName); void sampleFree(SoundDesc *sndDesc, bool noteAdLib = false, int index = -1); @@ -60,7 +60,7 @@ public: int16 frequency, int16 fadeLength = 0); void blasterStop(int16 fadeLength, SoundDesc *sndDesc = 0); - void blasterPlayComposition(int16 *composition, int16 freqVal, + void blasterPlayComposition(const int16 *composition, int16 freqVal, SoundDesc *sndDescs = 0, int8 sndCount = kSoundsCount); void blasterStopComposition(); void blasterRepeatComposition(int32 repCount); diff --git a/engines/gob/sound/soundblaster.cpp b/engines/gob/sound/soundblaster.cpp index 19c2346448..f267eee32d 100644 --- a/engines/gob/sound/soundblaster.cpp +++ b/engines/gob/sound/soundblaster.cpp @@ -88,7 +88,7 @@ void SoundBlaster::nextCompositionPos() { _compositionPos = -1; } -void SoundBlaster::playComposition(int16 *composition, int16 freqVal, +void SoundBlaster::playComposition(const int16 *composition, int16 freqVal, SoundDesc *sndDescs, int8 sndCount) { _compositionSamples = sndDescs; diff --git a/engines/gob/sound/soundblaster.h b/engines/gob/sound/soundblaster.h index c740ba2269..3c4968d611 100644 --- a/engines/gob/sound/soundblaster.h +++ b/engines/gob/sound/soundblaster.h @@ -41,7 +41,7 @@ public: int16 frequency, int16 fadeLength = 0); void stopSound(int16 fadeLength, SoundDesc *sndDesc = 0); - void playComposition(int16 *composition, int16 freqVal, + void playComposition(const int16 *composition, int16 freqVal, SoundDesc *sndDescs = 0, int8 sndCount = 60); void stopComposition(); void endComposition(); diff --git a/engines/gob/surface.cpp b/engines/gob/surface.cpp index 3eaf741be2..afbb7c3bae 100644 --- a/engines/gob/surface.cpp +++ b/engines/gob/surface.cpp @@ -684,6 +684,12 @@ void Surface::shadeRect(uint16 left, uint16 top, uint16 right, uint16 bottom, } +void Surface::recolor(uint8 from, uint8 to) { + for (Pixel p = get(); p.isValid(); ++p) + if (p.get() == from) + p.set(to); +} + void Surface::putPixel(uint16 x, uint16 y, uint32 color) { if ((x >= _width) || (y >= _height)) return; diff --git a/engines/gob/surface.h b/engines/gob/surface.h index 09be8d1a49..8f895a7910 100644 --- a/engines/gob/surface.h +++ b/engines/gob/surface.h @@ -156,6 +156,8 @@ public: void shadeRect(uint16 left, uint16 top, uint16 right, uint16 bottom, uint32 color, uint8 strength); + void recolor(uint8 from, uint8 to); + void putPixel(uint16 x, uint16 y, uint32 color); void drawLine(uint16 x0, uint16 y0, uint16 x1, uint16 y1, uint32 color); void drawRect(uint16 left, uint16 top, uint16 right, uint16 bottom, uint32 color); diff --git a/engines/gob/util.cpp b/engines/gob/util.cpp index 64dfcf9b12..5ac4ef024e 100644 --- a/engines/gob/util.cpp +++ b/engines/gob/util.cpp @@ -189,12 +189,27 @@ bool Util::getKeyFromBuffer(Common::KeyState &key) { return true; } +static const uint16 kLatin1ToCP850[] = { + 0xFF, 0xAD, 0xBD, 0x9C, 0xCF, 0xBE, 0xDD, 0xF5, 0xF9, 0xB8, 0xA6, 0xAE, 0xAA, 0xF0, 0xA9, 0xEE, + 0xF8, 0xF1, 0xFD, 0xFC, 0xEF, 0xE6, 0xF4, 0xFA, 0xF7, 0xFB, 0xA7, 0xAF, 0xAC, 0xAB, 0xF3, 0xA8, + 0xB7, 0xB5, 0xB6, 0xC7, 0x8E, 0x8F, 0x92, 0x80, 0xD4, 0x90, 0xD2, 0xD3, 0xDE, 0xD6, 0xD7, 0xD8, + 0xD1, 0xA5, 0xE3, 0xE0, 0xE2, 0xE5, 0x99, 0x9E, 0x9D, 0xEB, 0xE9, 0xEA, 0x9A, 0xED, 0xE8, 0xE1, + 0x85, 0xA0, 0x83, 0xC6, 0x84, 0x86, 0x91, 0x87, 0x8A, 0x82, 0x88, 0x89, 0x8D, 0xA1, 0x8C, 0x8B, + 0xD0, 0xA4, 0x95, 0xA2, 0x93, 0xE4, 0x94, 0xF6, 0x9B, 0x97, 0xA3, 0x96, 0x81, 0xEC, 0xE7, 0x98 +}; + +int16 Util::toCP850(uint16 latin1) { + if ((latin1 < 0xA0) || ((latin1 - 0xA0) >= ARRAYSIZE(kLatin1ToCP850))) + return 0; + + return kLatin1ToCP850[latin1 - 0xA0]; +} + int16 Util::translateKey(const Common::KeyState &key) { static struct keyS { int16 from; int16 to; } keys[] = { - {Common::KEYCODE_INVALID, kKeyNone }, {Common::KEYCODE_BACKSPACE, kKeyBackspace}, {Common::KEYCODE_SPACE, kKeySpace }, {Common::KEYCODE_RETURN, kKeyReturn }, @@ -216,20 +231,88 @@ int16 Util::translateKey(const Common::KeyState &key) { {Common::KEYCODE_F10, kKeyF10 } }; + // Translate special keys for (int i = 0; i < ARRAYSIZE(keys); i++) if (key.keycode == keys[i].from) return keys[i].to; - if ((key.keycode >= Common::KEYCODE_SPACE) && - (key.keycode <= Common::KEYCODE_DELETE)) { - - // Used as a user input in Gobliins 2 notepad, in the save dialog, ... + // Return the ascii value, for text input + if ((key.ascii >= 32) && (key.ascii <= 127)) return key.ascii; - } + + // Translate international characters into CP850 characters + if ((key.ascii >= 160) && (key.ascii <= 255)) + return toCP850(key.ascii); return 0; } +static const uint8 kLowerToUpper[][2] = { + {0x81, 0x9A}, + {0x82, 0x90}, + {0x83, 0xB6}, + {0x84, 0x8E}, + {0x85, 0xB7}, + {0x86, 0x8F}, + {0x87, 0x80}, + {0x88, 0xD2}, + {0x89, 0xD3}, + {0x8A, 0xD4}, + {0x8B, 0xD8}, + {0x8C, 0xD7}, + {0x8D, 0xDE}, + {0x91, 0x92}, + {0x93, 0xE2}, + {0x94, 0x99}, + {0x95, 0xE3}, + {0x96, 0xEA}, + {0x97, 0xEB}, + {0x95, 0xE3}, + {0x96, 0xEA}, + {0x97, 0xEB}, + {0x9B, 0x9D}, + {0xA0, 0xB5}, + {0xA1, 0xD6}, + {0xA2, 0xE0}, + {0xA3, 0xE9}, + {0xA4, 0xA5}, + {0xC6, 0xC7}, + {0xD0, 0xD1}, + {0xE4, 0xE5}, + {0xE7, 0xE8}, + {0xEC, 0xED} +}; + +char Util::toCP850Lower(char cp850) { + const uint8 cp = (unsigned char)cp850; + if (cp <= 32) + return cp850; + + if (cp <= 127) + return tolower(cp850); + + for (uint i = 0; i < ARRAYSIZE(kLowerToUpper); i++) + if (cp == kLowerToUpper[i][1]) + return (char)kLowerToUpper[i][0]; + + return cp850; +} + +char Util::toCP850Upper(char cp850) { + const uint8 cp = (unsigned char)cp850; + if (cp <= 32) + return cp850; + + if (cp <= 127) + return toupper(cp850); + + for (uint i = 0; i < ARRAYSIZE(kLowerToUpper); i++) + if (cp == kLowerToUpper[i][0]) + return (char)kLowerToUpper[i][1]; + + return cp850; +} + int16 Util::getKey() { Common::KeyState key; @@ -367,21 +450,29 @@ void Util::notifyNewAnim() { _startFrameTime = getTimeKey(); } -void Util::waitEndFrame() { +void Util::waitEndFrame(bool handleInput) { int32 time; - _vm->_video->waitRetrace(); - time = getTimeKey() - _startFrameTime; if ((time > 1000) || (time < 0)) { + _vm->_video->retrace(); _startFrameTime = getTimeKey(); return; } - int32 toWait = _frameWaitTime - time; + int32 toWait = 0; + do { + if (toWait > 0) + delay(MIN<int>(toWait, 10)); + + if (handleInput) + processInput(); + + _vm->_video->retrace(); - if (toWait > 0) - delay(toWait); + time = getTimeKey() - _startFrameTime; + toWait = _frameWaitTime - time; + } while (toWait > 0); _startFrameTime = getTimeKey(); } diff --git a/engines/gob/util.h b/engines/gob/util.h index b26a78ab2c..a4984c6207 100644 --- a/engines/gob/util.h +++ b/engines/gob/util.h @@ -124,7 +124,7 @@ public: int16 getFrameRate(); void setFrameRate(int16 rate); void notifyNewAnim(); - void waitEndFrame(); + void waitEndFrame(bool handleInput = true); void setScrollOffset(int16 x = -1, int16 y = -1); static void insertStr(const char *str1, char *str2, int16 pos); @@ -143,6 +143,11 @@ public: /** Read a constant-length string out of a stream. */ static Common::String readString(Common::SeekableReadStream &stream, int n); + /** Convert a character in CP850 encoding to the equivalent lower case character. */ + static char toCP850Lower(char cp850); + /** Convert a character in CP850 encoding to the equivalent upper case character. */ + static char toCP850Upper(char cp850); + Util(GobEngine *vm); protected: @@ -166,6 +171,7 @@ protected: void addKeyToBuffer(const Common::KeyState &key); bool getKeyFromBuffer(Common::KeyState &key); int16 translateKey(const Common::KeyState &key); + int16 toCP850(uint16 latin1); void checkJoystick(); void keyDown(const Common::Event &event); diff --git a/engines/gob/video.cpp b/engines/gob/video.cpp index 3b1c6423bb..64af34cf62 100644 --- a/engines/gob/video.cpp +++ b/engines/gob/video.cpp @@ -84,6 +84,10 @@ uint16 Font::getCharCount() const { return _endItem - _startItem + 1; } +bool Font::hasChar(uint8 c) const { + return (c >= _startItem) && (c <= _endItem); +} + bool Font::isMonospaced() const { return _charWidths == 0; } @@ -134,6 +138,23 @@ void Font::drawLetter(Surface &surf, uint8 c, uint16 x, uint16 y, } } +void Font::drawString(const Common::String &str, int16 x, int16 y, int16 color1, int16 color2, + bool transp, Surface &dest) const { + + const char *s = str.c_str(); + + while (*s != '\0') { + const int16 charRight = x + getCharWidth(*s); + const int16 charBottom = y + getCharHeight(); + + if ((x >= 0) && (y >= 0) && (charRight <= dest.getWidth()) && (charBottom <= dest.getHeight())) + drawLetter(dest, *s, x, y, color1, color2, transp); + + x += getCharWidth(*s); + s++; + } +} + const byte *Font::getCharData(uint8 c) const { if (_endItem == 0) { warning("Font::getCharData(): _endItem == 0"); @@ -225,7 +246,7 @@ void Video::setSize(bool defaultTo1XScaler) { void Video::retrace(bool mouse) { if (mouse) - CursorMan.showMouse((_vm->_draw->_showCursor & 2) != 0); + CursorMan.showMouse((_vm->_draw->_showCursor & 6) != 0); if (_vm->_global->_primarySurfDesc) { int screenX = _screenDeltaX; @@ -332,6 +353,10 @@ void Video::drawPackedSprite(byte *sprBuf, int16 width, int16 height, void Video::drawPackedSprite(const char *path, Surface &dest, int width) { int32 size; byte *data = _vm->_dataIO->getFile(path, size); + if (!data) { + warning("Video::drawPackedSprite(): Failed to open sprite \"%s\"", path); + return; + } drawPackedSprite(data, width, dest.getHeight(), 0, 0, 0, dest); delete[] data; diff --git a/engines/gob/video.h b/engines/gob/video.h index ecbb579c5f..122c1e47d5 100644 --- a/engines/gob/video.h +++ b/engines/gob/video.h @@ -41,11 +41,16 @@ public: uint8 getCharWidth () const; uint8 getCharHeight() const; + bool hasChar(uint8 c) const; + bool isMonospaced() const; void drawLetter(Surface &surf, uint8 c, uint16 x, uint16 y, uint32 color1, uint32 color2, bool transp) const; + void drawString(const Common::String &str, int16 x, int16 y, int16 color1, int16 color2, + bool transp, Surface &dest) const; + private: const byte *_dataPtr; const byte *_data; diff --git a/engines/groovie/saveload.cpp b/engines/groovie/saveload.cpp index 14e7a09cb2..1a92c02e0e 100644 --- a/engines/groovie/saveload.cpp +++ b/engines/groovie/saveload.cpp @@ -103,8 +103,6 @@ Common::InSaveFile *SaveLoad::openForLoading(const Common::String &target, int s if (descriptor) { // Initialize the SaveStateDescriptor descriptor->setSaveSlot(slot); - descriptor->setDeletableFlag(true); - descriptor->setWriteProtectedFlag(false); // TODO: Add extra information //setSaveDate(int year, int month, int day) diff --git a/engines/hugo/detection.cpp b/engines/hugo/detection.cpp index 90708163f5..bb5944acc8 100644 --- a/engines/hugo/detection.cpp +++ b/engines/hugo/detection.cpp @@ -244,9 +244,6 @@ SaveStateDescriptor HugoMetaEngine::querySaveMetaInfos(const char *target, int s Graphics::Surface *const thumbnail = Graphics::loadThumbnail(*file); desc.setThumbnail(thumbnail); - desc.setDeletableFlag(true); - desc.setWriteProtectedFlag(false); - uint32 saveDate = file->readUint32BE(); uint16 saveTime = file->readUint16BE(); diff --git a/engines/kyra/detection_tables.h b/engines/kyra/detection_tables.h index 79ef11e7a9..e2162f20e2 100644 --- a/engines/kyra/detection_tables.h +++ b/engines/kyra/detection_tables.h @@ -1329,6 +1329,22 @@ const KYRAGameDescription adGameDescs[] = { LOL_FLOPPY_CMP_FLAGS }, + { // French floppy version 1.20, bug #3552534 "KYRA: LOL Floppy FR version unknown" + { + "lol", + 0, + { + { "WESTWOOD.1", 0, "43857e24d1fc6731f3b13d9ed6db8c3a", -1 }, + { 0, 0, 0, 0 } + }, + Common::FR_FRA, + Common::kPlatformPC, + ADGF_NO_FLAGS, + GUIO8(GUIO_NOSPEECH, GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_MIDIGM, GUIO_MIDIPCSPK, GUIO_RENDERVGA, GAMEOPTION_LOL_SCROLLING, GAMEOPTION_LOL_CURSORS) + }, + LOL_FLOPPY_CMP_FLAGS + }, + { { "lol", @@ -1397,6 +1413,23 @@ const KYRAGameDescription adGameDescs[] = { LOL_FLOPPY_FLAGS }, + { // French floppy version 1.23, bug #3552534 "KYRA: LOL Floppy FR version unknown" + { + "lol", + "Extracted", + { + { "GENERAL.PAK", 0, "f4fd14f244bd7c7fa08d026fafe44cc5", -1 }, + { "CHAPTER7.PAK", 0, "733e33c8444c93843dac3b683c283eaa", -1 }, + { 0, 0, 0, 0 } + }, + Common::FR_FRA, + Common::kPlatformPC, + ADGF_NO_FLAGS, + GUIO8(GUIO_NOSPEECH, GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_MIDIGM, GUIO_MIDIPCSPK, GUIO_RENDERVGA, GAMEOPTION_LOL_SCROLLING, GAMEOPTION_LOL_CURSORS) + }, + LOL_FLOPPY_FLAGS + }, + // Russian fan translation { { diff --git a/engines/kyra/eobcommon.cpp b/engines/kyra/eobcommon.cpp index a63f123258..fadb1066e0 100644 --- a/engines/kyra/eobcommon.cpp +++ b/engines/kyra/eobcommon.cpp @@ -770,7 +770,7 @@ void EoBCoreEngine::releaseItemsAndDecorationsShapes() { if (_spellShapes) { for (int i = 0; i < 4; i++) { if (_spellShapes[i]) - delete [] _spellShapes[i]; + delete[] _spellShapes[i]; } delete[] _spellShapes; } @@ -820,7 +820,7 @@ void EoBCoreEngine::releaseItemsAndDecorationsShapes() { if (_firebeamShapes[i]) delete[] _firebeamShapes[i]; } - delete []_firebeamShapes; + delete[] _firebeamShapes; } delete[] _redSplatShape; diff --git a/engines/kyra/eobcommon.h b/engines/kyra/eobcommon.h index 050fe2b794..f60e755dd7 100644 --- a/engines/kyra/eobcommon.h +++ b/engines/kyra/eobcommon.h @@ -263,7 +263,7 @@ protected: // Main Menu, Intro, Finale virtual int mainMenu() = 0; - virtual void seq_xdeath() {}; + virtual void seq_xdeath() {} virtual void seq_playFinale() = 0; bool _playFinale; @@ -921,8 +921,8 @@ protected: void usePotion(int charIndex, int weaponSlot); void useWand(int charIndex, int weaponSlot); - virtual void turnUndeadAuto() {}; - virtual void turnUndeadAutoHit() {}; + virtual void turnUndeadAuto() {} + virtual void turnUndeadAutoHit() {} void castSpell(int spell, int weaponSlot); void removeCharacterEffect(int spell, int charIndex, int showWarning); diff --git a/engines/kyra/screen.cpp b/engines/kyra/screen.cpp index 4fd5985a09..04d805737f 100644 --- a/engines/kyra/screen.cpp +++ b/engines/kyra/screen.cpp @@ -141,7 +141,7 @@ bool Screen::init() { if (!font) error("Could not load any SJIS font, neither the original nor ScummVM's 'SJIS.FNT'"); - _fonts[FID_SJIS_FNT] = new SJISFont(this, font, _sjisInvisibleColor, _use16ColorMode, !_use16ColorMode); + _fonts[FID_SJIS_FNT] = new SJISFont(font, _sjisInvisibleColor, _use16ColorMode, !_use16ColorMode); } } @@ -3595,8 +3595,8 @@ void AMIGAFont::unload() { memset(_chars, 0, sizeof(_chars)); } -SJISFont::SJISFont(Screen *s, Graphics::FontSJIS *font, const uint8 invisColor, bool is16Color, bool outlineSize) - : _colorMap(0), _font(font), _invisColor(invisColor), _is16Color(is16Color), _screen(s) { +SJISFont::SJISFont(Graphics::FontSJIS *font, const uint8 invisColor, bool is16Color, bool outlineSize) + : _colorMap(0), _font(font), _invisColor(invisColor), _is16Color(is16Color) { assert(_font); _font->setDrawingMode(outlineSize ? Graphics::FontSJIS::kOutlineMode : Graphics::FontSJIS::kDefaultMode); diff --git a/engines/kyra/screen.h b/engines/kyra/screen.h index b064c72bb0..60bfeb3241 100644 --- a/engines/kyra/screen.h +++ b/engines/kyra/screen.h @@ -213,7 +213,7 @@ private: */ class SJISFont : public Font { public: - SJISFont(Screen *s, Graphics::FontSJIS *font, const uint8 invisColor, bool is16Color, bool outlineSize); + SJISFont(Graphics::FontSJIS *font, const uint8 invisColor, bool is16Color, bool outlineSize); ~SJISFont() { unload(); } bool usesOverlay() const { return true; } @@ -233,7 +233,6 @@ private: const uint8 _invisColor; const bool _is16Color; - const Screen *_screen; int _sjisWidth, _asciiWidth; int _fontHeight; }; diff --git a/engines/kyra/screen_eob.cpp b/engines/kyra/screen_eob.cpp index e06ca42c40..ae75c111b4 100644 --- a/engines/kyra/screen_eob.cpp +++ b/engines/kyra/screen_eob.cpp @@ -607,7 +607,7 @@ uint8 *Screen_EoB::encodeShape(uint16 x, uint16 y, uint16 w, uint16 h, bool enco srcLineStart += SCREEN_W; src = srcLineStart; } - delete [] colorMap; + delete[] colorMap; } return shp; diff --git a/engines/kyra/screen_lol.cpp b/engines/kyra/screen_lol.cpp index 08b232f400..3726b1f4b9 100644 --- a/engines/kyra/screen_lol.cpp +++ b/engines/kyra/screen_lol.cpp @@ -31,7 +31,7 @@ namespace Kyra { -Screen_LoL::Screen_LoL(LoLEngine *vm, OSystem *system) : Screen_v2(vm, system, vm->gameFlags().use16ColorMode ? _screenDimTable16C : _screenDimTable256C, _screenDimTableCount), _vm(vm) { +Screen_LoL::Screen_LoL(LoLEngine *vm, OSystem *system) : Screen_v2(vm, system, vm->gameFlags().use16ColorMode ? _screenDimTable16C : _screenDimTable256C, _screenDimTableCount) { _paletteOverlay1 = new uint8[0x100]; _paletteOverlay2 = new uint8[0x100]; _grayOverlay = new uint8[0x100]; diff --git a/engines/kyra/screen_lol.h b/engines/kyra/screen_lol.h index 3bba9f8b70..09496705bb 100644 --- a/engines/kyra/screen_lol.h +++ b/engines/kyra/screen_lol.h @@ -89,8 +89,6 @@ public: static void convertPC98Gfx(uint8 *data, int w, int h, int pitch); private: - LoLEngine *_vm; - static const ScreenDim _screenDimTable256C[]; static const ScreenDim _screenDimTable16C[]; static const int _screenDimTableCount; diff --git a/engines/kyra/sound_intern.h b/engines/kyra/sound_intern.h index 427bef66e2..827a487685 100644 --- a/engines/kyra/sound_intern.h +++ b/engines/kyra/sound_intern.h @@ -130,7 +130,6 @@ private: void fadeOutSoundEffects(); int _lastTrack; - Audio::AudioStream *_currentSFX; Audio::SoundHandle _sfxHandle; uint8 *_musicTrackData; diff --git a/engines/kyra/sound_midi.cpp b/engines/kyra/sound_midi.cpp index 0004395f6f..70cc304192 100644 --- a/engines/kyra/sound_midi.cpp +++ b/engines/kyra/sound_midi.cpp @@ -475,8 +475,8 @@ SoundMidiPC::SoundMidiPC(KyraEngine_v1 *vm, Audio::Mixer *mixer, MidiDriver *dri ::GUI::MessageDialog dialog(_("You appear to be using a General MIDI device,\n" "but your game only supports Roland MT32 MIDI.\n" "We try to map the Roland MT32 instruments to\n" - "General MIDI ones. After all it might happen\n" - "that a few tracks will not be correctly played.")); + "General MIDI ones. It is still possible that\n" + "some tracks sound incorrect.")); dialog.runModal(); } } diff --git a/engines/kyra/sound_towns.cpp b/engines/kyra/sound_towns.cpp index 2f996de1ac..4b25db33f2 100644 --- a/engines/kyra/sound_towns.cpp +++ b/engines/kyra/sound_towns.cpp @@ -34,7 +34,7 @@ namespace Kyra { SoundTowns::SoundTowns(KyraEngine_v1 *vm, Audio::Mixer *mixer) - : Sound(vm, mixer), _lastTrack(-1), _currentSFX(0), _musicTrackData(0), _sfxFileData(0), _cdaPlaying(0), + : Sound(vm, mixer), _lastTrack(-1), _musicTrackData(0), _sfxFileData(0), _cdaPlaying(0), _sfxFileIndex((uint)-1), _musicFadeTable(0), _sfxWDTable(0), _sfxBTTable(0), _sfxChannel(0x46) { _driver = new TownsEuphonyDriver(_mixer); diff --git a/engines/kyra/staticres.cpp b/engines/kyra/staticres.cpp index 423b827092..00dc4f9e13 100644 --- a/engines/kyra/staticres.cpp +++ b/engines/kyra/staticres.cpp @@ -38,7 +38,7 @@ namespace Kyra { -#define RESFILE_VERSION 82 +#define RESFILE_VERSION 83 namespace { bool checkKyraDat(Common::SeekableReadStream *file) { diff --git a/engines/lastexpress/data/animation.cpp b/engines/lastexpress/data/animation.cpp index 9d0ed532f2..7618259e69 100644 --- a/engines/lastexpress/data/animation.cpp +++ b/engines/lastexpress/data/animation.cpp @@ -32,10 +32,8 @@ #include "common/events.h" #include "common/rational.h" -#include "common/rect.h" #include "common/stream.h" #include "common/system.h" -#include "common/textconsole.h" #include "engines/engine.h" @@ -232,7 +230,7 @@ AnimFrame *Animation::processChunkFrame(Common::SeekableReadStream *in, const Ch i.read(str, false); // Decode the frame - AnimFrame *f = new AnimFrame(str, i); + AnimFrame *f = new AnimFrame(str, i, true); // Delete the temporary chunk buffer delete str; @@ -250,7 +248,7 @@ void Animation::processChunkAudio(Common::SeekableReadStream *in, const Chunk &c // Read Snd header uint32 header1 = in->readUint32LE(); uint16 header2 = in->readUint16LE(); - warning("Start ADPCM: %d, %d", header1, header2); + debugC(4, kLastExpressDebugSound, "Start ADPCM: %d, %d", header1, header2); size -= 6; } diff --git a/engines/lastexpress/data/cursor.cpp b/engines/lastexpress/data/cursor.cpp index 205c46f667..d176d963d1 100644 --- a/engines/lastexpress/data/cursor.cpp +++ b/engines/lastexpress/data/cursor.cpp @@ -128,7 +128,7 @@ Common::Rect Icon::draw(Graphics::Surface *surface) { for (int i = 0; i < 32; i++) { // Adjust brightness - if (_brightnessIndex == -1) + if (_brightnessIndex == -1 || _brightnessIndex >= ARRAYSIZE(brigthnessData)) *s = *image; else *s = (*image & brigthnessData[_brightnessIndex]) >> _brightnessIndex; diff --git a/engines/lastexpress/data/font.cpp b/engines/lastexpress/data/font.cpp index 79cf64e617..8ac1afce9a 100644 --- a/engines/lastexpress/data/font.cpp +++ b/engines/lastexpress/data/font.cpp @@ -149,7 +149,7 @@ uint8 Font::getCharWidth(uint16 c) const{ uint16 Font::getStringWidth(Common::String str) const { uint16 width = 0; for (uint i = 0; i < str.size(); i++) - width += getCharWidth((unsigned) (int)str[i]); + width += getCharWidth((unsigned char)str[i]); return width; } @@ -185,8 +185,8 @@ void Font::drawChar(Graphics::Surface *surface, int16 x, int16 y, uint16 c) { Common::Rect Font::drawString(Graphics::Surface *surface, int16 x, int16 y, Common::String str) { int16 currentX = x; for (uint i = 0; i < str.size(); i++) { - drawChar(surface, currentX, y, (unsigned) (int)str[i]); - currentX += getCharWidth((unsigned) (int)str[i]); + drawChar(surface, currentX, y, (unsigned char)str[i]); + currentX += getCharWidth((unsigned char)str[i]); } return Common::Rect(x, y, x + currentX, y + (int16)_charHeight); diff --git a/engines/lastexpress/data/scene.cpp b/engines/lastexpress/data/scene.cpp index 8f279ffbb3..fdb1ac6d46 100644 --- a/engines/lastexpress/data/scene.cpp +++ b/engines/lastexpress/data/scene.cpp @@ -28,7 +28,6 @@ #include "lastexpress/lastexpress.h" #include "lastexpress/resource.h" -#include "common/textconsole.h" #include "common/stream.h" namespace LastExpress { @@ -122,7 +121,7 @@ bool SceneHotspot::isInside(const Common::Point &point) { // Scene Scene::~Scene() { // Free the hotspots - for (int i = 0; i < (int)_hotspots.size(); i++) + for (uint i = 0; i < _hotspots.size(); i++) delete _hotspots[i]; } @@ -172,7 +171,7 @@ bool Scene::checkHotSpot(const Common::Point &coord, SceneHotspot **hotspot) { bool found = false; int _location = 0; - for (int i = 0; i < (int)_hotspots.size(); i++) { + for (uint i = 0; i < _hotspots.size(); i++) { if (_hotspots[i]->isInside(coord)) { if (_location <= _hotspots[i]->location) { _location = _hotspots[i]->location; @@ -224,7 +223,7 @@ Common::String Scene::toString() { // Hotspots if (_hotspots.size() != 0) { output += "\nHotspots:\n"; - for (int i = 0; i < (int)_hotspots.size(); i++) + for (uint i = 0; i < _hotspots.size(); i++) output += _hotspots[i]->toString() + "\n"; } @@ -241,7 +240,7 @@ SceneLoader::~SceneLoader() { void SceneLoader::clear() { // Remove all scenes - for (int i = 0; i < (int)_scenes.size(); i++) + for (uint i = 0; i < _scenes.size(); i++) delete _scenes[i]; _scenes.clear(); @@ -292,9 +291,9 @@ Scene *SceneLoader::get(SceneIndex index) { return NULL; // Load the hotspots if needed - _scenes[(int)index]->loadHotspots(_stream); + _scenes[(uint)index]->loadHotspots(_stream); - return _scenes[(int)index]; + return _scenes[(uint)index]; } } // End of namespace LastExpress diff --git a/engines/lastexpress/data/sequence.cpp b/engines/lastexpress/data/sequence.cpp index a62348f6c0..a5bcba84cd 100644 --- a/engines/lastexpress/data/sequence.cpp +++ b/engines/lastexpress/data/sequence.cpp @@ -27,7 +27,6 @@ #include "lastexpress/debug.h" #include "common/stream.h" -#include "common/textconsole.h" namespace LastExpress { @@ -77,7 +76,7 @@ void FrameInfo::read(Common::SeekableReadStream *in, bool isSequence) { // AnimFrame -AnimFrame::AnimFrame(Common::SeekableReadStream *in, const FrameInfo &f) : _palette(NULL) { +AnimFrame::AnimFrame(Common::SeekableReadStream *in, const FrameInfo &f, bool ignoreSubtype) : _palette(NULL), _ignoreSubtype(ignoreSubtype) { _palSize = 1; // TODO: use just the needed rectangle _image.create(640, 480, Graphics::PixelFormat::createFormatCLUT8()); diff --git a/engines/lastexpress/data/sequence.h b/engines/lastexpress/data/sequence.h index 9987eae48e..610a55cebf 100644 --- a/engines/lastexpress/data/sequence.h +++ b/engines/lastexpress/data/sequence.h @@ -49,8 +49,9 @@ byte {1} - Compression type byte {1} - Subtype (determines which set of decompression functions will be called) => 0, 1, 2, 3 byte {1} - Unknown + byte {1} - Keep previous frame while drawing + byte {1} - Unknown byte {1} - Unknown - uint16 {2} - Unknown byte {1} - Sound action byte {1} - Unknown uint32 {4} - positionId @@ -129,7 +130,7 @@ struct FrameInfo { class AnimFrame : public Drawable { public: - AnimFrame(Common::SeekableReadStream *in, const FrameInfo &f); + AnimFrame(Common::SeekableReadStream *in, const FrameInfo &f, bool ignoreSubtype = false); ~AnimFrame(); Common::Rect draw(Graphics::Surface *s); @@ -146,6 +147,7 @@ private: uint16 _palSize; uint16 *_palette; Common::Rect _rect; + bool _ignoreSubtype; }; class Sequence { diff --git a/engines/lastexpress/data/snd.cpp b/engines/lastexpress/data/snd.cpp index a9bee6155d..a77e4a06c6 100644 --- a/engines/lastexpress/data/snd.cpp +++ b/engines/lastexpress/data/snd.cpp @@ -28,11 +28,9 @@ #include "lastexpress/debug.h" #include "audio/decoders/adpcm_intern.h" -#include "audio/audiostream.h" #include "common/debug.h" #include "common/memstream.h" #include "common/system.h" -#include "common/textconsole.h" namespace LastExpress { @@ -358,6 +356,8 @@ public: Audio::ADPCMStream(stream, disposeAfterUse, size, 44100, 1, blockSize) { _currentFilterId = -1; _nextFilterId = filterId; + _stepAdjust1 = 0; + _stepAdjust2 = 0; } int readBuffer(int16 *buffer, const int numSamples) { @@ -378,7 +378,7 @@ public: // Get current filter _currentFilterId = _nextFilterId; - _nextFilterId = -1; + //_nextFilterId = -1; // FIXME: the filter id should be recomputed based on the sound entry status for each block // No filter: skip decoding if (_currentFilterId == -1) @@ -455,7 +455,9 @@ void SimpleSound::play(Audio::AudioStream *as) { ////////////////////////////////////////////////////////////////////////// StreamedSound::StreamedSound() : _as(NULL), _loaded(false) {} -StreamedSound::~StreamedSound() {} +StreamedSound::~StreamedSound() { + _as = NULL; +} bool StreamedSound::load(Common::SeekableReadStream *stream, int32 filterId) { if (!stream) @@ -484,6 +486,9 @@ bool StreamedSound::isFinished() { } void StreamedSound::setFilterId(int32 filterId) { + if (!_as) + return; + ((LastExpress_ADPCMStream *)_as)->setFilterId(filterId); } @@ -521,6 +526,7 @@ void AppendableSound::queueBuffer(Common::SeekableReadStream *bufferIn) { // Setup the ADPCM decoder uint32 sizeIn = (uint32)bufferIn->size(); Audio::AudioStream *adpcm = makeDecoder(bufferIn, sizeIn); + ((LastExpress_ADPCMStream *)adpcm)->setFilterId(16); // Queue the stream _as->queueAudioStream(adpcm); diff --git a/engines/lastexpress/data/subtitle.cpp b/engines/lastexpress/data/subtitle.cpp index 0be832cbdd..a9a8284588 100644 --- a/engines/lastexpress/data/subtitle.cpp +++ b/engines/lastexpress/data/subtitle.cpp @@ -32,7 +32,6 @@ #include "common/debug.h" #include "common/rect.h" #include "common/stream.h" -#include "common/textconsole.h" namespace LastExpress { @@ -151,7 +150,7 @@ SubtitleManager::~SubtitleManager() { } void SubtitleManager::reset() { - for (int i = 0; i < (int)_subtitles.size(); i++) + for (uint i = 0; i < _subtitles.size(); i++) delete _subtitles[i]; _subtitles.clear(); diff --git a/engines/lastexpress/debug.cpp b/engines/lastexpress/debug.cpp index dc2807db63..f89ad8b80d 100644 --- a/engines/lastexpress/debug.cpp +++ b/engines/lastexpress/debug.cpp @@ -28,13 +28,13 @@ #include "lastexpress/data/cursor.h" #include "lastexpress/data/scene.h" #include "lastexpress/data/sequence.h" -#include "lastexpress/data/snd.h" #include "lastexpress/data/subtitle.h" #include "lastexpress/fight/fight.h" #include "lastexpress/game/action.h" #include "lastexpress/game/beetle.h" +#include "lastexpress/game/entities.h" #include "lastexpress/game/inventory.h" #include "lastexpress/game/logic.h" #include "lastexpress/game/object.h" @@ -44,15 +44,12 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" #include "lastexpress/graphics.h" -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" #include "lastexpress/resource.h" #include "common/debug-channels.h" -#include "common/events.h" #include "common/md5.h" namespace LastExpress { @@ -612,7 +609,7 @@ bool Debugger::cmdPlayNis(int argc, const char **argv) { loadArchive((ArchiveIndex)getNumber(argv[2])); // If we got a nis filename, check that the file exists - if (name.contains('.') && _engine->getResourceManager()->hasFile(name)) { + if (name.contains('.') && !_engine->getResourceManager()->hasFile(name)) { DebugPrintf("Cannot find file: %s\n", name.c_str()); return true; } diff --git a/engines/lastexpress/detection.cpp b/engines/lastexpress/detection.cpp index 82a6520522..2fdeef910a 100644 --- a/engines/lastexpress/detection.cpp +++ b/engines/lastexpress/detection.cpp @@ -173,7 +173,7 @@ static const ADGameDescription gameDescriptions[] = { ADGF_UNSTABLE, GUIO1(GUIO_NOASPECT) }, - + // The Last Express (Russian) // expressw.exe 1999-04-05 15:33:56 // express.exe ??? @@ -211,6 +211,7 @@ public: return "LastExpress Engine (C) 1997 Smoking Car Productions"; } +protected: bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const; }; diff --git a/engines/lastexpress/entities/abbot.cpp b/engines/lastexpress/entities/abbot.cpp index 301c52e142..e0fe429520 100644 --- a/engines/lastexpress/entities/abbot.cpp +++ b/engines/lastexpress/entities/abbot.cpp @@ -34,9 +34,7 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" namespace LastExpress { @@ -417,7 +415,7 @@ IMPLEMENT_FUNCTION(22, Abbot, function22) break; case kActionNone: - TIME_CHECK_SAVEPOINT(kTime1971000, params->param1, kEntityAbbot, kEntityServers0, kAction218586752); + Entity::timeCheckSavepoint(kTime1971000, params->param1, kEntityAbbot, kEntityServers0, kAction218586752); if (getState()->time > kTime1989000 && getEntities()->isSomebodyInsideRestaurantOrSalon()) { getData()->inventoryItem = kItemNone; @@ -516,7 +514,8 @@ IMPLEMENT_FUNCTION(24, Abbot, function24) break; case kActionNone: - UPDATE_PARAM(params->param1, getState()->time, 900); + if (!Entity::updateParameter(params->param1, getState()->time, 900)) + break; setup_function25(); break; @@ -617,7 +616,8 @@ IMPLEMENT_FUNCTION(26, Abbot, function26) break; case kActionNone: - UPDATE_PARAM(params->param2, getState()->time, 4500); + if (!Entity::updateParameter(params->param2, getState()->time, 4500)) + break; if (getEntities()->isSomebodyInsideRestaurantOrSalon()) setup_function27(); @@ -691,7 +691,7 @@ IMPLEMENT_FUNCTION(28, Abbot, function28) break; case kActionNone: - TIME_CHECK_CALLBACK(kTime2052000, params->param1, 1, setup_function29); + Entity::timeCheckCallback(kTime2052000, params->param1, 1, WRAP_SETUP_FUNCTION(Abbot, setup_function29)); break; case kActionDefault: @@ -770,7 +770,7 @@ IMPLEMENT_FUNCTION(29, Abbot, function29) getSavePoints()->push(kEntityAbbot, kEntityBoutarel, kAction122358304); getEntities()->drawSequenceLeft(kEntityAbbot, "508B"); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -864,7 +864,8 @@ IMPLEMENT_FUNCTION(31, Abbot, function31) if (!params->param1) break; - UPDATE_PARAM(params->param5, getState()->time, 450); + if (!Entity::updateParameter(params->param5, getState()->time, 450)) + break; setCallback(6); setup_callbackActionRestaurantOrSalon(); @@ -920,7 +921,8 @@ IMPLEMENT_FUNCTION(31, Abbot, function31) getSavePoints()->push(kEntityAbbot, kEntityAlexei, kAction122288808); params->param1 = 1; - UPDATE_PARAM(params->param5, getState()->time, 450); + if (!Entity::updateParameter(params->param5, getState()->time, 450)) + break; setCallback(6); setup_callbackActionRestaurantOrSalon(); @@ -1163,7 +1165,8 @@ IMPLEMENT_FUNCTION(36, Abbot, function36) break; case 2: - UPDATE_PARAM(params->param4, getState()->time, 900); + if (!Entity::updateParameter(params->param4, getState()->time, 900)) + break; getSound()->playSound(kEntityAbbot, "Abb3042"); break; @@ -1287,7 +1290,7 @@ IMPLEMENT_FUNCTION_II(40, Abbot, function40, CarIndex, EntityPosition) case kActionNone: if (getEntities()->updateEntity(kEntityAbbot, (CarIndex)params->param1, (EntityPosition)params->param2)) { - CALLBACK_ACTION(); + callbackAction(); } else if (!getEvent(kEventAbbotInvitationDrink) && getEntities()->isDistanceBetweenEntities(kEntityAbbot, kEntityPlayer, 1000) && !getEntities()->isInsideCompartments(kEntityPlayer) @@ -1302,7 +1305,7 @@ IMPLEMENT_FUNCTION_II(40, Abbot, function40, CarIndex, EntityPosition) case kActionDefault: if (getEntities()->updateEntity(kEntityAbbot, (CarIndex)params->param1, (EntityPosition)params->param2)) - CALLBACK_ACTION(); + callbackAction(); break; case kActionCallback: @@ -1321,7 +1324,7 @@ IMPLEMENT_FUNCTION(41, Abbot, chapter4Handler) break; case kActionNone: - TIME_CHECK_SAVEPOINT(kTime2358000, params->param1, kEntityAbbot, kEntityServers0, kAction218128129); + Entity::timeCheckSavepoint(kTime2358000, params->param1, kEntityAbbot, kEntityServers0, kAction218128129); if (getState()->time > kTime2389500 && getEntities()->isSomebodyInsideRestaurantOrSalon()) setup_function42(); @@ -1425,10 +1428,12 @@ IMPLEMENT_FUNCTION(43, Abbot, function43) } label_callback_1: - TIME_CHECK(kTime2466000, params->param5, setup_function44); + if (Entity::timeCheck(kTime2466000, params->param5, WRAP_SETUP_FUNCTION(Abbot, setup_function44))) + break; if (params->param3) { - UPDATE_PARAM(params->param6, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param6, getState()->timeTicks, 75)) + break; params->param2 = 1; params->param3 = 0; @@ -1646,14 +1651,14 @@ IMPLEMENT_FUNCTION(48, Abbot, function48) if (ENTITY_PARAM(0, 1)) getData()->inventoryItem = kItemInvalid; - UPDATE_PARAM_PROC(params->param1, getState()->time, 1800) + if (Entity::updateParameter(params->param1, getState()->time, 1800)) { getData()->inventoryItem = kItemNone; setCallback(4); setup_updatePosition("126C", kCarRedSleeping, 52); - UPDATE_PARAM_PROC_END + } - TIME_CHECK_CALLBACK_INVENTORY(kTime2533500, params->param2, 5, setup_callbackActionRestaurantOrSalon); + Entity::timeCheckCallbackInventory(kTime2533500, params->param2, 5, WRAP_SETUP_FUNCTION(Abbot, setup_callbackActionRestaurantOrSalon)); break; case kAction1: @@ -1705,7 +1710,7 @@ IMPLEMENT_FUNCTION(48, Abbot, function48) getEntities()->drawSequenceLeft(kEntityAbbot, "126B"); params->param1 = 0; - TIME_CHECK_CALLBACK_INVENTORY(kTime2533500, params->param2, 5, setup_callbackActionRestaurantOrSalon); + Entity::timeCheckCallbackInventory(kTime2533500, params->param2, 5, WRAP_SETUP_FUNCTION(Abbot, setup_callbackActionRestaurantOrSalon)); break; case 5: @@ -1750,7 +1755,8 @@ IMPLEMENT_FUNCTION(49, Abbot, pickBomb) break; case kActionNone: - UPDATE_PARAM(params->param1, getState()->timeTicks, 150); + if (!Entity::updateParameter(params->param1, getState()->timeTicks, 150)) + break; getSavePoints()->push(kEntityAbbot, kEntityAbbot, kAction157489665); break; diff --git a/engines/lastexpress/entities/abbot.h b/engines/lastexpress/entities/abbot.h index 462f5a491e..ce52bb68ce 100644 --- a/engines/lastexpress/entities/abbot.h +++ b/engines/lastexpress/entities/abbot.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_ABBOT_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/alexei.cpp b/engines/lastexpress/entities/alexei.cpp index 54c2d87b89..115c890f6f 100644 --- a/engines/lastexpress/entities/alexei.cpp +++ b/engines/lastexpress/entities/alexei.cpp @@ -31,9 +31,6 @@ #include "lastexpress/game/scenes.h" #include "lastexpress/game/state.h" -#include "lastexpress/sound/sound.h" - -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" namespace LastExpress { @@ -207,7 +204,7 @@ IMPLEMENT_FUNCTION(13, Alexei, function13) getData()->entityPosition = kPosition_7500; getEntities()->clearSequences(kEntityAlexei); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -242,7 +239,7 @@ IMPLEMENT_FUNCTION(14, Alexei, function14) getObjects()->update(kObjectCompartment2, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand); getEntities()->exitCompartment(kEntityAlexei, kObjectCompartment2, true); - CALLBACK_ACTION(); + callbackAction(); break; } IMPLEMENT_FUNCTION_END @@ -254,7 +251,7 @@ IMPLEMENT_FUNCTION(15, Alexei, function15) break; case kActionNone: - UPDATE_PARAM_CHECK(params->param2, getState()->time, params->param1) + if (Entity::updateParameterCheck(params->param2, getState()->time, params->param1)) { if (getEntities()->isSomebodyInsideRestaurantOrSalon()) { getData()->location = kLocationOutsideCompartment; @@ -292,7 +289,7 @@ IMPLEMENT_FUNCTION(15, Alexei, function15) getData()->location = kLocationInsideCompartment; getEntities()->drawSequenceLeft(kEntityAlexei, "103B"); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -312,12 +309,13 @@ IMPLEMENT_FUNCTION_IS(16, Alexei, function16, TimeValue) getObjects()->update(kObjectCompartment2, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand); getObjects()->update(kObjectHandleInsideBathroom, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); break; } if (params->param5) { - UPDATE_PARAM(CURRENT_PARAM(1, 1), getState()->timeTicks, 75); + if (!Entity::updateParameter(CURRENT_PARAM(1, 1), getState()->timeTicks, 75)) + break; params->param5 = 0; params->param6 = 1; @@ -448,7 +446,7 @@ IMPLEMENT_FUNCTION(17, Alexei, chapter1) break; case kActionNone: - TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler) + Entity::timeCheck(kTimeChapter1, params->param1, WRAP_SETUP_FUNCTION(Alexei, setup_chapter1Handler)); break; case kActionDefault: @@ -485,7 +483,9 @@ IMPLEMENT_FUNCTION(18, Alexei, chapter1Handler) } if (params->param1) { - UPDATE_PARAM(params->param3, getState()->timeTicks, 90); + if (!Entity::updateParameter(params->param3, getState()->timeTicks, 90)) + break; + getScenes()->loadSceneFromPosition(kCarRestaurant, 61); } else { params->param3 = 0; @@ -689,7 +689,7 @@ IMPLEMENT_FUNCTION(21, Alexei, function21) break; case kActionNone: - UPDATE_PARAM_CHECK(params->param2, getState()->time, params->param1) + if (Entity::updateParameterCheck(params->param2, getState()->time, params->param1)) { getData()->location = kLocationOutsideCompartment; getData()->inventoryItem = kItemNone; @@ -751,7 +751,7 @@ IMPLEMENT_FUNCTION(22, Alexei, function22) break; case kActionNone: - UPDATE_PARAM_PROC(params->param2, getState()->time, params->param2) + if (Entity::updateParameter(params->param2, getState()->time, params->param2)) { if (getEntities()->isSomebodyInsideRestaurantOrSalon()) { getData()->location = kLocationOutsideCompartment; getData()->inventoryItem = kItemNone; @@ -760,7 +760,7 @@ IMPLEMENT_FUNCTION(22, Alexei, function22) setup_updatePosition("103D", kCarRestaurant, 52); break; } - UPDATE_PARAM_PROC_END + } if (params->param3 == kTimeInvalid || getState()->time <= kTime1111500) break; @@ -978,7 +978,7 @@ IMPLEMENT_FUNCTION(26, Alexei, function26) break; case kActionNone: - TIME_CHECK(kTime1512000, params->param1, setup_function27) + Entity::timeCheck(kTime1512000, params->param1, WRAP_SETUP_FUNCTION(Alexei, setup_function27)); break; case kActionDefault: @@ -1333,25 +1333,26 @@ IMPLEMENT_FUNCTION(35, Alexei, function35) case kActionNone: if (getEntities()->isInSalon(kEntityPlayer)) { - UPDATE_PARAM_PROC(params->param2, getState()->time, 2700) + if (Entity::updateParameter(params->param2, getState()->time, 2700)) { setCallback(1); setup_callbackActionRestaurantOrSalon(); break; - UPDATE_PARAM_PROC_END + } } else { params->param2 = 0; } - UPDATE_PARAM_PROC(params->param3, getState()->time, params->param1) + if (Entity::updateParameter(params->param3, getState()->time, params->param1)) { if (getEntities()->isSomebodyInsideRestaurantOrSalon()) { setCallback(3); setup_function15(); break; } - UPDATE_PARAM_PROC_END + } label_callback_3: - UPDATE_PARAM(params->param4, getState()->time, 9000); + if (!Entity::updateParameter(params->param4, getState()->time, 9000)) + break; setCallback(4); setup_callbackActionRestaurantOrSalon(); @@ -1378,7 +1379,7 @@ label_callback_3: case 2: case 5: - CALLBACK_ACTION(); + callbackAction(); break; case 3: @@ -1400,7 +1401,8 @@ IMPLEMENT_FUNCTION(36, Alexei, function36) if (params->param3 || params->param2) break; - UPDATE_PARAM(params->param4, getState()->timeTicks, params->param1); + if (!Entity::updateParameter(params->param4, getState()->timeTicks, params->param1)) + break; getEntities()->drawSequenceRight(kEntityAlexei, "124B"); @@ -1448,7 +1450,7 @@ IMPLEMENT_FUNCTION(36, Alexei, function36) break; case 4: - CALLBACK_ACTION(); + callbackAction(); break; } break; diff --git a/engines/lastexpress/entities/alexei.h b/engines/lastexpress/entities/alexei.h index 262826ae42..9792385863 100644 --- a/engines/lastexpress/entities/alexei.h +++ b/engines/lastexpress/entities/alexei.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_ALEXEI_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/alouan.cpp b/engines/lastexpress/entities/alouan.cpp index cd79870559..e834e1f7cb 100644 --- a/engines/lastexpress/entities/alouan.cpp +++ b/engines/lastexpress/entities/alouan.cpp @@ -28,9 +28,6 @@ #include "lastexpress/game/savepoint.h" #include "lastexpress/game/state.h" -#include "lastexpress/sound/sound.h" - -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" namespace LastExpress { @@ -89,22 +86,22 @@ IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// IMPLEMENT_FUNCTION(6, Alouan, compartment6) - COMPARTMENT_TO(Alouan, kObjectCompartment6, kPosition_4070, "621Cf", "621Df"); + Entity::goToCompartment(savepoint, kObjectCompartment6, kPosition_4070, "621Cf", "621Df"); IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// IMPLEMENT_FUNCTION(7, Alouan, compartment8) - COMPARTMENT_TO(Alouan, kObjectCompartment8, kPosition_2740, "621Ch", "621Dh"); + Entity::goToCompartment(savepoint, kObjectCompartment8, kPosition_2740, "621Ch", "621Dh"); IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// IMPLEMENT_FUNCTION(8, Alouan, compartment6to8) - COMPARTMENT_FROM_TO(Alouan, kObjectCompartment6, kPosition_4070, "621Bf", kObjectCompartment8, kPosition_2740, "621Ah"); + Entity::goToCompartmentFromCompartment(savepoint, kObjectCompartment6, kPosition_4070, "621Bf", kObjectCompartment8, kPosition_2740, "621Ah"); IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// IMPLEMENT_FUNCTION(9, Alouan, compartment8to6) - COMPARTMENT_FROM_TO(Alouan, kObjectCompartment8, kPosition_2740, "621Bh", kObjectCompartment6, kPosition_4070, "621Af"); + Entity::goToCompartmentFromCompartment(savepoint, kObjectCompartment8, kPosition_2740, "621Bh", kObjectCompartment6, kPosition_4070, "621Af"); IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// @@ -114,7 +111,7 @@ IMPLEMENT_FUNCTION(10, Alouan, chapter1) break; case kActionNone: - TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler); + Entity::timeCheck(kTimeChapter1, params->param1, WRAP_SETUP_FUNCTION(Alouan, setup_chapter1Handler)); break; case kActionDefault: @@ -134,7 +131,8 @@ IMPLEMENT_FUNCTION(11, Alouan, chapter1Handler) case kActionNone: - TIME_CHECK_CALLBACK(kTime1096200, params->param1, 1, setup_compartment8to6); + if (Entity::timeCheckCallback(kTime1096200, params->param1, 1, WRAP_SETUP_FUNCTION(Alouan, setup_compartment8to6))) + break; label_callback1: if (getState()->time > kTime1162800 && !params->param2) { @@ -284,21 +282,28 @@ IMPLEMENT_FUNCTION(16, Alouan, chapter3Handler) break; case kActionNone: - TIME_CHECK_CALLBACK(kTimeCitySalzbourg, params->param1, 1, setup_compartment8to6); + if (Entity::timeCheckCallback(kTimeCitySalzbourg, params->param1, 1, WRAP_SETUP_FUNCTION(Alouan, setup_compartment8to6))) + break; label_callback1: - if (params->param2 != kTimeInvalid && getState()->time > kTime1989000) - TIME_CHECK_CAR(kTime2119500, params->param5, 5, setup_compartment8); + if (params->param2 != kTimeInvalid && getState()->time > kTime1989000) { + if (Entity::timeCheckCar(kTime2119500, params->param5, 5, WRAP_SETUP_FUNCTION(Alouan, setup_compartment8))) + break; + } label_callback2: - TIME_CHECK_CALLBACK_1(kTime2052000, params->param3, 3, setup_playSound, "Har1005"); + if (Entity::timeCheckCallback(kTime2052000, params->param3, 3, "Har1005", WRAP_SETUP_FUNCTION_S(Alouan, setup_playSound))) + break; label_callback3: - TIME_CHECK_CALLBACK(kTime2133000, params->param4, 4, setup_compartment6to8); + if (Entity::timeCheckCallback(kTime2133000, params->param4, 4, WRAP_SETUP_FUNCTION(Alouan, setup_compartment6to8))) + break; label_callback4: - if (params->param5 != kTimeInvalid && getState()->time > kTime2151000) - TIME_CHECK_CAR(kTime2241000, params->param5, 5, setup_compartment8); + if (params->param5 != kTimeInvalid && getState()->time > kTime2151000) { + if (Entity::timeCheckCar(kTime2241000, params->param5, 5, WRAP_SETUP_FUNCTION(Alouan, setup_compartment8))) + break; + } break; case kActionDefault: @@ -355,11 +360,14 @@ IMPLEMENT_FUNCTION(18, Alouan, chapter4Handler) break; case kActionNone: - if (params->param1 != kTimeInvalid) - TIME_CHECK_CAR(kTime2443500, params->param1, 1, setup_compartment8); + if (params->param1 != kTimeInvalid) { + if (Entity::timeCheckCar(kTime2443500, params->param1, 1, WRAP_SETUP_FUNCTION(Alouan, setup_compartment8))) + break; + } label_callback1: - TIME_CHECK_CALLBACK(kTime2455200, params->param2, 2, setup_compartment8to6); + if (Entity::timeCheckCallback(kTime2455200, params->param2, 2, WRAP_SETUP_FUNCTION(Alouan, setup_compartment8to6))) + break; label_callback2: if (getState()->time > kTime2475000 && !params->param3) { @@ -441,7 +449,9 @@ IMPLEMENT_FUNCTION(22, Alouan, function22) break; case kActionNone: - UPDATE_PARAM(params->param1, getState()->time, 2700); + if (!Entity::updateParameter(params->param1, getState()->time, 2700)) + break; + setup_function23(); break; diff --git a/engines/lastexpress/entities/alouan.h b/engines/lastexpress/entities/alouan.h index c6a6beddd9..91254a449a 100644 --- a/engines/lastexpress/entities/alouan.h +++ b/engines/lastexpress/entities/alouan.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_ALOUAN_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/anna.cpp b/engines/lastexpress/entities/anna.cpp index b13aa21f6d..f8768032b5 100644 --- a/engines/lastexpress/entities/anna.cpp +++ b/engines/lastexpress/entities/anna.cpp @@ -34,9 +34,7 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" namespace LastExpress { @@ -200,16 +198,17 @@ IMPLEMENT_FUNCTION(12, Anna, function12) params->param2 = 1; if (params->param6) { - UPDATE_PARAM_PROC(params->param7, getState()->timeTicks, 75) + if (Entity::updateParameter(params->param7, getState()->timeTicks, 75)) { getSavePoints()->push(kEntityAnna, kEntityAnna, kActionEndSound); params->param6 = 0; params->param7 = 0; - UPDATE_PARAM_PROC_END + } } if (params->param4) { - UPDATE_PARAM(params->param8, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param8, getState()->timeTicks, 75)) + break; params->param4 = 0; params->param5 = 1; @@ -227,7 +226,7 @@ IMPLEMENT_FUNCTION(12, Anna, function12) case kActionEndSound: if (params->param2) { - CALLBACK_ACTION(); + callbackAction(); break; } @@ -289,7 +288,7 @@ IMPLEMENT_FUNCTION(12, Anna, function12) getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand); getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -424,12 +423,13 @@ IMPLEMENT_FUNCTION_IS(15, Anna, function15, TimeValue) getObjects()->update(kObjectCompartmentF, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand); getObjects()->update(kObject53, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); break; } if (params->param5) { - UPDATE_PARAM(params->param8, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param8, getState()->timeTicks, 75)) + break; params->param5 = 0; params->param6 = 1; @@ -547,7 +547,7 @@ IMPLEMENT_FUNCTION(16, Anna, chapter1) break; case kActionNone: - TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler); + Entity::timeCheck(kTimeChapter1, params->param1, WRAP_SETUP_FUNCTION(Anna, setup_chapter1Handler)); break; case kActionDefault: @@ -577,7 +577,7 @@ IMPLEMENT_FUNCTION_II(17, Anna, function17, uint32, uint32) if (getEntities()->updateEntity(kEntityAnna, (CarIndex)params->param1, (EntityPosition)params->param2)) { getData()->inventoryItem = kItemNone; - CALLBACK_ACTION(); + callbackAction(); } break; @@ -615,7 +615,7 @@ IMPLEMENT_FUNCTION_II(17, Anna, function17, uint32, uint32) } if (getEntities()->updateEntity(kEntityAnna, (CarIndex)params->param1, (EntityPosition)params->param2)) - CALLBACK_ACTION(); + callbackAction(); break; case kActionCallback: @@ -664,20 +664,21 @@ IMPLEMENT_FUNCTION_I(18, Anna, function18, TimeValue) case kActionNone: if (params->param1 && params->param1 < getState()->time && getEntities()->isSomebodyInsideRestaurantOrSalon()) { getData()->inventoryItem = kItemNone; - CALLBACK_ACTION(); + callbackAction(); break; } if (params->param5 && !params->param4) { - UPDATE_PARAM_PROC(params->param6, getState()->time, 900) + if (Entity::updateParameter(params->param6, getState()->time, 900)) { params->param2 |= kItemScarf; params->param5 = 0; params->param6 = 0; - UPDATE_PARAM_PROC_END + } } if (params->param3) { - UPDATE_PARAM(params->param7, getState()->timeTicks, 90); + if (!Entity::updateParameter(params->param7, getState()->timeTicks, 90)) + break; getScenes()->loadSceneFromPosition(kCarRestaurant, 61); } else { @@ -757,7 +758,7 @@ IMPLEMENT_FUNCTION_I(18, Anna, function18, TimeValue) case kAction259136835: case kAction268773672: getData()->inventoryItem = kItemNone; - CALLBACK_ACTION(); + callbackAction(); break; } IMPLEMENT_FUNCTION_END @@ -1151,15 +1152,16 @@ IMPLEMENT_FUNCTION(29, Anna, function29) case kActionNone: if (params->param2) { - UPDATE_PARAM_PROC(params->param3, getState()->time, 900) + if (Entity::updateParameter(params->param3, getState()->time, 900)) { getData()->inventoryItem = (InventoryItem)(getData()->inventoryItem | kItemScarf); params->param2 = 0; params->param3 = 0; - UPDATE_PARAM_PROC_END + } } if (params->param1) { - UPDATE_PARAM(params->param4, getState()->timeTicks, 90); + if (!Entity::updateParameter(params->param4, getState()->timeTicks, 90)) + break; getScenes()->loadSceneFromPosition(kCarRestaurant, 55); } else { @@ -1281,7 +1283,9 @@ IMPLEMENT_FUNCTION(30, Anna, function30) } if (params->param1) { - UPDATE_PARAM(params->param5, getState()->timeTicks, 90); + if (!Entity::updateParameter(params->param5, getState()->timeTicks, 90)) + break; + getScenes()->loadSceneFromPosition(kCarRestaurant, 55); } else { params->param5 = 0; @@ -1411,15 +1415,15 @@ IMPLEMENT_FUNCTION(34, Anna, function34) case kActionNone: if (!params->param1 && getEntities()->isPlayerPosition(kCarRedSleeping, 60)) { - UPDATE_PARAM_PROC(params->param2, getState()->time, 150) + if (Entity::updateParameter(params->param2, getState()->time, 150)) { setCallback(1); setup_draw("419B"); break; - UPDATE_PARAM_PROC_END + } } label_callback_1: - TIME_CHECK(kTime1489500, params->param3, setup_function35); + Entity::timeCheck(kTime1489500, params->param3, WRAP_SETUP_FUNCTION(Anna, setup_function35)); break; case kActionKnock: @@ -1485,7 +1489,8 @@ IMPLEMENT_FUNCTION(35, Anna, function35) if (!params->param1) break; - UPDATE_PARAM(params->param3, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param3, getState()->timeTicks, 75)) + break; switch (params->param2) { default: @@ -1666,7 +1671,7 @@ IMPLEMENT_FUNCTION_II(39, Anna, function39, CarIndex, EntityPosition) if (getEntities()->updateEntity(kEntityAnna, (CarIndex)params->param1, (EntityPosition)params->param2)) { getData()->inventoryItem = kItemNone; - CALLBACK_ACTION(); + callbackAction(); } break; @@ -1687,7 +1692,7 @@ IMPLEMENT_FUNCTION_II(39, Anna, function39, CarIndex, EntityPosition) if (getEntities()->updateEntity(kEntityAnna, (CarIndex)params->param1, (EntityPosition)params->param2)) { getData()->inventoryItem = kItemNone; - CALLBACK_ACTION(); + callbackAction(); } break; @@ -1799,7 +1804,8 @@ IMPLEMENT_FUNCTION(41, Anna, function41) break; case kActionNone: - UPDATE_PARAM(params->param2, getState()->time, 2700); + if (!Entity::updateParameter(params->param2, getState()->time, 2700)) + break; params->param5++; switch (params->param5) { @@ -1985,7 +1991,7 @@ IMPLEMENT_FUNCTION_I(45, Anna, function45, bool) case 2: getEntities()->exitCompartment(kEntityAnna, kObjectCompartmentF, true); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -2092,11 +2098,11 @@ IMPLEMENT_FUNCTION(48, Anna, function48) break; if (params->param3 != kTimeInvalid && getState()->time > kTime1969200) { - UPDATE_PARAM_PROC_TIME(kTime1983600, (!getEntities()->isInRestaurant(kEntityPlayer) || getSoundQueue()->isBuffered(kEntityBoutarel)), params->param3, 150) + if (Entity::updateParameterTime(kTime1983600, (!getEntities()->isInRestaurant(kEntityPlayer) || getSoundQueue()->isBuffered(kEntityBoutarel)), params->param3, 150)) { setCallback(3); setup_playSound("Aug3007A"); break; - UPDATE_PARAM_PROC_END + } } label_callback_4: @@ -2195,7 +2201,7 @@ IMPLEMENT_FUNCTION(49, Anna, leaveTableWithAugust) getSavePoints()->push(kEntityAnna, kEntityTables3, kActionDrawTablesWithChairs, "010M"); getEntities()->clearSequences(kEntityAugust); - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: @@ -2386,22 +2392,23 @@ IMPLEMENT_FUNCTION(53, Anna, function53) case kActionNone: if (getProgress().field_48 && params->param5 != kTimeInvalid) { - UPDATE_PARAM_PROC_TIME(kTime2065500, !getEntities()->isPlayerInCar(kCarRedSleeping), params->param5, 150) + if (Entity::updateParameterTime(kTime2065500, !getEntities()->isPlayerInCar(kCarRedSleeping), params->param5, 150)) { setup_function54(); break; - UPDATE_PARAM_PROC_END + } } if (params->param3) { - UPDATE_PARAM_PROC(params->param6, getState()->time, 9000) + if (Entity::updateParameter(params->param6, getState()->time, 9000)) { params->param4 = !params->param4; getEntities()->drawSequenceLeft(kEntityAnna, params->param4 ? "417B" : "417A"); params->param6 = 0; - UPDATE_PARAM_PROC_END + } } if (params->param1) { - UPDATE_PARAM(params->param7, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param7, getState()->timeTicks, 75)) + break; CursorStyle cursor = getEntities()->isInsideCompartment(kEntityMax, kCarRedSleeping, kPosition_4070) ? kCursorHand : kCursorNormal; @@ -2537,17 +2544,19 @@ IMPLEMENT_FUNCTION(54, Anna, function54) case kActionNone: if (params->param3) { - TIME_CHECK(kTime2079000, params->param5, setup_function55); + if (Entity::timeCheck(kTime2079000, params->param5, WRAP_SETUP_FUNCTION(Anna, setup_function55))) + break; - UPDATE_PARAM_PROC(params->param6, getState()->time, 9000) + if (Entity::updateParameter(params->param6, getState()->time, 9000)) { params->param4 = !params->param4; getEntities()->drawSequenceLeft(kEntityAnna, params->param4 ? "417B" : "417A"); params->param6 = 0; - UPDATE_PARAM_PROC_END + } } if (params->param1) { - UPDATE_PARAM(params->param7, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param7, getState()->timeTicks, 75)) + break; CursorStyle cursor = getEntities()->isInsideCompartment(kEntityMax, kCarRedSleeping, kPosition_4070) ? kCursorHand : kCursorNormal; @@ -2894,7 +2903,8 @@ IMPLEMENT_FUNCTION(59, Anna, function59) } if (params->param1) { - UPDATE_PARAM(params->param5, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param5, getState()->timeTicks, 75)) + break; CursorStyle style = getEntities()->isInsideCompartment(kEntityMax, kCarRedSleeping, kPosition_4070) ? kCursorHand : kCursorNormal; getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, style); @@ -3040,7 +3050,7 @@ IMPLEMENT_FUNCTION(60, Anna, function60) getData()->location = kLocationInsideCompartment; getEntities()->clearSequences(kEntityAnna); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -3268,7 +3278,8 @@ IMPLEMENT_FUNCTION(67, Anna, chapter4Handler) case kActionNone: if (getEntities()->isPlayerPosition(kCarRedSleeping, 46)) { - UPDATE_PARAM_GOTO(params->param4, getState()->timeTicks, 30, label_next); + if (!Entity::updateParameter(params->param4, getState()->timeTicks, 30)) + goto label_next; getScenes()->loadSceneFromPosition(kCarRedSleeping, 8); } @@ -3277,7 +3288,8 @@ IMPLEMENT_FUNCTION(67, Anna, chapter4Handler) label_next: if (params->param1) { - UPDATE_PARAM(params->param5, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param5, getState()->timeTicks, 75)) + break; params->param1 = 0; params->param2 = 1; @@ -3407,7 +3419,8 @@ IMPLEMENT_FUNCTION(69, Anna, function69) case kActionNone: if (params->param1) { - UPDATE_PARAM(params->param2, getState()->time, 4500); + if (!Entity::updateParameter(params->param2, getState()->time, 4500)) + break; getData()->car = kCarRedSleeping; getData()->entityPosition = kPosition_9270; @@ -3417,7 +3430,7 @@ IMPLEMENT_FUNCTION(69, Anna, function69) break; } - TIME_CHECK_CALLBACK(kTime2535300, params->param3, 4, setup_callbackActionRestaurantOrSalon); + Entity::timeCheckCallback(kTime2535300, params->param3, 4, WRAP_SETUP_FUNCTION(Anna, setup_callbackActionRestaurantOrSalon)); break; case kActionDefault: @@ -3533,7 +3546,7 @@ IMPLEMENT_FUNCTION(71, Anna, function71) getEntities()->exitCompartment(kEntityAnna, kObjectCompartmentF); getData()->entityPosition = kPosition_4070; - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: @@ -3589,7 +3602,7 @@ IMPLEMENT_FUNCTION_II(72, Anna, function72, CarIndex, EntityPosition) if (getEntities()->updateEntity(kEntityAnna, (CarIndex)params->param1, (EntityPosition)params->param2)) { getData()->inventoryItem = kItemNone; - CALLBACK_ACTION(); + callbackAction(); } break; @@ -3602,7 +3615,7 @@ IMPLEMENT_FUNCTION_II(72, Anna, function72, CarIndex, EntityPosition) case kActionDefault: if (getEntities()->updateEntity(kEntityAnna, (CarIndex)params->param1, (EntityPosition)params->param2)) { - CALLBACK_ACTION(); + callbackAction(); } else if (!getEvent(kEventAnnaTired)) getData()->inventoryItem = kItemInvalid; break; @@ -3911,7 +3924,8 @@ IMPLEMENT_FUNCTION(80, Anna, function80) break; case kActionNone: - UPDATE_PARAM(params->param1, getState()->timeTicks, 450); + if (!Entity::updateParameter(params->param1, getState()->timeTicks, 450)) + break; getSound()->playSound(kEntityPlayer, "Kro5001", kFlagDefault); break; @@ -3980,7 +3994,8 @@ IMPLEMENT_FUNCTION(81, Anna, finalSequence) break; case kActionNone: - UPDATE_PARAM(params->param1, getState()->timeTicks, 180); + if (!Entity::updateParameter(params->param1, getState()->timeTicks, 180)) + break; getSound()->playSound(kEntityTrain, "LIB069"); getLogic()->gameOver(kSavegameTypeIndex, 2, kSceneNone, true); diff --git a/engines/lastexpress/entities/anna.h b/engines/lastexpress/entities/anna.h index 72c6db4bd9..205ff9d42c 100644 --- a/engines/lastexpress/entities/anna.h +++ b/engines/lastexpress/entities/anna.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_ANNA_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/august.cpp b/engines/lastexpress/entities/august.cpp index cfde8a2d6f..67d810fde2 100644 --- a/engines/lastexpress/entities/august.cpp +++ b/engines/lastexpress/entities/august.cpp @@ -36,9 +36,7 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" namespace LastExpress { @@ -150,7 +148,7 @@ IMPLEMENT_FUNCTION_END IMPLEMENT_FUNCTION_SI(7, August, enterExitCompartment3, ObjectIndex) if (savepoint.action == kAction4) { getEntities()->exitCompartment(kEntityAugust, (ObjectIndex)params->param4); - CALLBACK_ACTION(); + callbackAction(); return; } @@ -177,7 +175,7 @@ IMPLEMENT_FUNCTION_IIS(10, August, callSavepointNoDrawing, EntityIndex, ActionIn if (!params->param6) getSavePoints()->call(kEntityAugust, (EntityIndex)params->param1, (ActionIndex)params->param2, (char *)¶ms->seq); - CALLBACK_ACTION(); + callbackAction(); break; case kAction10: @@ -233,7 +231,7 @@ IMPLEMENT_FUNCTION_I(17, August, function17, TimeValue) case kActionNone: if (params->param1 < getState()->time && !params->param2) { params->param2 = 1; - CALLBACK_ACTION(); + callbackAction(); break; } @@ -262,7 +260,7 @@ IMPLEMENT_FUNCTION_I(17, August, function17, TimeValue) case 1: if (ENTITY_PARAM(0, 1)) { - CALLBACK_ACTION(); + callbackAction(); break; } @@ -272,7 +270,7 @@ IMPLEMENT_FUNCTION_I(17, August, function17, TimeValue) case 2: case 3: if (ENTITY_PARAM(0, 1)) { - CALLBACK_ACTION(); + callbackAction(); break; } @@ -289,7 +287,7 @@ IMPLEMENT_FUNCTION_I(17, August, function17, TimeValue) case 5: if (ENTITY_PARAM(0, 1)) { - CALLBACK_ACTION(); + callbackAction(); break; } @@ -308,7 +306,7 @@ IMPLEMENT_FUNCTION_II(18, August, updateEntity2, CarIndex, EntityPosition) case kActionNone: if (getEntities()->updateEntity(_entityIndex, (CarIndex)params->param1, (EntityPosition)params->param2)) { - CALLBACK_ACTION(); + callbackAction(); } else if (getEntities()->isDistanceBetweenEntities(kEntityAugust, kEntityPlayer, 1000) && !getEntities()->isInGreenCarEntrance(kEntityPlayer) && !getEntities()->isInsideCompartments(kEntityPlayer) @@ -316,14 +314,14 @@ IMPLEMENT_FUNCTION_II(18, August, updateEntity2, CarIndex, EntityPosition) if (getData()->car == kCarGreenSleeping || getData()->car == kCarRedSleeping) { ENTITY_PARAM(0, 1) = 1; - CALLBACK_ACTION(); + callbackAction(); } } break; case kActionDefault: if (getEntities()->updateEntity(_entityIndex, (CarIndex)params->param1, (EntityPosition)params->param2)) - CALLBACK_ACTION(); + callbackAction(); break; } IMPLEMENT_FUNCTION_END @@ -407,7 +405,7 @@ IMPLEMENT_FUNCTION_II(19, August, function19, bool, bool) getData()->location = kLocationInsideCompartment; getEntities()->clearSequences(kEntityAugust); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -503,7 +501,7 @@ IMPLEMENT_FUNCTION_I(20, August, function20, bool) getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand); getEntities()->exitCompartment(kEntityAugust, kObjectCompartment3, true); - CALLBACK_ACTION(); + callbackAction(); break; } IMPLEMENT_FUNCTION_END @@ -520,12 +518,13 @@ IMPLEMENT_FUNCTION_I(21, August, function21, TimeValue) getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); break; } if (params->param2) { - UPDATE_PARAM_GOTO(params->param8, getState()->timeTicks, 75, label_continue); + if (!Entity::updateParameter(params->param8, getState()->timeTicks, 75)) + goto label_continue; params->param2 = 0; params->param3 = 1; @@ -540,10 +539,10 @@ label_continue: break; if (params->param6) { - UPDATE_PARAM_PROC(CURRENT_PARAM(1, 1), getState()->time, 6300) + if (Entity::updateParameter(CURRENT_PARAM(1, 1), getState()->time, 6300)) { params->param6 = 0; CURRENT_PARAM(1, 1) = 0; - UPDATE_PARAM_PROC_END + } } if (!params->param4 @@ -753,7 +752,7 @@ IMPLEMENT_FUNCTION(22, August, chapter1) break; case kActionNone: - TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler); + Entity::timeCheck(kTimeChapter1, params->param1, WRAP_SETUP_FUNCTION(August, setup_chapter1Handler)); break; case kActionDefault: @@ -786,7 +785,7 @@ IMPLEMENT_FUNCTION_I(23, August, function23, TimeValue) } else { getEntities()->exitCompartment(kEntityAugust, kObjectCompartment1, true); getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); } break; } @@ -806,7 +805,7 @@ IMPLEMENT_FUNCTION_I(23, August, function23, TimeValue) } label_callback_8: - UPDATE_PARAM_PROC(CURRENT_PARAM(1, 4), getState()->timeTicks, 75) + if (Entity::updateParameter(CURRENT_PARAM(1, 4), getState()->timeTicks, 75)) { getEntities()->exitCompartment(kEntityAugust, kObjectCompartment1, true); if (getProgress().eventCorpseMovedFromFloor) { @@ -822,7 +821,7 @@ label_callback_8: setup_savegame(kSavegameTypeEvent, kEventAugustFindCorpse); } break; - UPDATE_PARAM_PROC_END + } label_callback_9: if (params->param3 && params->param1 < getState()->time && !CURRENT_PARAM(1, 5)) { @@ -842,7 +841,8 @@ label_callback_9: break; if (getObjects()->get(kObjectCompartment1).location == kObjectLocation1) { - UPDATE_PARAM(CURRENT_PARAM(1, 2), getState()->timeTicks, 75); + if (!Entity::updateParameter(CURRENT_PARAM(1, 2), getState()->timeTicks, 75)) + break; getObjects()->update(kObjectCompartment1, kEntityAugust, getObjects()->get(kObjectCompartment1).location, kCursorNormal, kCursorNormal); @@ -867,7 +867,7 @@ label_callback_9: if (params->param8 >= 3) { getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); break; } @@ -959,7 +959,7 @@ label_callback_9: case 2: getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); break; case 3: @@ -986,7 +986,7 @@ label_callback_9: getScenes()->loadScene(kScene41); - CALLBACK_ACTION(); + callbackAction(); break; case 5: @@ -1028,7 +1028,7 @@ label_callback_9: case 12: getData()->location = kLocationOutsideCompartment; - CALLBACK_ACTION(); + callbackAction(); break; case 13: @@ -1056,7 +1056,7 @@ label_callback_9: getScenes()->loadScene(kScene41); - CALLBACK_ACTION(); + callbackAction(); break; case 15: @@ -1096,7 +1096,7 @@ IMPLEMENT_FUNCTION(24, August, dinner) getScenes()->loadSceneFromPosition(kCarRestaurant, 61); - CALLBACK_ACTION(); + callbackAction(); } break; } @@ -1485,7 +1485,8 @@ IMPLEMENT_FUNCTION(30, August, restaurant) break; case kActionNone: - UPDATE_PARAM(params->param3, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param3, getState()->timeTicks, 75)) + break; getData()->inventoryItem = kItemInvalid; break; @@ -1634,9 +1635,9 @@ IMPLEMENT_FUNCTION(32, August, function32) break; case kActionNone: - UPDATE_PARAM_PROC_TIME(kTime1179000, (!getEntities()->isInSalon(kEntityAnna) || getEntities()->isInSalon(kEntityPlayer)), params->param6, 0); + if (Entity::updateParameterTime(kTime1179000, (!getEntities()->isInSalon(kEntityAnna) || getEntities()->isInSalon(kEntityPlayer)), params->param6, 0)) { getSavePoints()->push(kEntityAugust, kEntityAnna, kAction123712592); - UPDATE_PARAM_PROC_END + } if (params->param1 && getEntities()->isSomebodyInsideRestaurantOrSalon()) { if (!params->param4) { @@ -1645,18 +1646,19 @@ IMPLEMENT_FUNCTION(32, August, function32) } if (params->param7 != kTimeInvalid && params->param4 < getState()->time) { - UPDATE_PARAM_PROC_TIME(params->param5, getEntities()->isInSalon(kEntityPlayer), params->param7, 0); + if (Entity::updateParameterTime((TimeValue)params->param5, getEntities()->isInSalon(kEntityPlayer), params->param7, 0)) { getData()->location = kLocationOutsideCompartment; setCallback(5); setup_updatePosition("109D", kCarRestaurant, 56); break; - UPDATE_PARAM_PROC_END + } } } if (params->param3) { - UPDATE_PARAM(params->param8, getState()->timeTicks, 90); + if (!Entity::updateParameter(params->param8, getState()->timeTicks, 90)) + break; getScenes()->loadSceneFromPosition(kCarRestaurant, 55); } else { @@ -1813,7 +1815,7 @@ IMPLEMENT_FUNCTION(36, August, chapter2Handler) break; case kActionNone: - TIME_CHECK_SAVEPOINT(kTime1755000, params->param2, kEntityAugust, kEntityServers0, kAction252568704); + Entity::timeCheckSavepoint(kTime1755000, params->param2, kEntityAugust, kEntityServers0, kAction252568704); if (getState()->time > kTime1773000 && params->param1 && getEntities()->isSomebodyInsideRestaurantOrSalon()) { getData()->inventoryItem = kItemNone; @@ -1904,7 +1906,7 @@ IMPLEMENT_FUNCTION(37, August, function37) break; case kActionNone: - TIME_CHECK_CALLBACK_1(kTime1791000, params->param2, 5, setup_function20, true); + Entity::timeCheckCallback(kTime1791000, params->param2, 5, true, WRAP_SETUP_FUNCTION_B(August, setup_function20)); break; case kActionDefault: @@ -1962,9 +1964,9 @@ IMPLEMENT_FUNCTION(38, August, function38) break; case kActionNone: - TIME_CHECK_SAVEPOINT(kTime1801800, params->param1, kEntityAugust, kEntityRebecca, kAction155980128); + Entity::timeCheckSavepoint(kTime1801800, params->param1, kEntityAugust, kEntityRebecca, kAction155980128); - TIME_CHECK_CALLBACK(kTime1820700, params->param2, 3, setup_callbackActionRestaurantOrSalon); + Entity::timeCheckCallback(kTime1820700, params->param2, 3, WRAP_SETUP_FUNCTION(August, setup_callbackActionRestaurantOrSalon)); break; case kActionDefault: @@ -2110,7 +2112,7 @@ IMPLEMENT_FUNCTION_II(41, August, function41, CarIndex, EntityPosition) getData()->inventoryItem = kItemNone; if (getEntities()->updateEntity(kEntityAugust, (CarIndex)params->param1, (EntityPosition)params->param2)) { - CALLBACK_ACTION(); + callbackAction(); break; } @@ -2147,7 +2149,7 @@ IMPLEMENT_FUNCTION_II(41, August, function41, CarIndex, EntityPosition) case kActionDefault: if (getEntities()->updateEntity(kEntityAugust, (CarIndex)params->param1, (EntityPosition)params->param2)) { - CALLBACK_ACTION(); + callbackAction(); break; } @@ -2172,7 +2174,7 @@ IMPLEMENT_FUNCTION_III(42, August, function42, CarIndex, EntityPosition, bool) if (getEntities()->updateEntity(kEntityAugust, (CarIndex)params->param1, (EntityPosition)params->param2)) { getData()->inventoryItem = kItemNone; - CALLBACK_ACTION(); + callbackAction(); } break; @@ -2191,7 +2193,7 @@ IMPLEMENT_FUNCTION_III(42, August, function42, CarIndex, EntityPosition, bool) case kActionDefault: if (getEntities()->updateEntity(kEntityAugust, (CarIndex)params->param1, (EntityPosition)params->param2)) { - CALLBACK_ACTION(); + callbackAction(); break; } @@ -2212,7 +2214,7 @@ IMPLEMENT_FUNCTION(43, August, chapter3Handler) break; case kActionNone: - TIME_CHECK_SAVEPOINT(kTime1953000, params->param2, kEntityAugust, kEntityAnna, kAction291662081); + Entity::timeCheckSavepoint(kTime1953000, params->param2, kEntityAugust, kEntityAnna, kAction291662081); // Set as same position as Anna if (params->param1) { @@ -2402,7 +2404,7 @@ IMPLEMENT_FUNCTION_END IMPLEMENT_FUNCTION(46, August, function46) switch (savepoint.action) { default: - TIME_CHECK_CALLBACK(kTime2088000, params->param1, 1, setup_function47); + Entity::timeCheckCallback(kTime2088000, params->param1, 1, WRAP_SETUP_FUNCTION(August, setup_function47)); break; case kActionNone: @@ -2473,7 +2475,7 @@ IMPLEMENT_FUNCTION(47, August, function47) break; case 5: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -2487,7 +2489,7 @@ IMPLEMENT_FUNCTION(48, August, function48) break; case kActionNone: - TIME_CHECK(kTimeCityLinz, params->param1, setup_function49); + Entity::timeCheck(kTimeCityLinz, params->param1, WRAP_SETUP_FUNCTION(August, setup_function49)); break; case kActionKnock: @@ -2770,7 +2772,8 @@ IMPLEMENT_FUNCTION(54, August, function54) getData()->inventoryItem = kItemInvalid; if (!params->param2 && params->param1) { - UPDATE_PARAM(params->param5, getState()->time, params->param1); + if (!Entity::updateParameter(params->param5, getState()->time, params->param1)) + break; getData()->inventoryItem = kItemNone; setup_function55(); @@ -3046,7 +3049,8 @@ IMPLEMENT_FUNCTION(60, August, function60) if (!params->param1) break; - UPDATE_PARAM(params->param3, getState()->time, 9000); + if (!Entity::updateParameter(params->param3, getState()->time, 9000)) + break; setCallback(1); setup_callbackActionRestaurantOrSalon(); @@ -3142,7 +3146,8 @@ IMPLEMENT_FUNCTION(62, August, function62) break; case kActionNone: - UPDATE_PARAM(params->param1, getState()->time, 900); + if (!Entity::updateParameter(params->param1, getState()->time, 900)) + break; getSound()->playSound(kEntityAugust, "Aug4003A"); @@ -3220,9 +3225,9 @@ IMPLEMENT_FUNCTION(63, August, function63) break; case kActionNone: - UPDATE_PARAM_PROC(params->param3, getState()->time, 1800) + if (Entity::updateParameter(params->param3, getState()->time, 1800)) { getData()->inventoryItem = kItemInvalid; - UPDATE_PARAM_PROC_END + } if (getState()->time > kTime2488500 && !params->param4) { params->param4 = 1; @@ -3231,7 +3236,8 @@ IMPLEMENT_FUNCTION(63, August, function63) break; } - UPDATE_PARAM(params->param5, getState()->timeTicks, params->param1); + if (!Entity::updateParameter(params->param5, getState()->timeTicks, params->param1)) + break; params->param2 = (params->param6 < 1 ? 1 : 0); @@ -3388,7 +3394,8 @@ IMPLEMENT_FUNCTION(68, August, function68) case kActionNone: if (params->param1) { - UPDATE_PARAM(params->param4, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param4, getState()->timeTicks, 75)) + break; params->param1 = 0; params->param2 = 1; diff --git a/engines/lastexpress/entities/august.h b/engines/lastexpress/entities/august.h index 2cbb31b968..606321955b 100644 --- a/engines/lastexpress/entities/august.h +++ b/engines/lastexpress/entities/august.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_AUGUST_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/boutarel.cpp b/engines/lastexpress/entities/boutarel.cpp index 315b12a69e..219ddf901b 100644 --- a/engines/lastexpress/entities/boutarel.cpp +++ b/engines/lastexpress/entities/boutarel.cpp @@ -32,9 +32,7 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" namespace LastExpress { @@ -231,7 +229,7 @@ IMPLEMENT_FUNCTION_I(11, Boutarel, function11, bool) case 7: getData()->location = kLocationInsideCompartment; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -249,7 +247,7 @@ IMPLEMENT_FUNCTION(12, Boutarel, enterTableWithMmeBoutarel) getSavePoints()->push(kEntityBoutarel, kEntityTables2, kAction136455232); getData()->location = kLocationInsideCompartment; - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: @@ -276,7 +274,7 @@ IMPLEMENT_FUNCTION(13, Boutarel, leaveTableWithMmeBoutarel) getSavePoints()->push(kEntityBoutarel, kEntityTables2, kActionDrawTablesWithChairs, "008F"); getEntities()->clearSequences(kEntityMmeBoutarel); - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: @@ -358,7 +356,7 @@ IMPLEMENT_FUNCTION_I(14, Boutarel, function14, bool) getEntities()->clearSequences(kEntityBoutarel); getData()->location = kLocationInsideCompartment; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -416,7 +414,7 @@ IMPLEMENT_FUNCTION_IS(15, Boutarel, function15, bool) case 5: getData()->location = kLocationInsideCompartment; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -464,7 +462,7 @@ IMPLEMENT_FUNCTION_IS(16, Boutarel, function16, bool) getData()->location = kLocationInsideCompartment; getEntities()->clearSequences(kEntityBoutarel); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -478,10 +476,13 @@ IMPLEMENT_FUNCTION_IS(17, Boutarel, function17, TimeValue) break; case kActionNone: - TIME_CHECK_CALLBACK_ACTION(params->param1, params->param6); + if (Entity::timeCheckCallbackAction((TimeValue)params->param1, params->param6)) + break; if (params->param5) { - UPDATE_PARAM(params->param7, getState()->timeTicks, 90) + if (!Entity::updateParameter(params->param7, getState()->timeTicks, 90)) + break; + getScenes()->loadSceneFromPosition(kCarRestaurant, 51); } else { params->param7 = 0; @@ -511,12 +512,13 @@ IMPLEMENT_FUNCTION_I(18, Boutarel, function18, TimeValue) getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand); getObjects()->update(kObject50, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); break; } if (params->param2) { - UPDATE_PARAM(params->param5, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param5, getState()->timeTicks, 75)) + break; params->param2 = 0; params->param3 = 1; @@ -615,7 +617,7 @@ IMPLEMENT_FUNCTION(19, Boutarel, chapter1) break; case kActionNone: - TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler); + Entity::timeCheck(kTimeChapter1, params->param1, WRAP_SETUP_FUNCTION(Boutarel, setup_chapter1Handler)); break; case kActionDefault: @@ -644,14 +646,14 @@ IMPLEMENT_FUNCTION(20, Boutarel, function20) break; if (!params->param2) { - UPDATE_PARAM_PROC(params->param3, getState()->time, 4500) + if (Entity::updateParameter(params->param3, getState()->time, 4500)) { setCallback(3); setup_playSound("MRB1078A"); break; - UPDATE_PARAM_PROC_END + } } - TIME_CHECK_CALLBACK_1(kTime1138500, params->param4, 4, setup_function14, false); + Entity::timeCheckCallback(kTime1138500, params->param4, 4, false, WRAP_SETUP_FUNCTION_B(Boutarel, setup_function14)); break; case kActionDefault: @@ -676,13 +678,13 @@ IMPLEMENT_FUNCTION(20, Boutarel, function20) break; case 3: - TIME_CHECK_CALLBACK_1(kTime1138500, params->param4, 4, setup_function14, false); + Entity::timeCheckCallback(kTime1138500, params->param4, 4, false, WRAP_SETUP_FUNCTION_B(Boutarel, setup_function14)); break; case 4: getSavePoints()->push(kEntityBoutarel, kEntityCooks, kAction224849280); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -834,7 +836,7 @@ IMPLEMENT_FUNCTION(24, Boutarel, chapter2Handler) break; case kActionNone: - TIME_CHECK_CALLBACK_1(kTime1759500, params->param2, 1, setup_function14, false); + Entity::timeCheckCallback(kTime1759500, params->param2, 1, false, WRAP_SETUP_FUNCTION_B(Boutarel, setup_function14)); break; case kActionDefault: @@ -934,9 +936,9 @@ IMPLEMENT_FUNCTION(29, Boutarel, function29) break; case kActionNone: - UPDATE_PARAM_PROC(params->param2, getState()->time, 450) + if (Entity::updateParameter(params->param2, getState()->time, 450)) { getSavePoints()->push(kEntityBoutarel, kEntityServers1, kAction256200848); - UPDATE_PARAM_PROC_END + } if (!params->param1) break; @@ -959,7 +961,7 @@ IMPLEMENT_FUNCTION(29, Boutarel, function29) } } - TIME_CHECK_CALLBACK_1(kTime2002500, params->param4, 1, setup_function14, true); + Entity::timeCheckCallback(kTime2002500, params->param4, 1, true, WRAP_SETUP_FUNCTION_B(Boutarel, setup_function14)); break; case kActionDefault: @@ -972,7 +974,7 @@ IMPLEMENT_FUNCTION(29, Boutarel, function29) break; case 1: - TIME_CHECK_CALLBACK_1(kTime2002500, params->param4, 1, setup_function14, true); + Entity::timeCheckCallback(kTime2002500, params->param4, 1, true, WRAP_SETUP_FUNCTION_B(Boutarel, setup_function14)); break; case 2: @@ -1045,7 +1047,7 @@ IMPLEMENT_FUNCTION(32, Boutarel, chapter4Handler) break; case kActionNone: - TIME_CHECK(kTime2367000, params->param1, setup_function33); + Entity::timeCheck(kTime2367000, params->param1, WRAP_SETUP_FUNCTION(Boutarel, setup_function33)); break; case kActionDefault: @@ -1063,7 +1065,7 @@ IMPLEMENT_FUNCTION(33, Boutarel, function33) case kActionNone: if (params->param1) - TIME_CHECK_CALLBACK_1(kTime2389500, params->param2, 3, setup_function14, false); + Entity::timeCheckCallback(kTime2389500, params->param2, 3, false, WRAP_SETUP_FUNCTION_B(Boutarel, setup_function14)); break; case kActionDefault: @@ -1111,7 +1113,8 @@ IMPLEMENT_FUNCTION(34, Boutarel, function34) break; case kActionNone: - TIME_CHECK(kTime2470500, params->param1, setup_function35); + if (Entity::timeCheck(kTime2470500, params->param1, WRAP_SETUP_FUNCTION(Boutarel, setup_function35))) + break; if (getState()->time > kTime2457000 && getEvent(kEventAugustDrink)) { getSavePoints()->push(kEntityBoutarel, kEntityAbbot, kAction159003408); diff --git a/engines/lastexpress/entities/boutarel.h b/engines/lastexpress/entities/boutarel.h index 5eb264631c..04838f6527 100644 --- a/engines/lastexpress/entities/boutarel.h +++ b/engines/lastexpress/entities/boutarel.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_BOUTAREL_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/chapters.cpp b/engines/lastexpress/entities/chapters.cpp index 4ef2dc50e8..a2f3a3d871 100644 --- a/engines/lastexpress/entities/chapters.cpp +++ b/engines/lastexpress/entities/chapters.cpp @@ -63,11 +63,9 @@ #include "lastexpress/game/state.h" #include "lastexpress/menu/menu.h" -#include "lastexpress/sound/sound.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" #include "lastexpress/resource.h" @@ -152,7 +150,7 @@ IMPLEMENT_FUNCTION(5, Chapters, resetMainEntities) RESET_ENTITY_STATE(kEntityVesna, Vesna, setup_reset); RESET_ENTITY_STATE(kEntityYasmin, Yasmin, setup_reset); - CALLBACK_ACTION(); + callbackAction(); IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// @@ -191,7 +189,7 @@ IMPLEMENT_FUNCTION(6, Chapters, chapter1End) getScenes()->loadScene(kScene41); - CALLBACK_ACTION(); + callbackAction(); } else { getSound()->playSound(kEntityPlayer, "LIB014"); getSound()->playSound(kEntityPlayer, "LIB015", kFlagDefault, 15); @@ -386,12 +384,6 @@ IMPLEMENT_FUNCTION(7, Chapters, chapter1Init) IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// -#define PLAY_STEAM() { \ - getSoundQueue()->resetState(); \ - getSound()->playSteam((CityIndex)ENTITY_PARAM(0, 4)); \ - ENTITY_PARAM(0, 2) = 0; \ - } - IMPLEMENT_FUNCTION(8, Chapters, chapter1Handler) switch (savepoint.action) { default: @@ -401,21 +393,28 @@ IMPLEMENT_FUNCTION(8, Chapters, chapter1Handler) if (!getProgress().isTrainRunning || getState()->time >= kTime1458000) goto label_processStations; - UPDATE_PARAM_PROC(params->param6, getState()->timeTicks, params->param2) + if (Entity::updateParameter(params->param6, getState()->timeTicks, params->param2)) { // Play sound FX getSound()->playLocomotiveSound(); params->param2 = 225 * (4 * rnd(5) + 20); params->param6 = 0; - UPDATE_PARAM_PROC_END + } label_processStations: // Process stations - TIME_CHECK_CALLBACK_2(kTime1039500, params->param7, 1, setup_savegame, kSavegameTypeTime, kTimeNone); + if (getState()->time > kTime1039500 && !params->param7) { + params->param7 = 1; + setCallback(1); + setup_savegame(kSavegameTypeTime, kTimeNone); + + break; + } label_enter_epernay: // Entering Epernay station - TIME_CHECK_CALLBACK_2(kTimeEnterEpernay, params->param8, 1, setup_enterStation, "Epernay", kCityEpernay); + if (timeCheckEnterStation(kTimeEnterEpernay, params->param8, 1, "Epernay", kCityEpernay)) + break; label_exit_epernay: // Exiting Epernay station @@ -445,19 +444,23 @@ label_enter_chalons: goto label_exit_strasbourg; // Entering Chalons station - TIME_CHECK_CALLBACK_2(kTimeEnterChalons, CURRENT_PARAM(1, 3), 5, setup_enterStation, "Chalons", kCityChalons); + if (timeCheckEnterStation(kTimeEnterChalons, CURRENT_PARAM(1, 3), 5, "Chalons", kCityChalons)) + break; label_exit_chalons: // Exiting Chalons station - TIME_CHECK_CALLBACK_1(kTimeExitChalons, CURRENT_PARAM(1, 4), 6, setup_exitStation, "Chalons"); + if (timeCheckExitStation(kTimeExitChalons, CURRENT_PARAM(1, 4), 6, "Chalons")) + break; label_enter_barleduc: // Entering Bar-Le-Duc station - TIME_CHECK_CALLBACK_2(kTimeCityBarLeDuc, CURRENT_PARAM(1, 5), 7, setup_enterStation, "BarLeDuc", kCityBarleduc); + if (timeCheckEnterStation(kTimeCityBarLeDuc, CURRENT_PARAM(1, 5), 7, "BarLeDuc", kCityBarleduc)) + break; label_exit_barleduc: // Exiting Bar-Le-Duc station - TIME_CHECK_CALLBACK_1(kTimeExitBarLeDuc, CURRENT_PARAM(1, 6), 8, setup_exitStation, "BarLeDuc"); + if (timeCheckExitStation(kTimeExitBarLeDuc, CURRENT_PARAM(1, 6), 8, "BarLeDuc")) + break; label_enter_nancy: if (getState()->time > kTime1260000 && !CURRENT_PARAM(1, 7)) { @@ -466,50 +469,67 @@ label_enter_nancy: } // Entering Nancy station - TIME_CHECK_CALLBACK_2(kTimeCityNancy, CURRENT_PARAM(1, 8), 9, setup_enterStation, "Nancy", kCityNancy); + if (timeCheckEnterStation(kTimeCityNancy, CURRENT_PARAM(1, 8), 9, "Nancy", kCityNancy)) + break; label_exit_nancy: // Exiting Nancy station - TIME_CHECK_CALLBACK_1(kTimeExitNancy, CURRENT_PARAM(2, 1), 10, setup_exitStation, "Nancy"); + if (timeCheckExitStation(kTimeExitNancy, CURRENT_PARAM(2, 1), 10, "Nancy")) + break; label_enter_luneville: // Entering Luneville station - TIME_CHECK_CALLBACK_2(kTimeCityLuneville, CURRENT_PARAM(2, 2), 11, setup_enterStation, "Luneville", kCityLuneville); + if (timeCheckEnterStation(kTimeCityLuneville, CURRENT_PARAM(2, 2), 11, "Luneville", kCityLuneville)) + break; label_exit_luneville: // Exiting Luneville station - TIME_CHECK_CALLBACK_1(kTimeExitLuneville, CURRENT_PARAM(2, 3), 12, setup_exitStation, "Luneville"); + if (timeCheckExitStation(kTimeExitLuneville, CURRENT_PARAM(2, 3), 12, "Luneville")) + break; label_enter_avricourt: // Entering Avricourt station - TIME_CHECK_CALLBACK_2(kTimeCityAvricourt, CURRENT_PARAM(2, 4), 13, setup_enterStation, "Avricourt", kCityAvricourt); + if (timeCheckEnterStation(kTimeCityAvricourt, CURRENT_PARAM(2, 4), 13, "Avricourt", kCityAvricourt)) + break; label_exit_avricourt: // Exiting Avricourt station - TIME_CHECK_CALLBACK_1(kTimeExitAvricourt, CURRENT_PARAM(2, 5), 14, setup_exitStation, "Avricourt"); + if (timeCheckExitStation(kTimeExitAvricourt, CURRENT_PARAM(2, 5), 14, "Avricourt")) + break; label_enter_deutschavricourt: // Entering Deutsch-Avricourt station - TIME_CHECK_CALLBACK_2(kTimeCityDeutschAvricourt, CURRENT_PARAM(2, 6), 15, setup_enterStation, "DeutschA", kCityDeutschAvricourt); + if (timeCheckEnterStation(kTimeCityDeutschAvricourt, CURRENT_PARAM(2, 6), 15, "DeutschA", kCityDeutschAvricourt)) + break; label_exit_deutschavricourt: // Exiting Avricourt station - TIME_CHECK_CALLBACK_1(kTimeExitDeutschAvricourt, CURRENT_PARAM(2, 7), 16, setup_exitStation, "DeutschA"); + if (timeCheckExitStation(kTimeExitDeutschAvricourt, CURRENT_PARAM(2, 7), 16, "DeutschA")) + break; label_enter_strasbourg: - TIME_CHECK_CALLBACK_2(kTimeCityStrasbourg, CURRENT_PARAM(2, 8), 17, setup_savegame, kSavegameTypeTime, kTimeNone); + if (getState()->time > kTimeCityStrasbourg && !CURRENT_PARAM(2, 8)) { + CURRENT_PARAM(2, 8) = 1; + setCallback(17); + setup_savegame(kSavegameTypeTime, kTimeNone); + + break; + } label_exit_strasbourg: // Exiting Strasbourg station - TIME_CHECK_CALLBACK_1(kTimeExitStrasbourg, CURRENT_PARAM(3, 1), 19, setup_exitStation, "Strasbou"); + if (timeCheckExitStation(kTimeExitStrasbourg, CURRENT_PARAM(3, 1), 19, "Strasbou")) + break; label_enter_badenoos: // Entering Baden Oos station - TIME_CHECK_CALLBACK_2(kTimeCityBadenOos, CURRENT_PARAM(3, 2), 20, setup_enterStation, "BadenOos", kCityBadenOos); + if (timeCheckEnterStation(kTimeCityBadenOos, CURRENT_PARAM(3, 2), 20, "BadenOos", kCityBadenOos)) + break; label_exit_badenoos: // Exiting Baden Oos station - TIME_CHECK_CALLBACK_1(kTimeExitBadenOos, CURRENT_PARAM(3, 3), 21, setup_exitStation, "BadenOos"); + if (timeCheckExitStation(kTimeExitBadenOos, CURRENT_PARAM(3, 3), 21, "BadenOos")) + break; label_chapter1_next: if (getState()->time > kTimeChapter1End3 && ! CURRENT_PARAM(3, 4)) { @@ -524,43 +544,43 @@ label_chapter1_next: getSavePoints()->push(kEntityChapters, kEntityTrain, kActionTrainStopRunning); if (getEntityData(kEntityPlayer)->location != kLocationOutsideTrain) { - PLAY_STEAM(); + playSteam(); break; } if (getEntities()->isOutsideAlexeiWindow()) { getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49); - PLAY_STEAM(); + playSteam(); break; } if (getEntities()->isOutsideAnnaWindow()) { getScenes()->loadSceneFromPosition(kCarRedSleeping, 49); - PLAY_STEAM(); + playSteam(); break; } CarIndex car = getEntityData(kEntityPlayer)->car; if (car < kCarRedSleeping || car > kCarCoalTender) { if (car < kCarBaggageRear || car > kCarGreenSleeping) { - PLAY_STEAM(); + playSteam(); break; } if (getEntities()->isPlayerPosition(kCarGreenSleeping, 98)) { getSound()->playSound(kEntityPlayer, "LIB015"); getScenes()->loadSceneFromPosition(kCarGreenSleeping, 71); - PLAY_STEAM(); + playSteam(); break; } getScenes()->loadSceneFromPosition(kCarGreenSleeping, 82); - PLAY_STEAM(); + playSteam(); break; } getScenes()->loadSceneFromPosition(kCarRestaurant, 82); - PLAY_STEAM(); + playSteam(); break; } @@ -817,7 +837,8 @@ IMPLEMENT_FUNCTION(12, Chapters, chapter2Handler) if (!getProgress().isTrainRunning) break; - UPDATE_PARAM(params->param2, getState()->timeTicks, params->param1); + if (!Entity::updateParameter(params->param2, getState()->timeTicks, params->param1)) + break; getSound()->playLocomotiveSound(); @@ -903,15 +924,15 @@ IMPLEMENT_FUNCTION(15, Chapters, chapter3Handler) case kActionNone: if (getProgress().isTrainRunning) { - UPDATE_PARAM_PROC(params->param4, getState()->timeTicks, params->param1) + if (Entity::updateParameter(params->param4, getState()->timeTicks, params->param1)) { getSound()->playLocomotiveSound(); params->param1 = 225 * (4 * rnd(5) + 20); params->param4 = 0; - UPDATE_PARAM_PROC_END + } } - UPDATE_PARAM_PROC(params->param5, getState()->timeTicks, params->param2) + if (Entity::updateParameter(params->param5, getState()->timeTicks, params->param2)) { switch (rnd(2)) { default: break; @@ -927,30 +948,38 @@ IMPLEMENT_FUNCTION(15, Chapters, chapter3Handler) params->param2 = 225 * (4 * rnd(6) + 8); params->param5 = 0; - UPDATE_PARAM_PROC_END + } - TIME_CHECK_CALLBACK_2(kTimeEnterSalzbourg, params->param6, 1, setup_enterStation, "Salzburg", kCitySalzbourg); + if (timeCheckEnterStation(kTimeEnterSalzbourg, params->param6, 1, "Salzburg", kCitySalzbourg)) + break; label_callback_1: - TIME_CHECK_CALLBACK_1(kTimeExitSalzbourg, params->param7, 2, setup_exitStation, "Salzburg"); + if (timeCheckExitStation(kTimeExitSalzbourg, params->param7, 2, "Salzburg")) + break; label_callback_2: - TIME_CHECK_CALLBACK_2(kTimeEnterAttnangPuchheim, params->param8, 3, setup_enterStation, "Attnang", kCityAttnangPuchheim); + if (timeCheckEnterStation(kTimeEnterAttnangPuchheim, params->param8, 3, "Attnang", kCityAttnangPuchheim)) + break; label_callback_3: - TIME_CHECK_CALLBACK_1(kTimeExitAttnangPuchheim, CURRENT_PARAM(1, 1), 4, setup_exitStation, "Attnang"); + if (timeCheckExitStation(kTimeExitAttnangPuchheim, CURRENT_PARAM(1, 1), 4, "Attnang")) + break; label_callback_4: - TIME_CHECK_CALLBACK_2(kTimeEnterWels, CURRENT_PARAM(1, 2), 5, setup_enterStation, "Wels", kCityWels); + if (timeCheckEnterStation(kTimeEnterWels, CURRENT_PARAM(1, 2), 5, "Wels", kCityWels)) + break; label_callback_5: - TIME_CHECK_CALLBACK_1(kTimeEnterWels, CURRENT_PARAM(1, 3), 6, setup_exitStation, "Wels"); + if (timeCheckExitStation(kTimeEnterWels, CURRENT_PARAM(1, 3), 6, "Wels")) + break; label_callback_6: - TIME_CHECK_CALLBACK_2(kTimeEnterLinz, CURRENT_PARAM(1, 4), 7, setup_enterStation, "Linz", kCityLinz); + if (timeCheckEnterStation(kTimeEnterLinz, CURRENT_PARAM(1, 4), 7, "Linz", kCityLinz)) + break; label_callback_7: - TIME_CHECK_CALLBACK_1(kTimeCityLinz, CURRENT_PARAM(1, 5), 8, setup_exitStation, "Linz"); + if (timeCheckExitStation(kTimeCityLinz, CURRENT_PARAM(1, 5), 8, "Linz")) + break; label_callback_8: if (getState()->time > kTime2187000 && !CURRENT_PARAM(1, 6)) { @@ -958,7 +987,7 @@ label_callback_8: getState()->timeDelta = 5; } - TIME_CHECK_CALLBACK_2(kTimeCityVienna, CURRENT_PARAM(1, 7), 9, setup_enterStation, "Vienna", kCityVienna); + timeCheckEnterStation(kTimeCityVienna, CURRENT_PARAM(1, 7), 9, "Vienna", kCityVienna); break; case kActionEndSound: @@ -1202,15 +1231,15 @@ IMPLEMENT_FUNCTION(19, Chapters, chapter4Handler) case kActionNone: if (getProgress().isTrainRunning) { - UPDATE_PARAM_PROC(params->param6, getState()->timeTicks, params->param4); + if (Entity::updateParameter(params->param6, getState()->timeTicks, params->param4)) { getSound()->playLocomotiveSound(); params->param4 = 225 * (4 * rnd(5) + 20); params->param6 = 0; - UPDATE_PARAM_PROC_END + } } - UPDATE_PARAM_PROC(params->param7, getState()->timeTicks, params->param5) + if (Entity::updateParameter(params->param7, getState()->timeTicks, params->param5)) { switch (rnd(2)) { default: break; @@ -1226,12 +1255,14 @@ IMPLEMENT_FUNCTION(19, Chapters, chapter4Handler) params->param5 = 225 * (4 * rnd(6) + 8); params->param7 = 0; - UPDATE_PARAM_PROC_END + } - TIME_CHECK_CALLBACK_2(kTimeEnterPoszony, params->param8, 1, setup_enterStation, "Pozsony", kCityPoszony); + if (timeCheckEnterStation(kTimeEnterPoszony, params->param8, 1, "Pozsony", kCityPoszony)) + break; label_exitPozsony: - TIME_CHECK_CALLBACK_1(kTimeExitPoszony, CURRENT_PARAM(1, 1), 2, setup_exitStation, "Pozsony"); + if (timeCheckExitStation(kTimeExitPoszony, CURRENT_PARAM(1, 1), 2, "Pozsony")) + break; label_enterGalanta: if (getObjects()->get(kObjectCompartment1).location2 == kObjectLocation1) { @@ -1244,10 +1275,12 @@ label_enterGalanta: if (params->param1) goto label_callback_4; - TIME_CHECK_CALLBACK_2(kTimeEnterGalanta, CURRENT_PARAM(1, 3), 3, setup_enterStation, "Galanta", kCityGalanta); + if (timeCheckEnterStation(kTimeEnterGalanta, CURRENT_PARAM(1, 3), 3, "Galanta", kCityGalanta)) + break; label_exitGalanta: - TIME_CHECK_CALLBACK_1(kTimeExitGalanta, CURRENT_PARAM(1, 4), 4, setup_exitStation, "Galanta"); + if (timeCheckExitStation(kTimeExitGalanta, CURRENT_PARAM(1, 4), 4, "Galanta")) + break; label_callback_4: if (getState()->time > kTime2470500 && !CURRENT_PARAM(1, 5)) { @@ -1280,43 +1313,43 @@ label_callback_4: getSavePoints()->push(kEntityChapters, kEntityTrain, kActionTrainStopRunning); if (getEntityData(kEntityPlayer)->location != kLocationOutsideTrain) { - PLAY_STEAM(); + playSteam(); break; } if (getEntities()->isOutsideAlexeiWindow()) { getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49); - PLAY_STEAM(); + playSteam(); break; } if (getEntities()->isOutsideAnnaWindow()) { getScenes()->loadSceneFromPosition(kCarRedSleeping, 49); - PLAY_STEAM(); + playSteam(); break; } CarIndex car = getEntityData(kEntityPlayer)->car; if (car < kCarRedSleeping || car > kCarCoalTender) { if (car < kCarBaggageRear || car > kCarGreenSleeping) { - PLAY_STEAM(); + playSteam(); break; } if (getEntities()->isPlayerPosition(kCarGreenSleeping, 98)) { getSound()->playSound(kEntityPlayer, "LIB015"); getScenes()->loadSceneFromPosition(kCarGreenSleeping, 71); - PLAY_STEAM(); + playSteam(); break; } getScenes()->loadSceneFromPosition(kCarGreenSleeping, 82); - PLAY_STEAM(); + playSteam(); break; } getScenes()->loadSceneFromPosition(kCarRestaurant, 82); - PLAY_STEAM(); + playSteam(); break; } @@ -1815,7 +1848,37 @@ void Chapters::enterExitHelper(bool isEnteringStation) { ENTITY_PARAM(0, 3) = 1; } - CALLBACK_ACTION(); + callbackAction(); +} + +void Chapters::playSteam() { + getSoundQueue()->resetState(); + getSound()->playSteam((CityIndex)ENTITY_PARAM(0, 4)); + ENTITY_PARAM(0, 2) = 0; +} + +bool Chapters::timeCheckEnterStation(TimeValue timeValue, uint ¶meter, byte callback, const char *sequence, CityIndex cityIndex) { + if (getState()->time > timeValue && !parameter) { + parameter = 1; + setCallback(callback); + setup_enterStation(sequence, cityIndex); + + return true; + } + + return false; +} + +bool Chapters::timeCheckExitStation(TimeValue timeValue, uint ¶meter, byte callback, const char *sequence) { + if (getState()->time > timeValue && !parameter) { + parameter = 1; + setCallback(callback); + setup_exitStation(sequence); + + return true; + } + + return false; } } // End of namespace LastExpress diff --git a/engines/lastexpress/entities/chapters.h b/engines/lastexpress/entities/chapters.h index 353d3a6b5b..ddb3de3bea 100644 --- a/engines/lastexpress/entities/chapters.h +++ b/engines/lastexpress/entities/chapters.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_CHAPTERS_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { @@ -154,8 +153,11 @@ public: DECLARE_FUNCTION(chapter5Handler) private: + bool timeCheckEnterStation(TimeValue timeValue, uint ¶meter, byte callback, const char *sequence, CityIndex cityIndex); + bool timeCheckExitStation(TimeValue timeValue, uint ¶meter, byte callback, const char *sequence); void enterExitStation(const SavePoint &savepoint, bool isEnteringStation); void enterExitHelper(bool isEnteringStation); + void playSteam(); }; } // End of namespace LastExpress diff --git a/engines/lastexpress/entities/cooks.cpp b/engines/lastexpress/entities/cooks.cpp index 42e888cc7c..5e8a2df8cb 100644 --- a/engines/lastexpress/entities/cooks.cpp +++ b/engines/lastexpress/entities/cooks.cpp @@ -29,9 +29,7 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" namespace LastExpress { @@ -96,7 +94,7 @@ IMPLEMENT_FUNCTION(3, Cooks, function3) case kActionDrawScene: if (!getEntities()->isInKitchen(kEntityPlayer)) { getEntities()->clearSequences(kEntityCooks); - CALLBACK_ACTION(); + callbackAction(); break; } @@ -108,7 +106,7 @@ IMPLEMENT_FUNCTION(3, Cooks, function3) if (!getEntities()->hasValidFrame(kEntityCooks)) { getSound()->playSound(kEntityCooks, "LIB015"); getEntities()->clearSequences(kEntityCooks); - CALLBACK_ACTION(); + callbackAction(); } break; } @@ -122,7 +120,7 @@ IMPLEMENT_FUNCTION(3, Cooks, function3) if (params->param1 && !getEntities()->hasValidFrame(kEntityCooks)) { getSound()->playSound(kEntityCooks, "LIB015"); getEntities()->clearSequences(kEntityCooks); - CALLBACK_ACTION(); + callbackAction(); } break; @@ -182,7 +180,7 @@ IMPLEMENT_FUNCTION(4, Cooks, function4) case kActionDrawScene: if (!getEntities()->isInKitchen(kEntityPlayer)) { getEntities()->clearSequences(kEntityCooks); - CALLBACK_ACTION(); + callbackAction(); break; } @@ -194,7 +192,7 @@ IMPLEMENT_FUNCTION(4, Cooks, function4) if (!getEntities()->hasValidFrame(kEntityCooks)) { getSound()->playSound(kEntityCooks, "LIB015"); getEntities()->clearSequences(kEntityCooks); - CALLBACK_ACTION(); + callbackAction(); } break; } @@ -208,7 +206,7 @@ IMPLEMENT_FUNCTION(4, Cooks, function4) if (params->param1 && !getEntities()->hasValidFrame(kEntityCooks)) { getSound()->playSound(kEntityCooks, "LIB015"); getEntities()->clearSequences(kEntityCooks); - CALLBACK_ACTION(); + callbackAction(); } break; @@ -241,7 +239,7 @@ IMPLEMENT_FUNCTION(5, Cooks, chapter1) break; case kActionNone: - TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler); + Entity::timeCheck(kTimeChapter1, params->param1, WRAP_SETUP_FUNCTION(Cooks, setup_chapter1Handler)); break; case kActionDefault: @@ -262,7 +260,8 @@ IMPLEMENT_FUNCTION(6, Cooks, chapter1Handler) break; case kActionNone: - UPDATE_PARAM(params->param4, getState()->time, params->param2); + if (!Entity::updateParameter(params->param4, getState()->time, params->param2)) + break; // Broken plate sound getSound()->playSound(kEntityPlayer, "LIB122", getSound()->getSoundFlag(kEntityCooks)); @@ -375,7 +374,8 @@ IMPLEMENT_FUNCTION(9, Cooks, chapter2Handler) break; case kActionNone: - UPDATE_PARAM(params->param3, getState()->time, params->param1); + if (!Entity::updateParameter(params->param3, getState()->time, params->param1)) + break; // Broken plate sound getSound()->playSound(kEntityPlayer, "LIB122", getSound()->getSoundFlag(kEntityCooks)); @@ -434,12 +434,12 @@ IMPLEMENT_FUNCTION(11, Cooks, chapter3Handler) break; case kActionNone: - UPDATE_PARAM_PROC(params->param4, getState()->time, params->param2) + if (Entity::updateParameter(params->param4, getState()->time, params->param2)) { // Broken plate sound getSound()->playSound(kEntityPlayer, "LIB122", getSound()->getSoundFlag(kEntityCooks)); params->param2 = 225 * (4 * rnd(30) + 120); params->param4 = 0; - UPDATE_PARAM_PROC_END + } if (getState()->time > kTime2079000 && !params->param5) { params->param1 = 0; @@ -526,7 +526,8 @@ IMPLEMENT_FUNCTION(13, Cooks, chapter4Handler) break; case kActionNone: - UPDATE_PARAM(params->param3, getState()->time, params->param1) + if (!Entity::updateParameter(params->param3, getState()->time, params->param1)) + break; // Broken plate sound getSound()->playSound(kEntityPlayer, "LIB122", getSound()->getSoundFlag(kEntityCooks)); diff --git a/engines/lastexpress/entities/cooks.h b/engines/lastexpress/entities/cooks.h index 3ab7d35161..f01d0b2ca0 100644 --- a/engines/lastexpress/entities/cooks.h +++ b/engines/lastexpress/entities/cooks.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_COOKS_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/coudert.cpp b/engines/lastexpress/entities/coudert.cpp index c3e7e37b88..b604277903 100644 --- a/engines/lastexpress/entities/coudert.cpp +++ b/engines/lastexpress/entities/coudert.cpp @@ -32,22 +32,11 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" namespace LastExpress { -#define SAVEGAME_BLOOD_JACKET() \ - if (getProgress().jacket == kJacketBlood \ - && getEntities()->isDistanceBetweenEntities(kEntityCoudert, kEntityPlayer, 1000) \ - && !getEntities()->isInsideCompartments(kEntityPlayer) \ - && !getEntities()->checkFields10(kEntityPlayer)) { \ - setCallback(1); \ - setup_savegame(kSavegameTypeEvent, kEventMertensBloodJacket); \ - } - Coudert::Coudert(LastExpressEngine *engine) : Entity(engine, kEntityCoudert) { ADD_CALLBACK_FUNCTION(Coudert, reset); ADD_CALLBACK_FUNCTION(Coudert, bloodJacket); @@ -126,11 +115,11 @@ IMPLEMENT_FUNCTION_S(2, Coudert, bloodJacket) break; case kActionNone: - SAVEGAME_BLOOD_JACKET(); + Entity::savegameBloodJacket(); break; case kActionExitCompartment: - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: @@ -153,7 +142,7 @@ IMPLEMENT_FUNCTION_SI(3, Coudert, enterExitCompartment, ObjectIndex) break; case kActionNone: - SAVEGAME_BLOOD_JACKET(); + Entity::savegameBloodJacket(); return; case kActionCallback: @@ -175,15 +164,15 @@ IMPLEMENT_FUNCTION(4, Coudert, callbackActionOnDirection) case kActionNone: if (getData()->direction != kDirectionRight) { - CALLBACK_ACTION(); + callbackAction(); break; } - SAVEGAME_BLOOD_JACKET(); + Entity::savegameBloodJacket(); break; case kActionExitCompartment: - CALLBACK_ACTION(); + callbackAction(); break; case kActionCallback: @@ -202,7 +191,7 @@ IMPLEMENT_FUNCTION_SIII(5, Coudert, enterExitCompartment2, ObjectIndex, EntityPo break; case kActionNone: - SAVEGAME_BLOOD_JACKET(); + Entity::savegameBloodJacket(); return; case kActionCallback: @@ -223,11 +212,11 @@ IMPLEMENT_FUNCTION_S(6, Coudert, playSound) break; case kActionNone: - SAVEGAME_BLOOD_JACKET(); + Entity::savegameBloodJacket(); break; case kActionEndSound: - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: @@ -252,11 +241,11 @@ IMPLEMENT_FUNCTION_NOSETUP(7, Coudert, playSound16) break; case kActionNone: - SAVEGAME_BLOOD_JACKET(); + Entity::savegameBloodJacket(); break; case kActionEndSound: - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: @@ -296,7 +285,7 @@ IMPLEMENT_FUNCTION_II(9, Coudert, updateEntity, CarIndex, EntityPosition) if (getEntities()->updateEntity(kEntityCoudert, (CarIndex)params->param1, (EntityPosition)params->param2)) { getData()->inventoryItem = kItemNone; - CALLBACK_ACTION(); + callbackAction(); } break; } @@ -332,7 +321,7 @@ IMPLEMENT_FUNCTION_II(9, Coudert, updateEntity, CarIndex, EntityPosition) params->param3 = kItemInvalid; if (getEntities()->updateEntity(kEntityCoudert, (CarIndex)params->param1, (EntityPosition)params->param2)) - CALLBACK_ACTION(); + callbackAction(); break; case kActionCallback: @@ -365,11 +354,12 @@ IMPLEMENT_FUNCTION_I(10, Coudert, updateFromTime, uint32) break; case kActionNone: - SAVEGAME_BLOOD_JACKET(); + Entity::savegameBloodJacket(); - UPDATE_PARAM(params->param2, getState()->time, params->param1); + if (!Entity::updateParameter(params->param2, getState()->time, params->param1)) + break; - CALLBACK_ACTION(); + callbackAction(); break; case kActionCallback: @@ -388,11 +378,12 @@ IMPLEMENT_FUNCTION_I(11, Coudert, updateFromTicks, uint32) break; case kActionNone: - SAVEGAME_BLOOD_JACKET(); + Entity::savegameBloodJacket(); - UPDATE_PARAM(params->param2, getState()->timeTicks, params->param1); + if (!Entity::updateParameter(params->param2, getState()->timeTicks, params->param1)) + break; - CALLBACK_ACTION(); + callbackAction(); break; case kActionCallback: @@ -412,7 +403,7 @@ IMPLEMENT_FUNCTION_I(12, Coudert, excuseMe, EntityIndex) return; if (getSoundQueue()->isBuffered(kEntityCoudert)) { - CALLBACK_ACTION(); + callbackAction(); return; } @@ -452,7 +443,7 @@ IMPLEMENT_FUNCTION_I(12, Coudert, excuseMe, EntityIndex) getSound()->playSound(kEntityCoudert, "JAC1112E"); } - CALLBACK_ACTION(); + callbackAction(); IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// @@ -462,7 +453,7 @@ IMPLEMENT_FUNCTION_II(13, Coudert, function13, bool, EntityIndex) break; case kActionNone: - SAVEGAME_BLOOD_JACKET(); + Entity::savegameBloodJacket(); if (!params->param2 && !params->param3) { @@ -487,7 +478,8 @@ IMPLEMENT_FUNCTION_II(13, Coudert, function13, bool, EntityIndex) } } - UPDATE_PARAM(params->param5, getState()->timeTicks, 225); + if (!Entity::updateParameter(params->param5, getState()->timeTicks, 225)) + break; getData()->inventoryItem = kItemNone; setCallback(5); @@ -561,7 +553,7 @@ IMPLEMENT_FUNCTION_II(13, Coudert, function13, bool, EntityIndex) case 5: case 6: case 7: - CALLBACK_ACTION(); + callbackAction(); break; case 9: @@ -584,7 +576,7 @@ IMPLEMENT_FUNCTION_I(14, Coudert, function14, EntityIndex) break; case kActionNone: - SAVEGAME_BLOOD_JACKET(); + Entity::savegameBloodJacket(); break; case kActionDefault: @@ -629,7 +621,7 @@ IMPLEMENT_FUNCTION_I(14, Coudert, function14, EntityIndex) break; case 5: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -706,7 +698,7 @@ IMPLEMENT_FUNCTION_I(15, Coudert, function15, bool) break; case 5: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -724,7 +716,7 @@ IMPLEMENT_FUNCTION(16, Coudert, function16) ENTITY_PARAM(2, 1) = 0; getInventory()->setLocationAndProcess(kItem5, kObjectLocation1); - CALLBACK_ACTION(); + callbackAction(); break; } @@ -743,7 +735,7 @@ IMPLEMENT_FUNCTION(16, Coudert, function16) if (!getEntities()->isPlayerPosition(kCarRedSleeping, 2)) getData()->entityPosition = kPosition_2088; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -761,7 +753,7 @@ IMPLEMENT_FUNCTION_I(17, Coudert, function17, bool) if (ENTITY_PARAM(2, 1)) { ENTITY_PARAM(2, 1) = 0; - CALLBACK_ACTION(); + callbackAction(); break; } @@ -789,7 +781,7 @@ IMPLEMENT_FUNCTION_I(17, Coudert, function17, bool) case 1: case 2: case 3: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -817,7 +809,7 @@ IMPLEMENT_FUNCTION(18, Coudert, function18) getEntities()->drawSequenceLeft(kEntityCoudert, "627K"); getScenes()->loadSceneFromItemPosition(kItem5); - CALLBACK_ACTION(); + callbackAction(); break; } @@ -849,7 +841,7 @@ IMPLEMENT_FUNCTION(18, Coudert, function18) break; case 2: - CALLBACK_ACTION(); + callbackAction(); break; case 3: @@ -857,7 +849,7 @@ IMPLEMENT_FUNCTION(18, Coudert, function18) ENTITY_PARAM(0, 1) = 0; getSavePoints()->push(kEntityCoudert, kEntityCoudert, kActionDrawScene); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -876,14 +868,14 @@ IMPLEMENT_FUNCTION_I(19, Coudert, function19, bool) || ENTITY_PARAM(2, 4) || ENTITY_PARAM(2, 6)) { getInventory()->setLocationAndProcess(kItem5, kObjectLocation1); ENTITY_PARAM(2, 1) = 1; - CALLBACK_ACTION(); + callbackAction(); break; } if (ENTITY_PARAM(0, 3) || ENTITY_PARAM(0, 5) || ENTITY_PARAM(0, 4)) { getScenes()->loadSceneFromItemPosition(kItem5); ENTITY_PARAM(2, 1) = 1; - CALLBACK_ACTION(); + callbackAction(); break; } @@ -903,7 +895,7 @@ IMPLEMENT_FUNCTION_I(19, Coudert, function19, bool) getEntities()->drawSequenceLeft(kEntityCoudert, ENTITY_PARAM(0, 2) ? "627B" : "627E"); ENTITY_PARAM(0, 1) = 0; - CALLBACK_ACTION(); + callbackAction(); } break; } @@ -916,11 +908,12 @@ IMPLEMENT_FUNCTION_II(20, Coudert, function20, ObjectIndex, ObjectIndex) break; case kActionNone: - UPDATE_PARAM_PROC(CURRENT_PARAM(1, 3), getState()->time, 300) + if (Entity::updateParameter(CURRENT_PARAM(1, 3), getState()->time, 300)) { getSound()->playSound(kEntityPlayer, "ZFX1004", getSound()->getSoundFlag(kEntityCoudert)); - UPDATE_PARAM_PROC_END + } - UPDATE_PARAM(CURRENT_PARAM(1, 4), getState()->time, 900); + if (!Entity::updateParameter(CURRENT_PARAM(1, 4), getState()->time, 900)) + break; getObjects()->updateLocation2((ObjectIndex)params->param1, kObjectLocation1); @@ -930,7 +923,7 @@ IMPLEMENT_FUNCTION_II(20, Coudert, function20, ObjectIndex, ObjectIndex) if (params->param2) getObjects()->update((ObjectIndex)params->param2, (EntityIndex)params->param7, (ObjectLocation)params->param8, (CursorStyle)CURRENT_PARAM(1, 1), (CursorStyle)CURRENT_PARAM(1, 2)); - CALLBACK_ACTION(); + callbackAction(); break; case kActionKnock: @@ -999,7 +992,8 @@ IMPLEMENT_FUNCTION(21, Coudert, function21) case kActionNone: if (!params->param1) { - UPDATE_PARAM(params->param2, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param2, getState()->timeTicks, 75)) + break; setCallback(3); setup_enterExitCompartment("627Zh", kObjectCompartmentH); @@ -1044,7 +1038,7 @@ IMPLEMENT_FUNCTION(21, Coudert, function21) case 5: getData()->location = kLocationOutsideCompartment; - CALLBACK_ACTION(); + callbackAction(); break; case 6: @@ -1073,7 +1067,7 @@ IMPLEMENT_FUNCTION(21, Coudert, function21) getData()->location = kLocationOutsideCompartment; getSavePoints()->push(kEntityCoudert, kEntityIvo, kAction123852928); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1100,7 +1094,8 @@ IMPLEMENT_FUNCTION(22, Coudert, function22) case kActionNone: if (!params->param1) { - UPDATE_PARAM(params->param2, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param2, getState()->timeTicks, 75)) + break; setCallback(3); setup_enterExitCompartment("627Rg", kObjectCompartmentG); @@ -1145,7 +1140,7 @@ IMPLEMENT_FUNCTION(22, Coudert, function22) case 5: getData()->location = kLocationOutsideCompartment; - CALLBACK_ACTION(); + callbackAction(); break; case 6: @@ -1174,7 +1169,7 @@ IMPLEMENT_FUNCTION(22, Coudert, function22) getData()->location = kLocationOutsideCompartment; getSavePoints()->push(kEntityCoudert, kEntityMilos, kAction123852928); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1225,7 +1220,7 @@ IMPLEMENT_FUNCTION(23, Coudert, function23) case 3: getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentF, true); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1296,7 +1291,7 @@ IMPLEMENT_FUNCTION(25, Coudert, function25) getData()->location = kLocationOutsideCompartment; getSavePoints()->push(kEntityCoudert, kEntityRebecca, kAction123852928); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1311,7 +1306,8 @@ IMPLEMENT_FUNCTION(26, Coudert, function26) case kActionNone: if (params->param1) { - UPDATE_PARAM(params->param2, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param2, getState()->timeTicks, 75)) + break; setCallback(3); setup_enterExitCompartment2("627Zd", kObjectCompartmentD, kPosition_5790, kPosition_6130); @@ -1357,7 +1353,7 @@ IMPLEMENT_FUNCTION(26, Coudert, function26) case 5: getData()->location = kLocationOutsideCompartment; - CALLBACK_ACTION(); + callbackAction(); break; case 6: @@ -1375,7 +1371,7 @@ IMPLEMENT_FUNCTION(26, Coudert, function26) getData()->location = kLocationOutsideCompartment; getSavePoints()->push(kEntityCoudert, kEntityMmeBoutarel, kAction123852928); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1402,7 +1398,8 @@ IMPLEMENT_FUNCTION(27, Coudert, function27) case kActionNone: if (!params->param1) { - UPDATE_PARAM(params->param2, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param2, getState()->timeTicks, 75)) + break; setCallback(3); setup_enterExitCompartment2("627Rc", kObjectCompartmentC, kPosition_6470, kPosition_6130); @@ -1446,7 +1443,7 @@ IMPLEMENT_FUNCTION(27, Coudert, function27) case 5: getData()->location = kLocationOutsideCompartment; - CALLBACK_ACTION(); + callbackAction(); break; case 6: @@ -1473,7 +1470,7 @@ IMPLEMENT_FUNCTION(27, Coudert, function27) getData()->location = kLocationOutsideCompartment; getSavePoints()->push(kEntityCoudert, kEntityBoutarel, kAction123852928); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1515,7 +1512,7 @@ IMPLEMENT_FUNCTION_I(30, Coudert, function30, ObjectIndex) case kActionDefault: switch (parameters->param1) { default: - CALLBACK_ACTION(); + callbackAction(); // Stop processing here return; @@ -1615,7 +1612,7 @@ IMPLEMENT_FUNCTION_I(30, Coudert, function30, ObjectIndex) break; case 6: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1654,7 +1651,7 @@ IMPLEMENT_FUNCTION_I(31, Coudert, function31, uint32) case 2: case 3: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1699,7 +1696,7 @@ IMPLEMENT_FUNCTION(32, Coudert, function32) break; case 5: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1726,7 +1723,7 @@ IMPLEMENT_FUNCTION(33, Coudert, function33) setup_updateEntity(kCarRedSleeping, kPosition_540); } } else { - CALLBACK_ACTION(); + callbackAction(); } break; @@ -1763,7 +1760,7 @@ IMPLEMENT_FUNCTION(33, Coudert, function33) case 4: ENTITY_PARAM(2, 6) = 0; - CALLBACK_ACTION(); + callbackAction(); break; case 5: @@ -1808,7 +1805,7 @@ IMPLEMENT_FUNCTION(33, Coudert, function33) case 10: ENTITY_PARAM(2, 6) = 0; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1882,7 +1879,7 @@ IMPLEMENT_FUNCTION_I(34, Coudert, function34, bool) break; case 7: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1902,7 +1899,8 @@ IMPLEMENT_FUNCTION_I(35, Coudert, function35, bool) getScenes()->loadSceneFromPosition(kCarRestaurant, 65); } - UPDATE_PARAM(params->param2, getState()->time, 2700); + if (!Entity::updateParameter(params->param2, getState()->time, 2700)) + break; getSavePoints()->push(kEntityCoudert, kEntityMax, kActionMaxFreeFromCage); @@ -1951,7 +1949,7 @@ IMPLEMENT_FUNCTION_I(35, Coudert, function35, bool) break; case 4: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1965,7 +1963,7 @@ IMPLEMENT_FUNCTION(36, Coudert, chapter1) break; case kActionNone: - TIME_CHECK_CALLBACK(kTimeChapter1, params->param1, 1, setup_chapter1Handler) + Entity::timeCheckCallback(kTimeChapter1, params->param1, 1, WRAP_SETUP_FUNCTION(Coudert, setup_chapter1Handler)); break; case kActionDefault: @@ -2174,7 +2172,7 @@ IMPLEMENT_FUNCTION(39, Coudert, function39) getSavePoints()->push(kEntityCoudert, kEntityVerges, kAction167854368); ENTITY_PARAM(2, 2) = 0; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -2283,22 +2281,22 @@ label_callback_9: label_callback_10: if (getState()->time > kTime1189800 && !ENTITY_PARAM(0, 1) && !ENTITY_PARAM(2, 1)) { - UPDATE_PARAM_PROC(params->param3, getState()->time, 2700); + if (Entity::updateParameter(params->param3, getState()->time, 2700)) { ENTITY_PARAM(0, 2) = 1; ENTITY_PARAM(0, 1) = 1; getEntities()->drawSequenceLeft(kEntityCoudert, "697F"); params->param3 = 0; - UPDATE_PARAM_PROC_END + } } if (!ENTITY_PARAM(0, 2)) break; - TIME_CHECK_OBJECT(kTime1107000, params->param4, kObject111, kObjectLocation2); - TIME_CHECK_OBJECT(kTime1161000, params->param5, kObject111, kObjectLocation3); - TIME_CHECK_OBJECT(kTime1206000, params->param6, kObject111, kObjectLocation4); + timeCheckObject(kTime1107000, params->param4, kObject111, kObjectLocation2); + timeCheckObject(kTime1161000, params->param5, kObject111, kObjectLocation3); + timeCheckObject(kTime1206000, params->param6, kObject111, kObjectLocation4); break; case kAction1: @@ -2536,7 +2534,7 @@ IMPLEMENT_FUNCTION(41, Coudert, function41) case 18: getSavePoints()->push(kEntityCoudert, kEntityMilos, kAction208228224); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -2827,28 +2825,34 @@ label_callback_12: } label_callback_13: - TIME_CHECK_CALLBACK(kTime2088900, params->param1, 14, setup_function32); + if (Entity::timeCheckCallback(kTime2088900, params->param1, 14, WRAP_SETUP_FUNCTION(Coudert, setup_function32))) + break; label_callback_14: - TIME_CHECK_CALLBACK(kTime2119500, params->param2, 15, setup_function32); + if (Entity::timeCheckCallback(kTime2119500, params->param2, 15, WRAP_SETUP_FUNCTION(Coudert, setup_function32))) + break; label_callback_15: - TIME_CHECK_CALLBACK(kTime2138400, params->param3, 16, setup_function32); + if (Entity::timeCheckCallback(kTime2138400, params->param3, 16, WRAP_SETUP_FUNCTION(Coudert, setup_function32))) + break; label_callback_16: - TIME_CHECK_CALLBACK(kTime2147400, params->param4, 17, setup_function32); + if (Entity::timeCheckCallback(kTime2147400, params->param4, 17, WRAP_SETUP_FUNCTION(Coudert, setup_function32))) + break; label_callback_17: - TIME_CHECK_CALLBACK(kTime2160000, params->param5, 18, setup_function32); + if (Entity::timeCheckCallback(kTime2160000, params->param5, 18, WRAP_SETUP_FUNCTION(Coudert, setup_function32))) + break; label_callback_18: - TIME_CHECK_CALLBACK(kTime2205000, params->param6, 19, setup_function32); + if (Entity::timeCheckCallback(kTime2205000, params->param6, 19, WRAP_SETUP_FUNCTION(Coudert, setup_function32))) + break; label_callback_19: if (ENTITY_PARAM(0, 2)) { - TIME_CHECK_OBJECT(kTime2025000, params->param7, kObject111, kObjectLocation7); - TIME_CHECK_OBJECT(kTime2133000, params->param8, kObject111, kObjectLocation8); - TIME_CHECK_OBJECT(kTime2173500, CURRENT_PARAM(1, 1), kObject111, kObjectLocation9); + timeCheckObject(kTime2025000, params->param7, kObject111, kObjectLocation7); + timeCheckObject(kTime2133000, params->param8, kObject111, kObjectLocation8); + timeCheckObject(kTime2173500, CURRENT_PARAM(1, 1), kObject111, kObjectLocation9); } break; @@ -3051,7 +3055,7 @@ IMPLEMENT_FUNCTION(46, Coudert, function46) break; case 11: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -3116,7 +3120,7 @@ IMPLEMENT_FUNCTION_I(47, Coudert, function47, bool) break; case 7: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -3164,7 +3168,7 @@ IMPLEMENT_FUNCTION(48, Coudert, function48) break; case 5: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -3261,7 +3265,7 @@ IMPLEMENT_FUNCTION(49, Coudert, function49) break; case 11: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -3347,7 +3351,7 @@ IMPLEMENT_FUNCTION(50, Coudert, function50) break; case 9: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -3541,11 +3545,11 @@ label_callback_1: params->param2 = (uint)(getState()->time + 4500); if (params->param3 != kTimeInvalid) { - UPDATE_PARAM_PROC_TIME(params->param2, !getEntities()->isPlayerInCar(kCarRedSleeping), params->param3, 0) + if (Entity::updateParameterTime((TimeValue)params->param2, !getEntities()->isPlayerInCar(kCarRedSleeping), params->param3, 0)) { setCallback(2); setup_function55(); break; - UPDATE_PARAM_PROC_END + } } } @@ -3558,18 +3562,22 @@ label_callback_2: label_callback_3: if (!params->param1) { - TIME_CHECK_CALLBACK(kTime2394000, params->param4, 4, setup_function56); + if (Entity::timeCheckCallback(kTime2394000, params->param4, 4, WRAP_SETUP_FUNCTION(Coudert, setup_function56))) + break; label_callback_4: - TIME_CHECK_CALLBACK(kTime2434500, params->param5, 5, setup_function32); + if (Entity::timeCheckCallback(kTime2434500, params->param5, 5, WRAP_SETUP_FUNCTION(Coudert, setup_function32))) + break; label_callback_5: - TIME_CHECK_CALLBACK(kTime2448000, params->param6, 6, setup_function32); + if (Entity::timeCheckCallback(kTime2448000, params->param6, 6, WRAP_SETUP_FUNCTION(Coudert, setup_function32))) + break; } label_callback_6: if (getState()->time > kTime2538000 && !ENTITY_PARAM(0, 1) && !ENTITY_PARAM(2, 1)) { - UPDATE_PARAM(params->param7, getState()->time, 2700); + if (!Entity::updateParameter(params->param7, getState()->time, 2700)) + break; ENTITY_PARAM(0, 2) = 0; ENTITY_PARAM(0, 1) = 1; @@ -3696,7 +3704,7 @@ IMPLEMENT_FUNCTION(54, Coudert, function54) break; case 3: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -3764,7 +3772,7 @@ IMPLEMENT_FUNCTION(55, Coudert, function55) break; case 7: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -3868,7 +3876,7 @@ IMPLEMENT_FUNCTION(56, Coudert, function56) break; case 16: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -4034,7 +4042,8 @@ IMPLEMENT_FUNCTION(62, Coudert, function62) case kActionNone: if (params->param1) { - UPDATE_PARAM(params->param4, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param4, getState()->timeTicks, 75)) + break; params->param1 = 0; params->param2 = 1; @@ -4168,7 +4177,7 @@ void Coudert::visitCompartment(const SavePoint &savepoint, EntityPosition positi case 6: getData()->location = kLocationOutsideCompartment; - CALLBACK_ACTION(); + callbackAction(); break; } break; diff --git a/engines/lastexpress/entities/coudert.h b/engines/lastexpress/entities/coudert.h index 45d13ce9bb..8303c80a32 100644 --- a/engines/lastexpress/entities/coudert.h +++ b/engines/lastexpress/entities/coudert.h @@ -24,8 +24,6 @@ #define LASTEXPRESS_COUDERT_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" - namespace LastExpress { class LastExpressEngine; diff --git a/engines/lastexpress/entities/entity.cpp b/engines/lastexpress/entities/entity.cpp index e136ca4776..2deca291f6 100644 --- a/engines/lastexpress/entities/entity.cpp +++ b/engines/lastexpress/entities/entity.cpp @@ -22,23 +22,13 @@ #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" - #include "lastexpress/data/sequence.h" #include "lastexpress/game/action.h" #include "lastexpress/game/entities.h" -#include "lastexpress/game/logic.h" -#include "lastexpress/game/scenes.h" -#include "lastexpress/game/state.h" +#include "lastexpress/game/object.h" #include "lastexpress/game/savegame.h" -#include "lastexpress/game/savepoint.h" -#include "lastexpress/game/state.h" - -#include "lastexpress/sound/sound.h" - -#include "lastexpress/helpers.h" -#include "lastexpress/lastexpress.h" +#include "lastexpress/game/scenes.h" namespace LastExpress { @@ -55,6 +45,17 @@ EntityData::EntityCallData::~EntityCallData() { SAFE_DELETE(sequence3); } +void EntityData::EntityCallData::syncString(Common::Serializer &s, Common::String &string, int length) { + char seqName[13]; + memset(&seqName, 0, length); + + if (s.isSaving()) strcpy((char *)&seqName, string.c_str()); + s.syncBytes((byte *)&seqName, length); + + if (s.isLoading()) + string = seqName; +} + void EntityData::EntityCallData::saveLoadWithSerializer(Common::Serializer &s) { for (uint i = 0; i < ARRAYSIZE(callbacks); i++) s.syncAsByte(callbacks[i]); @@ -81,23 +82,19 @@ void EntityData::EntityCallData::saveLoadWithSerializer(Common::Serializer &s) { s.syncAsByte(directionSwitch); // Sync strings -#define SYNC_STRING(varName, count) { \ - char seqName[13]; \ - memset(&seqName, 0, count); \ - if (s.isSaving()) strcpy((char *)&seqName, varName.c_str()); \ - s.syncBytes((byte *)&seqName, count); \ - if (s.isLoading()) varName = seqName; \ -} - - SYNC_STRING(sequenceName, 13); - SYNC_STRING(sequenceName2, 13); - SYNC_STRING(sequenceNamePrefix, 7); - SYNC_STRING(sequenceNameCopy, 13); - -#undef SYNC_STRING + syncString(s, sequenceName, 13); + syncString(s, sequenceName2, 13); + syncString(s, sequenceNamePrefix, 7); + syncString(s, sequenceNameCopy, 13); // Skip pointers to frame & sequences - s.skip(5 * 4); + // (we are using a compressed stream, so we cannot seek on load) + if (s.isLoading()) { + byte empty[5 * 4]; + s.syncBytes(empty, 5 * 4); + } else { + s.skip(5 * 4); + } } ////////////////////////////////////////////////////////////////////////// @@ -246,16 +243,38 @@ void Entity::savegame(const SavePoint &savepoint) { break; case kActionNone: - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: getSaveLoad()->saveGame((SavegameType)params->param1, _entityIndex, (EventIndex)params->param2); - CALLBACK_ACTION(); + callbackAction(); break; } } +void Entity::savegameBloodJacket() { + if (getProgress().jacket == kJacketBlood + && getEntities()->isDistanceBetweenEntities(_entityIndex, kEntityPlayer, 1000) + && !getEntities()->isInsideCompartments(kEntityPlayer) + && !getEntities()->checkFields10(kEntityPlayer)) { + setCallback(1); + + switch (_entityIndex) { + default: + break; + + case kEntityCoudert: + setup_savegame(kSavegameTypeEvent, kEventCoudertBloodJacket); + break; + + case kEntityMertens: + setup_savegame(kSavegameTypeEvent, kEventCoudertBloodJacket); + break; + } + } +} + void Entity::playSound(const SavePoint &savepoint, bool resetItem, SoundFlag flag) { EXPOSE_PARAMS(EntityData::EntityParametersSIIS) @@ -264,7 +283,7 @@ void Entity::playSound(const SavePoint &savepoint, bool resetItem, SoundFlag fla break; case kActionEndSound: - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: @@ -284,7 +303,7 @@ void Entity::draw(const SavePoint &savepoint, bool handleExcuseMe) { break; case kActionExitCompartment: - CALLBACK_ACTION(); + callbackAction(); break; case kActionExcuseMeCath: @@ -308,7 +327,7 @@ void Entity::draw2(const SavePoint &savepoint) { break; case kActionExitCompartment: - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: @@ -326,8 +345,10 @@ void Entity::updateFromTicks(const SavePoint &savepoint) { break; case kActionNone: - UPDATE_PARAM(params->param2, getState()->timeTicks, params->param1) - CALLBACK_ACTION(); + if (Entity::updateParameter(params->param2, getState()->timeTicks, params->param1)) + break; + + callbackAction(); break; } } @@ -340,8 +361,10 @@ void Entity::updateFromTime(const SavePoint &savepoint) { break; case kActionNone: - UPDATE_PARAM(params->param2, getState()->time, params->param1) - CALLBACK_ACTION(); + if (Entity::updateParameter(params->param2, getState()->time, params->param1)) + break; + + callbackAction(); break; } } @@ -352,12 +375,12 @@ void Entity::callbackActionOnDirection(const SavePoint &savepoint) { break; case kActionExitCompartment: - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: if (getData()->direction != kDirectionRight) - CALLBACK_ACTION(); + callbackAction(); break; } } @@ -370,7 +393,7 @@ void Entity::callbackActionRestaurantOrSalon(const SavePoint &savepoint) { case kActionNone: case kActionDefault: if (getEntities()->isSomebodyInsideRestaurantOrSalon()) - CALLBACK_ACTION(); + callbackAction(); break; } } @@ -395,7 +418,7 @@ void Entity::updateEntity(const SavePoint &savepoint, bool handleExcuseMe) { case kActionNone: case kActionDefault: if (getEntities()->updateEntity(_entityIndex, (CarIndex)params->param1, (EntityPosition)params->param2)) - CALLBACK_ACTION(); + callbackAction(); break; } } @@ -410,7 +433,7 @@ void Entity::callSavepoint(const SavePoint &savepoint, bool handleExcuseMe) { case kActionExitCompartment: if (!CURRENT_PARAM(1, 1)) getSavePoints()->call(_entityIndex, (EntityIndex)params->param4, (ActionIndex)params->param5, (char *)¶ms->seq2); - CALLBACK_ACTION(); + callbackAction(); break; case kActionExcuseMeCath: @@ -448,7 +471,7 @@ void Entity::enterExitCompartment(const SavePoint &savepoint, EntityPosition pos if (updateLocation) getData()->location = kLocationInsideCompartment; - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: @@ -468,6 +491,74 @@ void Entity::enterExitCompartment(const SavePoint &savepoint, EntityPosition pos } } +void Entity::goToCompartment(const SavePoint &savepoint, ObjectIndex compartmentFrom, EntityPosition positionFrom, Common::String sequenceFrom, Common::String sequenceTo) { + switch (savepoint.action) { + default: + break; + + case kActionDefault: + getData()->entityPosition = positionFrom; + setCallback(1); + setup_enterExitCompartment(sequenceFrom.c_str(), compartmentFrom); + break; + + case kActionCallback: + switch (getCallback()) { + default: + break; + + case 1: + setCallback(2); + setup_enterExitCompartment(sequenceTo.c_str(), compartmentFrom); + break; + + case 2: + getData()->entityPosition = positionFrom; + getEntities()->clearSequences(_entityIndex); + callbackAction(); + break; + } + break; + } +} + +void Entity::goToCompartmentFromCompartment(const SavePoint &savepoint, ObjectIndex compartmentFrom, EntityPosition positionFrom, Common::String sequenceFrom, ObjectIndex compartmentTo, EntityPosition positionTo, Common::String sequenceTo) { + switch (savepoint.action) { + default: + break; + + case kActionDefault: + getData()->entityPosition = positionFrom; + getData()->location = kLocationOutsideCompartment; + setCallback(1); + setup_enterExitCompartment(sequenceFrom.c_str(), compartmentFrom); + break; + + case kActionCallback: + switch (getCallback()) { + default: + break; + + case 1: + setCallback(2); + setup_updateEntity(kCarGreenSleeping, positionTo); + break; + + case 2: + setCallback(3); + setup_enterExitCompartment(sequenceTo.c_str(), compartmentTo); + break; + + case 3: + getData()->location = kLocationInsideCompartment; + getEntities()->clearSequences(_entityIndex); + callbackAction(); + break; + } + break; + } +} + void Entity::updatePosition(const SavePoint &savepoint, bool handleExcuseMe) { EXPOSE_PARAMS(EntityData::EntityParametersSIII) @@ -477,7 +568,7 @@ void Entity::updatePosition(const SavePoint &savepoint, bool handleExcuseMe) { case kActionExitCompartment: getEntities()->updatePositionExit(_entityIndex, (CarIndex)params->param4, (Position)params->param5); - CALLBACK_ACTION(); + callbackAction(); break; case kActionExcuseMeCath: @@ -494,4 +585,385 @@ void Entity::updatePosition(const SavePoint &savepoint, bool handleExcuseMe) { } } +void Entity::callbackAction() { + if (getData()->currentCall == 0) + error("[Entity::callbackAction] currentCall is already 0, cannot proceed"); + + getData()->currentCall--; + + getSavePoints()->setCallback(_entityIndex, _callbacks[_data->getCurrentCallback()]); + + getSavePoints()->call(_entityIndex, _entityIndex, kActionCallback); +} + +////////////////////////////////////////////////////////////////////////// +// Setup functions +////////////////////////////////////////////////////////////////////////// +void Entity::setup(const char *name, uint index) { + debugC(6, kLastExpressDebugLogic, "Entity: %s()", name); + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->setCallback(_entityIndex, _callbacks[index]); + _data->setCurrentCallback(index); + _data->resetCurrentParameters<EntityData::EntityParametersIIII>(); + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->call(_entityIndex, _entityIndex, kActionDefault); +} + +void Entity::setupI(const char *name, uint index, uint param1) { + debugC(6, kLastExpressDebugLogic, "Entity: %s(%u)", name, param1); + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->setCallback(_entityIndex, _callbacks[index]); + _data->setCurrentCallback(index); + _data->resetCurrentParameters<EntityData::EntityParametersIIII>(); + + EntityData::EntityParametersIIII *params = (EntityData::EntityParametersIIII *)_data->getCurrentParameters(); + params->param1 = (unsigned int)param1; + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->call(_entityIndex, _entityIndex, kActionDefault); +} + +void Entity::setupII(const char *name, uint index, uint param1, uint param2) { + debugC(6, kLastExpressDebugLogic, "Entity: %s(%u, %u)", name, param1, param2); + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->setCallback(_entityIndex, _callbacks[index]); + _data->setCurrentCallback(index); + _data->resetCurrentParameters<EntityData::EntityParametersIIII>(); + + EntityData::EntityParametersIIII *params = (EntityData::EntityParametersIIII *)_data->getCurrentParameters(); + params->param1 = param1; + params->param2 = param2; + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->call(_entityIndex, _entityIndex, kActionDefault); +} + +void Entity::setupIII(const char *name, uint index, uint param1, uint param2, uint param3) { + debugC(6, kLastExpressDebugLogic, "Entity: %s(%u, %u, %u)", name, param1, param2, param3); + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->setCallback(_entityIndex, _callbacks[index]); + _data->setCurrentCallback(index); + _data->resetCurrentParameters<EntityData::EntityParametersIIII>(); + + EntityData::EntityParametersIIII *params = (EntityData::EntityParametersIIII *)_data->getCurrentParameters(); + params->param1 = param1; + params->param2 = param2; + params->param3 = param3; + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->call(_entityIndex, _entityIndex, kActionDefault); +} + +void Entity::setupS(const char *name, uint index, const char *seq1) { + debugC(6, kLastExpressDebugLogic, "Entity: %s(%s)", name, seq1); + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->setCallback(_entityIndex, _callbacks[index]); + _data->setCurrentCallback(index); + _data->resetCurrentParameters<EntityData::EntityParametersSIIS>(); + + EntityData::EntityParametersSIIS *params = (EntityData::EntityParametersSIIS*)_data->getCurrentParameters(); + strncpy(params->seq1, seq1, 12); + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->call(_entityIndex, _entityIndex, kActionDefault); +} + +void Entity::setupSS(const char *name, uint index, const char *seq1, const char *seq2) { + debugC(6, kLastExpressDebugLogic, "Entity: %s(%s, %s)", name, seq1, seq2); + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->setCallback(_entityIndex, _callbacks[index]); + _data->setCurrentCallback(index); + _data->resetCurrentParameters<EntityData::EntityParametersSSII>(); + + EntityData::EntityParametersSSII *params = (EntityData::EntityParametersSSII*)_data->getCurrentParameters(); + strncpy(params->seq1, seq1, 12); + strncpy(params->seq2, seq2, 12); + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->call(_entityIndex, _entityIndex, kActionDefault); +} + +void Entity::setupSI(const char *name, uint index, const char *seq1, uint param4) { + debugC(6, kLastExpressDebugLogic, "Entity: %s(%s, %u)", name, seq1, param4); + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->setCallback(_entityIndex, _callbacks[index]); + _data->setCurrentCallback(index); + _data->resetCurrentParameters<EntityData::EntityParametersSIIS>(); + + EntityData::EntityParametersSIIS *params = (EntityData::EntityParametersSIIS *)_data->getCurrentParameters(); + strncpy(params->seq1, seq1, 12); + params->param4 = param4; + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->call(_entityIndex, _entityIndex, kActionDefault); +} + +void Entity::setupSII(const char *name, uint index, const char *seq1, uint param4, uint param5) { + debugC(6, kLastExpressDebugLogic, "Entity: %s(%s, %u, %u)", name, seq1, param4, param5); + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->setCallback(_entityIndex, _callbacks[index]); + _data->setCurrentCallback(index); + _data->resetCurrentParameters<EntityData::EntityParametersSIIS>(); + + EntityData::EntityParametersSIIS *params = (EntityData::EntityParametersSIIS *)_data->getCurrentParameters(); + strncpy(params->seq1, seq1, 12); + params->param4 = param4; + params->param5 = param5; + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->call(_entityIndex, _entityIndex, kActionDefault); +} + +void Entity::setupSIII(const char *name, uint index, const char *seq, uint param4, uint param5, uint param6) { + debugC(6, kLastExpressDebugLogic, "Entity: %s(%s, %u, %u, %u)", name, seq, param4, param5, param6); + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->setCallback(_entityIndex, _callbacks[index]); + _data->setCurrentCallback(index); + _data->resetCurrentParameters<EntityData::EntityParametersSIII>(); + + EntityData::EntityParametersSIII *params = (EntityData::EntityParametersSIII *)_data->getCurrentParameters(); + strncpy(params->seq, seq, 12); + params->param4 = param4; + params->param5 = param5; + params->param6 = param6; + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->call(_entityIndex, _entityIndex, kActionDefault); +} + +void Entity::setupSIIS(const char *name, uint index, const char *seq1, uint param4, uint param5, const char *seq2) { + debugC(6, kLastExpressDebugLogic, "Entity: %s(%s, %u, %u, %s)", name, seq1, param4, param5, seq2); + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->setCallback(_entityIndex, _callbacks[index]); + _data->setCurrentCallback(index); + _data->resetCurrentParameters<EntityData::EntityParametersSIIS>(); + + EntityData::EntityParametersSIIS *params = (EntityData::EntityParametersSIIS *)_data->getCurrentParameters(); + strncpy(params->seq1, seq1, 12); + params->param4 = param4; + params->param5 = param5; + strncpy(params->seq2, seq2, 12); + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->call(_entityIndex, _entityIndex, kActionDefault); +} + +void Entity::setupSSI(const char *name, uint index, const char *seq1, const char *seq2, uint param7) { + debugC(6, kLastExpressDebugLogic, "Entity: %s(%s, %s, %u)", name, seq1, seq2, param7); + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->setCallback(_entityIndex, _callbacks[index]); + _data->setCurrentCallback(index); + _data->resetCurrentParameters<EntityData::EntityParametersSSII>(); + + EntityData::EntityParametersSSII *params = (EntityData::EntityParametersSSII *)_data->getCurrentParameters(); + strncpy(params->seq1, seq1, 12); + strncpy(params->seq2, seq2, 12); + params->param7 = param7; + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->call(_entityIndex, _entityIndex, kActionDefault); +} + +void Entity::setupIS(const char *name, uint index, uint param1, const char *seq) { + debugC(6, kLastExpressDebugLogic, "Entity: %s(%u, %s)", name, param1, seq); + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->setCallback(_entityIndex, _callbacks[index]); + _data->setCurrentCallback(index); + _data->resetCurrentParameters<EntityData::EntityParametersISII>(); + + EntityData::EntityParametersISII *params = (EntityData::EntityParametersISII *)_data->getCurrentParameters(); + params->param1 = (unsigned int)param1; + strncpy(params->seq, seq, 12); + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->call(_entityIndex, _entityIndex, kActionDefault); +} + +void Entity::setupISS(const char *name, uint index, uint param1, const char *seq1, const char *seq2) { + debugC(6, kLastExpressDebugLogic, "Entity: %s(%u, %s, %s)", name, param1, seq1, seq2); + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->setCallback(_entityIndex, _callbacks[index]); + _data->setCurrentCallback(index); + _data->resetCurrentParameters<EntityData::EntityParametersISSI>(); + + EntityData::EntityParametersISSI *params = (EntityData::EntityParametersISSI *)_data->getCurrentParameters(); + params->param1 = param1; + strncpy(params->seq1, seq1, 12); + strncpy(params->seq2, seq2, 12); + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->call(_entityIndex, _entityIndex, kActionDefault); +} + +void Entity::setupIIS(const char *name, uint index, uint param1, uint param2, const char *seq) { + debugC(6, kLastExpressDebugLogic, "Entity: %s(%u, %u, %s)", name, param1, param2, seq); + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->setCallback(_entityIndex, _callbacks[index]); + _data->setCurrentCallback(index); + _data->resetCurrentParameters<EntityData::EntityParametersIISI>(); + + EntityData::EntityParametersIISI *params = (EntityData::EntityParametersIISI *)_data->getCurrentParameters(); + params->param1 = param1; + params->param2 = param2; + strncpy(params->seq, seq, 12); + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->call(_entityIndex, _entityIndex, kActionDefault); +} + +void Entity::setupIISS(const char *name, uint index, uint param1, uint param2, const char *seq1, const char *seq2) { + debugC(6, kLastExpressDebugLogic, "Entity: %s(%u, %u, %s, %s)", name, param1, param2, seq1, seq2); + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->setCallback(_entityIndex, _callbacks[index]); + _data->setCurrentCallback(index); + _data->resetCurrentParameters<EntityData::EntityParametersIISS>(); + + EntityData::EntityParametersIISS *params = (EntityData::EntityParametersIISS *)_data->getCurrentParameters(); + params->param1 = param1; + params->param2 = param2; + strncpy(params->seq1, seq1, 12); + strncpy(params->seq2, seq2, 12); + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->call(_entityIndex, _entityIndex, kActionDefault); +} + +////////////////////////////////////////////////////////////////////////// +// Helper functions +////////////////////////////////////////////////////////////////////////// + +bool Entity::updateParameter(uint ¶meter, uint timeType, uint delta) { + if (!parameter) + parameter = (uint)(timeType + delta); + + if (parameter >= timeType) + return false; + + parameter = kTimeInvalid; + + return true; +} + +bool Entity::updateParameterTime(TimeValue timeValue, bool check, uint ¶meter, uint delta) { + if (getState()->time <= timeValue) { + if (check || !parameter) + parameter = (uint)(getState()->time + delta); + } + + if (parameter >= getState()->time && getState()->time <= timeValue) + return false; + + parameter = kTimeInvalid; + + return true; +} + +bool Entity::updateParameterCheck(uint ¶meter, uint timeType, uint delta) { + if (parameter && parameter >= timeType) + return false; + + if (!parameter) + parameter = (uint)(timeType + delta); + + return true; +} + +bool Entity::timeCheck(TimeValue timeValue, uint ¶meter, Common::Functor0<void> *function) { + if (getState()->time > timeValue && !parameter) { + parameter = 1; + (*function)(); + + return true; + } + + return false; +} + +bool Entity::timeCheckCallback(TimeValue timeValue, uint ¶meter, byte callback, Common::Functor0<void> *function) { + if (getState()->time > timeValue && !parameter) { + parameter = 1; + setCallback(callback); + (*function)(); + + return true; + } + + return false; +} + +bool Entity::timeCheckCallback(TimeValue timeValue, uint ¶meter, byte callback, const char *str, Common::Functor1<const char *, void> *function) { + if (getState()->time > timeValue && !parameter) { + parameter = 1; + setCallback(callback); + (*function)(str); + + return true; + } + + return false; +} + +bool Entity::timeCheckCallback(TimeValue timeValue, uint ¶meter, byte callback, bool check, Common::Functor1<bool, void> *function) { + if (getState()->time > timeValue && !parameter) { + parameter = 1; + setCallback(callback); + (*function)(check); + + return true; + } + + return false; +} + +bool Entity::timeCheckCallbackInventory(TimeValue timeValue, uint ¶meter, byte callback, Common::Functor0<void> *function) { + if (getState()->time > timeValue && !parameter) { + parameter = 1; + getData()->inventoryItem = kItemNone; + setCallback(callback); + (*function)(); + + return true; + } + + return false; +} + +bool Entity::timeCheckCar(TimeValue timeValue, uint ¶meter, byte callback, Common::Functor0<void> *function) { + if ((getState()->time <= timeValue && !getEntities()->isPlayerInCar(kCarGreenSleeping)) || !parameter) + parameter = (uint)getState()->time + 75; + + if (getState()->time > timeValue || parameter < getState()->time) { + parameter = kTimeInvalid; + setCallback(callback); + (*function)(); + + return true; + } + + return false; +} + +void Entity::timeCheckSavepoint(TimeValue timeValue, uint ¶meter, EntityIndex entity1, EntityIndex entity2, ActionIndex action) { + if (getState()->time > timeValue && !parameter) { + parameter = 1; + getSavePoints()->push(entity1, entity2, action); + } +} + +void Entity::timeCheckObject(TimeValue timeValue, uint ¶meter, ObjectIndex object, ObjectLocation location) { + if (getState()->time > timeValue && !parameter) { + parameter = 1; + getObjects()->updateLocation2(object, location); + } +} + +bool Entity::timeCheckCallbackAction(TimeValue timeValue, uint ¶meter) { + if (getState()->time > timeValue && !parameter) { + parameter = 1; + callbackAction(); + return true; + } + + return false; +} + +bool Entity::timeCheckPlaySoundUpdatePosition(TimeValue timeValue, uint ¶meter, byte callback, const char* sound, EntityPosition position) { + if (getState()->time > timeValue && !parameter) { + parameter = 1; + getData()->entityPosition = position; + setCallback(callback); + setup_playSound(sound); + return true; + } + + return false; +} + + } // End of namespace LastExpress diff --git a/engines/lastexpress/entities/entity.h b/engines/lastexpress/entities/entity.h index 039f461c7b..3601f34f6f 100644 --- a/engines/lastexpress/entities/entity.h +++ b/engines/lastexpress/entities/entity.h @@ -25,8 +25,13 @@ #include "lastexpress/shared.h" +#include "lastexpress/game/logic.h" +#include "lastexpress/game/savepoint.h" +#include "lastexpress/game/state.h" + #include "lastexpress/sound/sound.h" +#include "lastexpress/lastexpress.h" #include "lastexpress/helpers.h" #include "common/array.h" @@ -41,10 +46,229 @@ class Sequence; class SequenceFrame; struct SavePoint; +////////////////////////////////////////////////////////////////////////// +// Declaration +////////////////////////////////////////////////////////////////////////// +#define DECLARE_FUNCTION(name) \ + void setup_##name(); \ + void name(const SavePoint &savepoint); + +#define DECLARE_FUNCTION_1(name, param1) \ + void setup_##name(param1); \ + void name(const SavePoint &savepoint); + +#define DECLARE_FUNCTION_2(name, param1, param2) \ + void setup_##name(param1, param2); \ + void name(const SavePoint &savepoint); + +#define DECLARE_FUNCTION_3(name, param1, param2, param3) \ + void setup_##name(param1, param2, param3); \ + void name(const SavePoint &savepoint); + +#define DECLARE_FUNCTION_4(name, param1, param2, param3, param4) \ + void setup_##name(param1, param2, param3, param4); \ + void name(const SavePoint &savepoint); + +#define DECLARE_FUNCTION_NOSETUP(name) \ + void name(const SavePoint &savepoint); + +#define DECLARE_NULL_FUNCTION() \ + void setup_nullfunction(); + +////////////////////////////////////////////////////////////////////////// +// Callbacks +////////////////////////////////////////////////////////////////////////// +#define ENTITY_CALLBACK(class, name, pointer) \ + Common::Functor1Mem<const SavePoint&, void, class>(pointer, &class::name) + +#define ADD_CALLBACK_FUNCTION(class, name) \ + _callbacks.push_back(new ENTITY_CALLBACK(class, name, this)); + +#define ADD_NULL_FUNCTION() \ + _callbacks.push_back(new ENTITY_CALLBACK(Entity, nullfunction, this)); + +#define WRAP_SETUP_FUNCTION(className, method) \ + new Common::Functor0Mem<void, className>(this, &className::method) + +#define WRAP_SETUP_FUNCTION_S(className, method) \ + new Common::Functor1Mem<const char *, void, className>(this, &className::method) + +#define WRAP_SETUP_FUNCTION_B(className, method) \ + new Common::Functor1Mem<bool, void, className>(this, &className::method) + +////////////////////////////////////////////////////////////////////////// +// Parameters macros +////////////////////////////////////////////////////////////////////////// +#define CURRENT_PARAM(index, id) \ + ((EntityData::EntityParametersIIII*)_data->getCurrentParameters(index))->param##id + +#define ENTITY_PARAM(index, id) \ + ((EntityData::EntityParametersIIII*)_data->getParameters(8, index))->param##id + +////////////////////////////////////////////////////////////////////////// +// Misc +////////////////////////////////////////////////////////////////////////// +#define RESET_ENTITY_STATE(entity, class, function) \ + getEntities()->resetState(entity); \ + ((class *)getEntities()->get(entity))->function(); + +////////////////////////////////////////////////////////////////////////// +// Implementation +////////////////////////////////////////////////////////////////////////// + +// Expose parameters and check validity +#define EXPOSE_PARAMS(type) \ + type *params = (type *)_data->getCurrentParameters(); \ + if (!params) \ + error("[EXPOSE_PARAMS] Trying to call an entity function with invalid parameters"); \ + +// function signature without setup (we keep the index for consistency but never use it) +#define IMPLEMENT_FUNCTION_NOSETUP(index, class, name) \ + void class::name(const SavePoint &savepoint) { \ + debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(index=" #index ")"); + +// simple setup with no parameters +#define IMPLEMENT_FUNCTION(index, class, name) \ + void class::setup_##name() { \ + Entity::setup(#class "::setup_" #name, index); \ + } \ + void class::name(const SavePoint &savepoint) { \ + EXPOSE_PARAMS(EntityData::EntityParametersIIII) \ + debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "() - action: %s", ACTION_NAME(savepoint.action)); + +#define IMPLEMENT_FUNCTION_END } + +// nullfunction call +#define IMPLEMENT_NULL_FUNCTION(index, class) \ + void class::setup_nullfunction() { \ + Entity::setup(#class "::setup_nullfunction", index); \ + } + +// setup with one uint parameter +#define IMPLEMENT_FUNCTION_I(index, class, name, paramType) \ + void class::setup_##name(paramType param1) { \ + Entity::setupI(#class "::setup_" #name, index, param1); \ + } \ + void class::name(const SavePoint &savepoint) { \ + EXPOSE_PARAMS(EntityData::EntityParametersIIII) \ + debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%d) - action: %s", params->param1, ACTION_NAME(savepoint.action)); + +// setup with two uint parameters +#define IMPLEMENT_FUNCTION_II(index, class, name, paramType1, paramType2) \ + void class::setup_##name(paramType1 param1, paramType2 param2) { \ + Entity::setupII(#class "::setup_" #name, index, param1, param2); \ + } \ + void class::name(const SavePoint &savepoint) { \ + EXPOSE_PARAMS(EntityData::EntityParametersIIII) \ + debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%d, %d) - action: %s", params->param1, params->param2, ACTION_NAME(savepoint.action)); + +// setup with three uint parameters +#define IMPLEMENT_FUNCTION_III(index, class, name, paramType1, paramType2, paramType3) \ + void class::setup_##name(paramType1 param1, paramType2 param2, paramType3 param3) { \ + Entity::setupIII(#class "::setup_" #name, index, param1, param2, param3); \ + } \ + void class::name(const SavePoint &savepoint) { \ + EXPOSE_PARAMS(EntityData::EntityParametersIIII) \ + debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%d, %d, %d) - action: %s", params->param1, params->param2, params->param3, ACTION_NAME(savepoint.action)); + +// setup with one char *parameter +#define IMPLEMENT_FUNCTION_S(index, class, name) \ + void class::setup_##name(const char *seq1) { \ + Entity::setupS(#class "::setup_" #name, index, seq1); \ + } \ + void class::name(const SavePoint &savepoint) { \ + EXPOSE_PARAMS(EntityData::EntityParametersSIIS) \ + debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%s) - action: %s", (char *)¶ms->seq1, ACTION_NAME(savepoint.action)); + +// setup with one char *parameter and one uint +#define IMPLEMENT_FUNCTION_SI(index, class, name, paramType2) \ + void class::setup_##name(const char *seq1, paramType2 param4) { \ + Entity::setupSI(#class "::setup_" #name, index, seq1, param4); \ + } \ + void class::name(const SavePoint &savepoint) { \ + EXPOSE_PARAMS(EntityData::EntityParametersSIIS) \ + debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%s, %d) - action: %s", (char *)¶ms->seq1, params->param4, ACTION_NAME(savepoint.action)); + +// setup with one char *parameter and two uints +#define IMPLEMENT_FUNCTION_SII(index, class, name, paramType2, paramType3) \ + void class::setup_##name(const char *seq1, paramType2 param4, paramType3 param5) { \ + Entity::setupSII(#class "::setup_" #name, index, seq1, param4, param5); \ + } \ + void class::name(const SavePoint &savepoint) { \ + EXPOSE_PARAMS(EntityData::EntityParametersSIIS) \ + debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%s, %d, %d) - action: %s", (char *)¶ms->seq1, params->param4, params->param5, ACTION_NAME(savepoint.action)); + +// setup with one char *parameter and three uints +#define IMPLEMENT_FUNCTION_SIII(index, class, name, paramType2, paramType3, paramType4) \ + void class::setup_##name(const char *seq, paramType2 param4, paramType3 param5, paramType4 param6) { \ + Entity::setupSIII(#class "::setup_" #name, index, seq, param4, param5, param6); \ + } \ + void class::name(const SavePoint &savepoint) { \ + EXPOSE_PARAMS(EntityData::EntityParametersSIII) \ + debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%s, %d, %d, %d) - action: %s", (char *)¶ms->seq, params->param4, params->param5, params->param6, ACTION_NAME(savepoint.action)); + +#define IMPLEMENT_FUNCTION_SIIS(index, class, name, paramType2, paramType3) \ + void class::setup_##name(const char *seq1, paramType2 param4, paramType3 param5, const char *seq2) { \ + Entity::setupSIIS(#class "::setup_" #name, index, seq1, param4, param5, seq2); \ + } \ + void class::name(const SavePoint &savepoint) { \ + EXPOSE_PARAMS(EntityData::EntityParametersSIIS) \ + debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%s, %d, %d, %s) - action: %s", (char *)¶ms->seq1, params->param4, params->param5, (char *)¶ms->seq2, ACTION_NAME(savepoint.action)); + +#define IMPLEMENT_FUNCTION_SS(index, class, name) \ + void class::setup_##name(const char *seq1, const char *seq2) { \ + Entity::setupSS(#class "::setup_" #name, index, seq1, seq2); \ + } \ + void class::name(const SavePoint &savepoint) { \ + EXPOSE_PARAMS(EntityData::EntityParametersSSII) \ + debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%s, %s) - action: %s", (char *)¶ms->seq1, (char *)¶ms->seq2, ACTION_NAME(savepoint.action)); + +#define IMPLEMENT_FUNCTION_SSI(index, class, name, paramType3) \ + void class::setup_##name(const char *seq1, const char *seq2, paramType3 param7) { \ + Entity::setupSSI(#class "::setup_" #name, index, seq1, seq2, param7); \ + } \ + void class::name(const SavePoint &savepoint) { \ + EXPOSE_PARAMS(EntityData::EntityParametersSSII) \ + debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%s, %s, %d) - action: %s", (char *)¶ms->seq1, (char *)¶ms->seq2, params->param7, ACTION_NAME(savepoint.action)); + +#define IMPLEMENT_FUNCTION_IS(index, class, name, paramType) \ + void class::setup_##name(paramType param1, const char *seq) { \ + Entity::setupIS(#class "::setup_" #name, index, param1, seq); \ + } \ + void class::name(const SavePoint &savepoint) { \ + EXPOSE_PARAMS(EntityData::EntityParametersISII) \ + debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%d, %s) - action: %s", params->param1, (char *)¶ms->seq, ACTION_NAME(savepoint.action)); + +#define IMPLEMENT_FUNCTION_ISS(index, class, name, paramType) \ + void class::setup_##name(paramType param1, const char *seq1, const char *seq2) { \ + Entity::setupISS(#class "::setup_" #name, index, param1, seq1, seq2); \ + } \ + void class::name(const SavePoint &savepoint) { \ + EXPOSE_PARAMS(EntityData::EntityParametersISSI) \ + debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%d, %s, %s) - action: %s", params->param1, (char *)¶ms->seq1, (char *)¶ms->seq2, ACTION_NAME(savepoint.action)); + +#define IMPLEMENT_FUNCTION_IIS(index, class, name, paramType1, paramType2) \ + void class::setup_##name(paramType1 param1, paramType2 param2, const char *seq) { \ + Entity::setupIIS(#class "::setup_" #name, index, param1, param2, seq); \ + } \ + void class::name(const SavePoint &savepoint) { \ + EXPOSE_PARAMS(EntityData::EntityParametersIISI) \ + debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%d, %d, %s) - action: %s", params->param1, params->param2, (char *)¶ms->seq, ACTION_NAME(savepoint.action)); + +#define IMPLEMENT_FUNCTION_IISS(index, class, name, paramType1, paramType2) \ + void class::setup_##name(paramType1 param1, paramType2 param2, const char *seq1, const char *seq2) { \ + Entity::setupIISS(#class "::setup_" #name, index, param1, param2, seq1, seq2); \ + } \ + void class::name(const SavePoint &savepoint) { \ + EXPOSE_PARAMS(EntityData::EntityParametersIISS) \ + debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%d, %d, %s, %s) - action: %s", params->param1, params->param2, (char *)¶ms->seq1, (char *)¶ms->seq2, ACTION_NAME(savepoint.action)); + + +////////////////////////////////////////////////////////////////////////// class EntityData : Common::Serializable { public: - struct EntityParameters : Common::Serializable{ + struct EntityParameters : Common::Serializable { virtual ~EntityParameters() {} virtual Common::String toString() = 0; @@ -596,6 +820,15 @@ public: return str; } + /** + * Synchronizes a string. + * + * @param s The Common::Serializer to use. + * @param string The string. + * @param length Length of the string. + */ + void syncString(Common::Serializer &s, Common::String &string, int length); + // Serializable void saveLoadWithSerializer(Common::Serializer &s); }; @@ -611,32 +844,30 @@ public: params->parameters[i] = new T(); } - EntityCallData *getCallData() { return &_data; } + EntityCallData *getCallData() { return &_data; } - EntityParameters *getParameters(uint callback, byte index) const; - EntityParameters *getCurrentParameters(byte index = 0) { return getParameters(_data.currentCall, index); } + EntityParameters *getParameters(uint callback, byte index) const; + EntityParameters *getCurrentParameters(byte index = 0) { return getParameters(_data.currentCall, index); } + EntityCallParameters *getCurrentCallParameters() { return &_parameters[_data.currentCall]; } - int getCallback(uint callback) const; - int getCurrentCallback() { return getCallback(_data.currentCall); } - void setCallback(uint callback, byte index); - void setCurrentCallback(uint index) { setCallback(_data.currentCall, index); } + int getCallback(uint callback) const; + int getCurrentCallback() { return getCallback(_data.currentCall); } + void setCallback(uint callback, byte index); + void setCurrentCallback(uint index) { setCallback(_data.currentCall, index); } - void updateParameters(uint32 index) const; + void updateParameters(uint32 index) const; // Serializable - void saveLoadWithSerializer(Common::Serializer &ser); + void saveLoadWithSerializer(Common::Serializer &ser); private: - EntityCallData _data; + EntityCallData _data; EntityCallParameters _parameters[9]; }; class Entity : Common::Serializable { public: - - typedef Common::Functor1<const SavePoint&, void> Callback; - Entity(LastExpressEngine *engine, EntityIndex index); virtual ~Entity(); @@ -657,6 +888,12 @@ public: virtual void setup_chapter4() = 0; virtual void setup_chapter5() = 0; + // Shared functions + virtual void setup_savegame(SavegameType, uint32) { error("[Entity::setup_savegame] Trying to call the parent setup function. Use the specific entity function directly"); } + virtual void setup_enterExitCompartment(const char *, ObjectIndex) { error("[Entity::setup_enterExitCompartment] Trying to call the parent setup function. Use the specific entity function directly"); } + virtual void setup_updateEntity(CarIndex, EntityPosition) { error("[Entity::setup_updateEntity] Trying to call the parent setup function. Use the specific entity function directly"); } + virtual void setup_playSound(const char*) { error("[Entity::setup_playSound] Trying to call the parent setup function. Use the specific entity function directly"); } + // Serializable void saveLoadWithSerializer(Common::Serializer &ser) { _data->saveLoadWithSerializer(ser); } @@ -664,10 +901,11 @@ public: protected: LastExpressEngine *_engine; + typedef Common::Functor1<const SavePoint&, void> Callback; - EntityIndex _entityIndex; - EntityData *_data; - Common::Array<Callback *> _callbacks; + EntityIndex _entityIndex; + EntityData *_data; + Common::Array<Callback *> _callbacks; /** * Saves the game @@ -679,6 +917,13 @@ protected: void savegame(const SavePoint &savepoint); /** + * Saves the game before being found out with a blood covered jacket. + * + * @param saveFunction The setup function to call to save the game + */ + void savegameBloodJacket(); + + /** * Play sound * * @param savepoint The savepoint @@ -782,15 +1027,83 @@ protected: void enterExitCompartment(const SavePoint &savepoint, EntityPosition position1 = kPositionNone, EntityPosition position2 = kPositionNone, CarIndex car = kCarNone, ObjectIndex compartment = kObjectNone, bool alternate = false, bool updateLocation = false); /** + * Go to compartment. + * + * @param savepoint The savepoint. + * @param compartmentFrom The compartment from. + * @param positionFrom The position from. + * @param sequenceFrom The sequence from. + * @param sequenceTo The sequence to. + */ + void goToCompartment(const SavePoint &savepoint, ObjectIndex compartmentFrom, EntityPosition positionFrom, Common::String sequenceFrom, Common::String sequenceTo); + + /** + * Go to compartment from compartment. + * + * @param savepoint The savepoint. + * @param compartmentFrom The compartment from. + * @param positionFrom The position from. + * @param sequenceFrom The sequence from. + * @param compartmentTo The compartment to. + * @param positionTo The position to. + * @param sequenceTo The sequence to. + */ + void goToCompartmentFromCompartment(const SavePoint &savepoint, ObjectIndex compartmentFrom, EntityPosition positionFrom, Common::String sequenceFrom, ObjectIndex compartmentTo, EntityPosition positionTo, Common::String sequenceTo); + + /** * Updates the position * - * @param savepoint The savepoint + * @param savepoint The savepoint * - Sequence name * - CarIndex * - Position * @param handleExcuseMe true to handle excuseMe actions */ void updatePosition(const SavePoint &savepoint, bool handleExcuseMe = false); + + /** + * Store the current callback information and perform the callback action + */ + void callbackAction(); + + ////////////////////////////////////////////////////////////////////////// + // Setup functions + ////////////////////////////////////////////////////////////////////////// + void setup(const char *name, uint index); + void setupI(const char *name, uint index, uint param1); + void setupII(const char *name, uint index, uint param1, uint param2); + void setupIII(const char *name, uint index, uint param1, uint param2, uint param3); + void setupS(const char *name, uint index, const char *seq1); + void setupSS(const char *name, uint index, const char *seq1, const char *seq2); + void setupSI(const char *name, uint index, const char *seq1, uint param4); + void setupSII(const char *name, uint index, const char *seq1, uint param4, uint param5); + void setupSIII(const char *name, uint index, const char *seq, uint param4, uint param5, uint param6); + void setupSIIS(const char *name, uint index, const char *seq1, uint param4, uint param5, const char *seq2); + void setupSSI(const char *name, uint index, const char *seq1, const char *seq2, uint param7); + void setupIS(const char *name, uint index, uint param1, const char *seq); + void setupISS(const char *name, uint index, uint param1, const char *seq1, const char *seq2); + void setupIIS(const char *name, uint index, uint param1, uint param2, const char *seq); + void setupIISS(const char *name, uint index, uint param1, uint param2, const char *seq1, const char *seq2); + + ////////////////////////////////////////////////////////////////////////// + // Helper functions + ////////////////////////////////////////////////////////////////////////// + + bool updateParameter(uint ¶meter, uint timeType, uint delta); + bool updateParameterCheck(uint ¶meter, uint timeType, uint delta); + bool updateParameterTime(TimeValue timeValue, bool check, uint ¶meter, uint delta); + + bool timeCheck(TimeValue timeValue, uint ¶meter, Common::Functor0<void> *function); + bool timeCheckCallback(TimeValue timeValue, uint ¶meter, byte callback, Common::Functor0<void> *function); + bool timeCheckCallback(TimeValue timeValue, uint ¶meter, byte callback, const char *str, Common::Functor1<const char *, void> *function); + bool timeCheckCallback(TimeValue timeValue, uint ¶meter, byte callback, bool check, Common::Functor1<bool, void> *function); + bool timeCheckCallbackInventory(TimeValue timeValue, uint ¶meter, byte callback, Common::Functor0<void> *function); + bool timeCheckCar(TimeValue timeValue, uint ¶meter, byte callback, Common::Functor0<void> *function); + void timeCheckSavepoint(TimeValue timeValue, uint ¶meter, EntityIndex entity1, EntityIndex entity2, ActionIndex action); + void timeCheckObject(TimeValue timeValue, uint ¶meter, ObjectIndex index, ObjectLocation location); + bool timeCheckCallbackAction(TimeValue timeValue, uint ¶meter); + bool timeCheckPlaySoundUpdatePosition(TimeValue timeValue, uint ¶meter, byte callback, const char* sound, EntityPosition position); + }; diff --git a/engines/lastexpress/entities/entity39.cpp b/engines/lastexpress/entities/entity39.cpp index e786d153a0..1786cd2201 100644 --- a/engines/lastexpress/entities/entity39.cpp +++ b/engines/lastexpress/entities/entity39.cpp @@ -28,7 +28,6 @@ #include "lastexpress/game/savepoint.h" #include "lastexpress/game/state.h" -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/entity39.h b/engines/lastexpress/entities/entity39.h index 4335a9566e..54b65408c7 100644 --- a/engines/lastexpress/entities/entity39.h +++ b/engines/lastexpress/entities/entity39.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_ENTITY39_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { @@ -72,4 +71,4 @@ private: } // End of namespace LastExpress -#endif // LASTEXPRESS_##define##_H +#endif // LASTEXPRESS_ENTITY39_H diff --git a/engines/lastexpress/entities/entity_intern.h b/engines/lastexpress/entities/entity_intern.h index 2da0da15b3..fd803676a9 100644 --- a/engines/lastexpress/entities/entity_intern.h +++ b/engines/lastexpress/entities/entity_intern.h @@ -25,502 +25,6 @@ namespace LastExpress { -#define LOW_BYTE(w) ((unsigned char)(((unsigned long)(w)) & 0xff)) - -////////////////////////////////////////////////////////////////////////// -// Callbacks -#define ENTITY_CALLBACK(class, name, pointer) \ - Common::Functor1Mem<const SavePoint&, void, class>(pointer, &class::name) - -#define ADD_CALLBACK_FUNCTION(class, name) \ - _callbacks.push_back(new ENTITY_CALLBACK(class, name, this)); - -#define ADD_NULL_FUNCTION() \ - _callbacks.push_back(new ENTITY_CALLBACK(Entity, nullfunction, this)); - -////////////////////////////////////////////////////////////////////////// -// Declaration -////////////////////////////////////////////////////////////////////////// - -#define DECLARE_FUNCTION(name) \ - void setup_##name(); \ - void name(const SavePoint &savepoint); - -#define DECLARE_FUNCTION_1(name, param1) \ - void setup_##name(param1); \ - void name(const SavePoint &savepoint); - -#define DECLARE_FUNCTION_2(name, param1, param2) \ - void setup_##name(param1, param2); \ - void name(const SavePoint &savepoint); - -#define DECLARE_FUNCTION_3(name, param1, param2, param3) \ - void setup_##name(param1, param2, param3); \ - void name(const SavePoint &savepoint); - -#define DECLARE_FUNCTION_4(name, param1, param2, param3, param4) \ - void setup_##name(param1, param2, param3, param4); \ - void name(const SavePoint &savepoint); - -#define DECLARE_FUNCTION_NOSETUP(name) \ - void name(const SavePoint &savepoint); - -#define DECLARE_NULL_FUNCTION() \ - void setup_nullfunction(); - -////////////////////////////////////////////////////////////////////////// -// Setup -////////////////////////////////////////////////////////////////////////// - -#define IMPLEMENT_SETUP(class, callback_class, name, index) \ -void class::setup_##name() { \ - BEGIN_SETUP(callback_class, name, index, EntityData::EntityParametersIIII) \ - debugC(6, kLastExpressDebugLogic, "Entity: " #class "::setup_" #name "()"); \ - END_SETUP() \ -} - -#define BEGIN_SETUP(class, name, index, type) \ - _engine->getGameLogic()->getGameState()->getGameSavePoints()->setCallback(_entityIndex, _callbacks[index]); \ - _data->setCurrentCallback(index); \ - _data->resetCurrentParameters<type>(); - -#define END_SETUP() \ - _engine->getGameLogic()->getGameState()->getGameSavePoints()->call(_entityIndex, _entityIndex, kActionDefault); - - -////////////////////////////////////////////////////////////////////////// -// Implementation -////////////////////////////////////////////////////////////////////////// - -// Expose parameters and check validity -#define EXPOSE_PARAMS(type) \ - type *params = (type *)_data->getCurrentParameters(); \ - if (!params) \ - error("[EXPOSE_PARAMS] Trying to call an entity function with invalid parameters"); \ - - -// function signature without setup (we keep the index for consistency but never use it) -#define IMPLEMENT_FUNCTION_NOSETUP(index, class, name) \ - void class::name(const SavePoint &savepoint) { \ - debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(index=" #index ")"); - -// simple setup with no parameters -#define IMPLEMENT_FUNCTION(index, class, name) \ - IMPLEMENT_SETUP(class, class, name, index) \ - void class::name(const SavePoint &savepoint) { \ - EXPOSE_PARAMS(EntityData::EntityParametersIIII) \ - debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "() - action: %s", ACTION_NAME(savepoint.action)); - -#define IMPLEMENT_FUNCTION_END } - -// nullfunction call -#define IMPLEMENT_NULL_FUNCTION(index, class) \ - IMPLEMENT_SETUP(class, Entity, nullfunction, index) - -// setup with one uint parameter -#define IMPLEMENT_FUNCTION_I(index, class, name, paramType) \ - void class::setup_##name(paramType param1) { \ - BEGIN_SETUP(class, name, index, EntityData::EntityParametersIIII) \ - EntityData::EntityParametersIIII *params = (EntityData::EntityParametersIIII*)_data->getCurrentParameters(); \ - params->param1 = (unsigned int)param1; \ - END_SETUP() \ - } \ - void class::name(const SavePoint &savepoint) { \ - EXPOSE_PARAMS(EntityData::EntityParametersIIII) \ - debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%d) - action: %s", params->param1, ACTION_NAME(savepoint.action)); - -// setup with two uint parameters -#define IMPLEMENT_FUNCTION_II(index, class, name, paramType1, paramType2) \ - void class::setup_##name(paramType1 param1, paramType2 param2) { \ - BEGIN_SETUP(class, name, index, EntityData::EntityParametersIIII) \ - EntityData::EntityParametersIIII *params = (EntityData::EntityParametersIIII*)_data->getCurrentParameters(); \ - params->param1 = param1; \ - params->param2 = param2; \ - END_SETUP() \ - } \ - void class::name(const SavePoint &savepoint) { \ - EXPOSE_PARAMS(EntityData::EntityParametersIIII) \ - debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%d, %d) - action: %s", params->param1, params->param2, ACTION_NAME(savepoint.action)); - -// setup with three uint parameters -#define IMPLEMENT_FUNCTION_III(index, class, name, paramType1, paramType2, paramType3) \ - void class::setup_##name(paramType1 param1, paramType2 param2, paramType3 param3) { \ - BEGIN_SETUP(class, name, index, EntityData::EntityParametersIIII) \ - EntityData::EntityParametersIIII *params = (EntityData::EntityParametersIIII*)_data->getCurrentParameters(); \ - params->param1 = param1; \ - params->param2 = param2; \ - params->param3 = param3; \ - END_SETUP() \ - } \ - void class::name(const SavePoint &savepoint) { \ - EXPOSE_PARAMS(EntityData::EntityParametersIIII) \ - debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%d, %d, %d) - action: %s", params->param1, params->param2, params->param3, ACTION_NAME(savepoint.action)); - -// setup with one char *parameter -#define IMPLEMENT_FUNCTION_S(index, class, name) \ - void class::setup_##name(const char *seq1) { \ - BEGIN_SETUP(class, name, index, EntityData::EntityParametersSIIS) \ - EntityData::EntityParametersSIIS *params = (EntityData::EntityParametersSIIS*)_data->getCurrentParameters(); \ - strncpy((char *)¶ms->seq1, seq1, 12); \ - END_SETUP() \ - } \ - void class::name(const SavePoint &savepoint) { \ - EXPOSE_PARAMS(EntityData::EntityParametersSIIS) \ - debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%s) - action: %s", (char *)¶ms->seq1, ACTION_NAME(savepoint.action)); - -// setup with one char *parameter and one uint -#define IMPLEMENT_FUNCTION_SI(index, class, name, paramType2) \ - void class::setup_##name(const char *seq1, paramType2 param4) { \ - BEGIN_SETUP(class, name, index, EntityData::EntityParametersSIIS) \ - EntityData::EntityParametersSIIS *params = (EntityData::EntityParametersSIIS*)_data->getCurrentParameters(); \ - strncpy((char *)¶ms->seq1, seq1, 12); \ - params->param4 = param4; \ - END_SETUP() \ - } \ - void class::name(const SavePoint &savepoint) { \ - EXPOSE_PARAMS(EntityData::EntityParametersSIIS) \ - debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%s, %d) - action: %s", (char *)¶ms->seq1, params->param4, ACTION_NAME(savepoint.action)); - -// setup with one char *parameter and two uints -#define IMPLEMENT_FUNCTION_SII(index, class, name, paramType2, paramType3) \ - void class::setup_##name(const char *seq1, paramType2 param4, paramType3 param5) { \ - BEGIN_SETUP(class, name, index, EntityData::EntityParametersSIIS) \ - EntityData::EntityParametersSIIS *params = (EntityData::EntityParametersSIIS*)_data->getCurrentParameters(); \ - strncpy((char *)¶ms->seq1, seq1, 12); \ - params->param4 = param4; \ - params->param5 = param5; \ - END_SETUP() \ - } \ - void class::name(const SavePoint &savepoint) { \ - EXPOSE_PARAMS(EntityData::EntityParametersSIIS) \ - debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%s, %d, %d) - action: %s", (char *)¶ms->seq1, params->param4, params->param5, ACTION_NAME(savepoint.action)); - -// setup with one char *parameter and three uints -#define IMPLEMENT_FUNCTION_SIII(index, class, name, paramType2, paramType3, paramType4) \ - void class::setup_##name(const char *seq, paramType2 param4, paramType3 param5, paramType4 param6) { \ - BEGIN_SETUP(class, name, index, EntityData::EntityParametersSIII) \ - EntityData::EntityParametersSIII *params = (EntityData::EntityParametersSIII*)_data->getCurrentParameters(); \ - strncpy((char *)¶ms->seq, seq, 12); \ - params->param4 = param4; \ - params->param5 = param5; \ - params->param6 = param6; \ - END_SETUP() \ - } \ - void class::name(const SavePoint &savepoint) { \ - EXPOSE_PARAMS(EntityData::EntityParametersSIII) \ - debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%s, %d, %d, %d) - action: %s", (char *)¶ms->seq, params->param4, params->param5, params->param6, ACTION_NAME(savepoint.action)); - -#define IMPLEMENT_FUNCTION_SIIS(index, class, name, paramType2, paramType3) \ - void class::setup_##name(const char *seq1, paramType2 param4, paramType3 param5, const char *seq2) { \ - BEGIN_SETUP(class, name, index, EntityData::EntityParametersSIIS) \ - EntityData::EntityParametersSIIS *params = (EntityData::EntityParametersSIIS*)_data->getCurrentParameters(); \ - strncpy((char *)¶ms->seq1, seq1, 12); \ - params->param4 = param4; \ - params->param5 = param5; \ - strncpy((char *)¶ms->seq2, seq2, 12); \ - END_SETUP() \ - } \ - void class::name(const SavePoint &savepoint) { \ - EXPOSE_PARAMS(EntityData::EntityParametersSIIS) \ - debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%s, %d, %d, %s) - action: %s", (char *)¶ms->seq1, params->param4, params->param5, (char *)¶ms->seq2, ACTION_NAME(savepoint.action)); - -#define IMPLEMENT_FUNCTION_SS(index, class, name) \ - void class::setup_##name(const char *seq1, const char *seq2) { \ - BEGIN_SETUP(class, name, index, EntityData::EntityParametersSSII) \ - EntityData::EntityParametersSSII *params = (EntityData::EntityParametersSSII*)_data->getCurrentParameters(); \ - strncpy((char *)¶ms->seq1, seq1, 12); \ - strncpy((char *)¶ms->seq2, seq2, 12); \ - END_SETUP() \ - } \ - void class::name(const SavePoint &savepoint) { \ - EXPOSE_PARAMS(EntityData::EntityParametersSSII) \ - debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%s, %s) - action: %s", (char *)¶ms->seq1, (char *)¶ms->seq2, ACTION_NAME(savepoint.action)); - -#define IMPLEMENT_FUNCTION_SSI(index, class, name, paramType3) \ - void class::setup_##name(const char *seq1, const char *seq2, paramType3 param7) { \ - BEGIN_SETUP(class, name, index, EntityData::EntityParametersSSII) \ - EntityData::EntityParametersSSII *params = (EntityData::EntityParametersSSII*)_data->getCurrentParameters(); \ - strncpy((char *)¶ms->seq1, seq1, 12); \ - strncpy((char *)¶ms->seq2, seq2, 12); \ - params->param7 = param7; \ - END_SETUP() \ - } \ - void class::name(const SavePoint &savepoint) { \ - EXPOSE_PARAMS(EntityData::EntityParametersSSII) \ - debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%s, %s, %d) - action: %s", (char *)¶ms->seq1, (char *)¶ms->seq2, params->param7, ACTION_NAME(savepoint.action)); - -#define IMPLEMENT_FUNCTION_IS(index, class, name, paramType) \ - void class::setup_##name(paramType param1, const char *seq) { \ - BEGIN_SETUP(class, name, index, EntityData::EntityParametersISII) \ - EntityData::EntityParametersISII *params = (EntityData::EntityParametersISII*)_data->getCurrentParameters(); \ - params->param1 = (unsigned int)param1; \ - strncpy((char *)¶ms->seq, seq, 12); \ - END_SETUP() \ - } \ - void class::name(const SavePoint &savepoint) { \ - EXPOSE_PARAMS(EntityData::EntityParametersISII) \ - debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%d, %s) - action: %s", params->param1, (char *)¶ms->seq, ACTION_NAME(savepoint.action)); - -#define IMPLEMENT_FUNCTION_ISS(index, class, name, paramType) \ - void class::setup_##name(paramType param1, const char *seq1, const char *seq2) { \ - BEGIN_SETUP(class, name, index, EntityData::EntityParametersISSI) \ - EntityData::EntityParametersISSI *params = (EntityData::EntityParametersISSI*)_data->getCurrentParameters(); \ - params->param1 = param1; \ - strncpy((char *)¶ms->seq1, seq1, 12); \ - strncpy((char *)¶ms->seq2, seq2, 12); \ - END_SETUP() \ - } \ - void class::name(const SavePoint &savepoint) { \ - EXPOSE_PARAMS(EntityData::EntityParametersISSI) \ - debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%d, %s, %s) - action: %s", params->param1, (char *)¶ms->seq1, (char *)¶ms->seq2, ACTION_NAME(savepoint.action)); - -#define IMPLEMENT_FUNCTION_IIS(index, class, name, paramType1, paramType2) \ - void class::setup_##name(paramType1 param1, paramType2 param2, const char *seq) { \ - BEGIN_SETUP(class, name, index, EntityData::EntityParametersIISI) \ - EntityData::EntityParametersIISI *params = (EntityData::EntityParametersIISI*)_data->getCurrentParameters(); \ - params->param1 = param1; \ - params->param2 = param2; \ - strncpy((char *)¶ms->seq, seq, 12); \ - END_SETUP() \ - } \ - void class::name(const SavePoint &savepoint) { \ - EXPOSE_PARAMS(EntityData::EntityParametersIISI) \ - debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%d, %d, %s) - action: %s", params->param1, params->param2, (char *)¶ms->seq, ACTION_NAME(savepoint.action)); - -#define IMPLEMENT_FUNCTION_IISS(index, class, name, paramType1, paramType2) \ - void class::setup_##name(paramType1 param1, paramType2 param2, const char *seq1, const char *seq2) { \ - BEGIN_SETUP(class, name, index, EntityData::EntityParametersIISS) \ - EntityData::EntityParametersIISS *params = (EntityData::EntityParametersIISS*)_data->getCurrentParameters(); \ - params->param1 = param1; \ - params->param2 = param2; \ - strncpy((char *)¶ms->seq1, seq1, 12); \ - strncpy((char *)¶ms->seq2, seq2, 12); \ - END_SETUP() \ - } \ - void class::name(const SavePoint &savepoint) { \ - EXPOSE_PARAMS(EntityData::EntityParametersIISS) \ - debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%d, %d, %s, %s) - action: %s", params->param1, params->param2, (char *)¶ms->seq1, (char *)¶ms->seq2, ACTION_NAME(savepoint.action)); - - -////////////////////////////////////////////////////////////////////////// -// Misc -////////////////////////////////////////////////////////////////////////// -#define RESET_ENTITY_STATE(entity, class, function) \ - getEntities()->resetState(entity); \ - ((class *)getEntities()->get(entity))->function(); - -////////////////////////////////////////////////////////////////////////// -// Parameters macros (for default IIII parameters) -////////////////////////////////////////////////////////////////////////// -#define CURRENT_PARAM(index, id) \ - ((EntityData::EntityParametersIIII*)_data->getCurrentParameters(index))->param##id - -#define ENTITY_PARAM(index, id) \ - ((EntityData::EntityParametersIIII*)_data->getParameters(8, index))->param##id - -////////////////////////////////////////////////////////////////////////// -// Time check macros -////////////////////////////////////////////////////////////////////////// -#define TIME_CHECK(timeValue, parameter, function) \ - if (getState()->time > timeValue && !parameter) { \ - parameter = 1; \ - function(); \ - break; \ - } - -#define TIME_CHECK_SAVEPOINT(timeValue, parameter, entity1, entity2, action) \ - if (getState()->time > timeValue && !parameter) { \ - parameter = 1; \ - getSavePoints()->push(entity1, entity2, action); \ - } - -#define TIME_CHECK_CALLBACK(timeValue, parameter, callback, function) \ - if (getState()->time > timeValue && !parameter) { \ - parameter = 1; \ - setCallback(callback); \ - function(); \ - break; \ - } - -#define TIME_CHECK_CALLBACK_1(timeValue, parameter, callback, function, param1) \ - if (getState()->time > timeValue && !parameter) { \ - parameter = 1; \ - setCallback(callback); \ - function(param1); \ - break; \ - } - -#define TIME_CHECK_CALLBACK_2(timeValue, parameter, callback, function, param1, param2) \ - if (getState()->time > timeValue && !parameter) { \ - parameter = 1; \ - setCallback(callback); \ - function(param1, param2); \ - break; \ - } - -#define TIME_CHECK_CALLBACK_3(timeValue, parameter, callback, function, param1, param2, param3) \ - if (getState()->time > timeValue && !parameter) { \ - parameter = 1; \ - setCallback(callback); \ - function(param1, param2, param3); \ - break; \ - } - -#define TIME_CHECK_CALLBACK_INVENTORY(timeValue, parameter, callback, function) \ - if (getState()->time > timeValue && !parameter) { \ - parameter = 1; \ - getData()->inventoryItem = kItemNone; \ - setCallback(callback); \ - function(); \ - break; \ - } - -#define TIME_CHECK_CALLBACK_ACTION(timeValue, parameter) \ - if (getState()->time > timeValue && !parameter) { \ - parameter = 1; \ - CALLBACK_ACTION(); \ - break; \ - } - -#define TIME_CHECK_PLAYSOUND_UPDATEPOSITION(timeValue, parameter, callback, sound, position) \ - if (getState()->time > timeValue && !parameter) { \ - parameter = 1; \ - getData()->entityPosition = position; \ - setCallback(callback); \ - setup_playSound(sound); \ - break; \ - } - -#define TIME_CHECK_OBJECT(timeValue, parameter, object, location) \ - if (getState()->time > timeValue && !parameter) { \ - parameter = 1; \ - getObjects()->updateLocation2(object, location); \ - } - -#define TIME_CHECK_CAR(timeValue, parameter, callback, function) {\ - if ((getState()->time <= timeValue && !getEntities()->isPlayerInCar(kCarGreenSleeping)) || !parameter) \ - parameter = (uint)getState()->time + 75; \ - if (getState()->time > timeValue || parameter < getState()->time) { \ - parameter = kTimeInvalid; \ - setCallback(callback); \ - function(); \ - break; \ - } \ -} - -////////////////////////////////////////////////////////////////////////// -// Callback action -////////////////////////////////////////////////////////////////////////// -#define CALLBACK_ACTION() { \ - if (getData()->currentCall == 0) \ - error("[CALLBACK_ACTION] currentCall is already 0, cannot proceed"); \ - getData()->currentCall--; \ - getSavePoints()->setCallback(_entityIndex, _callbacks[_data->getCurrentCallback()]); \ - getSavePoints()->call(_entityIndex, _entityIndex, kActionCallback); \ - } - -////////////////////////////////////////////////////////////////////////// -// Param update -////////////////////////////////////////////////////////////////////////// -#define UPDATE_PARAM(parameter, type, value) { \ - if (!parameter) \ - parameter = (uint)(type + value); \ - if (parameter >= type) \ - break; \ - parameter = kTimeInvalid; \ -} - -// Todo: replace with UPDATE_PARAM_PROC as appropriate -#define UPDATE_PARAM_GOTO(parameter, type, value, label) { \ - if (!parameter) \ - parameter = (uint)(type + value); \ - if (parameter >= type) \ - goto label; \ - parameter = kTimeInvalid; \ -} - -// Updating parameter with code inside the check -#define UPDATE_PARAM_PROC(parameter, type, value) \ - if (!parameter) \ - parameter = (uint)(type + value); \ - if (parameter < type) { \ - parameter = kTimeInvalid; - -#define UPDATE_PARAM_PROC_TIME(timeValue, test, parameter, value) \ - if (getState()->time <= timeValue) { \ - if (test || !parameter) \ - parameter = (uint)(getState()->time + value); \ - } \ - if (parameter < getState()->time || getState()->time > timeValue) { \ - parameter = kTimeInvalid; - -#define UPDATE_PARAM_PROC_END } - -// Updating parameter with an added check (and code inside the check) -#define UPDATE_PARAM_CHECK(parameter, type, value) \ - if (!parameter || parameter < type) { \ - if (!parameter) \ - parameter = (uint)(type + value); - -////////////////////////////////////////////////////////////////////////// -// Compartments -////////////////////////////////////////////////////////////////////////// -// Go from one compartment to another (or the same one if no optional args are passed -#define COMPARTMENT_TO(class, compartmentFrom, positionFrom, sequenceFrom, sequenceTo) \ - switch (savepoint.action) { \ - default: \ - break; \ - case kActionDefault: \ - getData()->entityPosition = positionFrom; \ - setCallback(1); \ - setup_enterExitCompartment(sequenceFrom, compartmentFrom); \ - break; \ - case kActionCallback: \ - switch (getCallback()) { \ - default: \ - break; \ - case 1: \ - setCallback(2); \ - setup_enterExitCompartment(sequenceTo, compartmentFrom); \ - break; \ - case 2: \ - getData()->entityPosition = positionFrom; \ - getEntities()->clearSequences(_entityIndex); \ - CALLBACK_ACTION(); \ - } \ - break; \ - } - -#define COMPARTMENT_FROM_TO(class, compartmentFrom, positionFrom, sequenceFrom, compartmentTo, positionTo, sequenceTo) \ - switch (savepoint.action) { \ - default: \ - break; \ - case kActionDefault: \ - getData()->entityPosition = positionFrom; \ - getData()->location = kLocationOutsideCompartment; \ - setCallback(1); \ - setup_enterExitCompartment(sequenceFrom, compartmentFrom); \ - break; \ - case kActionCallback: \ - switch (getCallback()) { \ - default: \ - break; \ - case 1: \ - setCallback(2); \ - setup_updateEntity(kCarGreenSleeping, positionTo); \ - break; \ - case 2: \ - setCallback(3); \ - setup_enterExitCompartment(sequenceTo, compartmentTo); \ - break; \ - case 3: \ - getData()->location = kLocationInsideCompartment; \ - getEntities()->clearSequences(_entityIndex); \ - CALLBACK_ACTION(); \ - break; \ - } \ - break; \ - } } // End of namespace LastExpress diff --git a/engines/lastexpress/entities/francois.cpp b/engines/lastexpress/entities/francois.cpp index 46cd790ffb..d2bbc9854c 100644 --- a/engines/lastexpress/entities/francois.cpp +++ b/engines/lastexpress/entities/francois.cpp @@ -32,7 +32,6 @@ #include "lastexpress/sound/queue.h" -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" namespace LastExpress { @@ -114,7 +113,7 @@ IMPLEMENT_FUNCTION_II(8, Francois, updateEntity, CarIndex, EntityPosition) case kActionNone: if (getEntities()->updateEntity(_entityIndex, (CarIndex)params->param1, (EntityPosition)params->param2)) { - CALLBACK_ACTION(); + callbackAction(); } else { if (!getEntities()->isDistanceBetweenEntities(kEntityFrancois, kEntityPlayer, 2000) || !getInventory()->hasItem(kItemFirebird) @@ -169,7 +168,7 @@ IMPLEMENT_FUNCTION_II(8, Francois, updateEntity, CarIndex, EntityPosition) case kActionDefault: if (getEntities()->updateEntity(_entityIndex, (CarIndex)params->param1, (EntityPosition)params->param2)) - CALLBACK_ACTION(); + callbackAction(); break; case kActionCallback: @@ -225,7 +224,7 @@ IMPLEMENT_FUNCTION(9, Francois, function9) case 2: getData()->location = kLocationOutsideCompartment; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -263,7 +262,7 @@ IMPLEMENT_FUNCTION(10, Francois, function10) getData()->location = kLocationInsideCompartment; getEntities()->clearSequences(kEntityFrancois); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -279,7 +278,7 @@ IMPLEMENT_FUNCTION_I(11, Francois, function11, TimeValue) case kActionNone: if (!getSoundQueue()->isBuffered(kEntityFrancois)) { - UPDATE_PARAM_PROC(CURRENT_PARAM(1, 1), getState()->timeTicks, params->param6) + if (Entity::updateParameter(CURRENT_PARAM(1, 1), getState()->timeTicks, params->param6)) { switch (rnd(7)) { default: break; @@ -312,7 +311,7 @@ IMPLEMENT_FUNCTION_I(11, Francois, function11, TimeValue) params->param6 = 15 * rnd(7); CURRENT_PARAM(1, 1) = 0; - UPDATE_PARAM_PROC_END + } } if (!getEntities()->hasValidFrame(kEntityFrancois) || !getEntities()->isWalkingOppositeToPlayer(kEntityFrancois)) @@ -442,7 +441,7 @@ label_callback: break; case 5: - CALLBACK_ACTION(); + callbackAction(); break; case 6: @@ -465,7 +464,7 @@ label_callback: getData()->field_4A3 = 30; getData()->inventoryItem = kItemNone; - CALLBACK_ACTION(); + callbackAction(); break; case kAction205346192: @@ -524,7 +523,7 @@ IMPLEMENT_FUNCTION(12, Francois, function12) break; case 7: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -609,7 +608,7 @@ IMPLEMENT_FUNCTION(13, Francois, function13) break; case 11: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -708,7 +707,7 @@ IMPLEMENT_FUNCTION_IIS(14, Francois, function14, ObjectIndex, EntityPosition) break; case 13: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -764,7 +763,7 @@ IMPLEMENT_FUNCTION(15, Francois, function15) case 7: if (!getEntities()->isInsideCompartment(kEntityMmeBoutarel, kCarRedSleeping, kPosition_5790)) { - CALLBACK_ACTION(); + callbackAction(); break; } @@ -773,7 +772,7 @@ IMPLEMENT_FUNCTION(15, Francois, function15) break; case 8: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -830,7 +829,7 @@ IMPLEMENT_FUNCTION(16, Francois, function16) getData()->entityPosition = kPosition_5790; getData()->location = kLocationInsideCompartment; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -849,7 +848,7 @@ IMPLEMENT_FUNCTION(17, Francois, chapter1) break; case kActionNone: - TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler); + Entity::timeCheck(kTimeChapter1, params->param1, WRAP_SETUP_FUNCTION(Francois, setup_chapter1Handler)); break; case kActionDefault: @@ -867,7 +866,7 @@ IMPLEMENT_FUNCTION(18, Francois, chapter1Handler) break; case kActionNone: - TIME_CHECK_CALLBACK_1(kTimeParisEpernay, params->param1, 1, setup_function11, kTime1093500); + timeCheckCallback(kTimeParisEpernay, params->param1, 1, kTime1093500); break; case kActionCallback: @@ -884,7 +883,7 @@ IMPLEMENT_FUNCTION(19, Francois, function19) break; case kActionNone: - TIME_CHECK_CALLBACK(kTime1161000, params->param1, 2, setup_function12); + Entity::timeCheckCallback(kTime1161000, params->param1, 2, WRAP_SETUP_FUNCTION(Francois, setup_function12)); break; case kAction101107728: @@ -979,17 +978,21 @@ IMPLEMENT_FUNCTION(23, Francois, function23) } label_callback_1: - TIME_CHECK_CALLBACK_1(kTime1764000, params->param1, 2, setup_playSound, "Fra2011"); + if (Entity::timeCheckCallback(kTime1764000, params->param1, 2, "Fra2011", WRAP_SETUP_FUNCTION_S(Francois, setup_playSound))) + break; label_callback_2: - TIME_CHECK_CALLBACK(kTime1800000, params->param2, 3, setup_function13); + if (Entity::timeCheckCallback(kTime1800000, params->param2, 3, WRAP_SETUP_FUNCTION(Francois, setup_function13))) + break; label_callback_3: if (!getInventory()->hasItem(kItemWhistle) && getInventory()->get(kItemWhistle)->location != kObjectLocation3) { - TIME_CHECK_CALLBACK_1(kTime1768500, params->param3, 4, setup_function11, kTime1773000); + if (timeCheckCallback(kTime1768500, params->param3, 4, kTime1773000)) + break; label_callback_4: - TIME_CHECK_CALLBACK_1(kTime1827000, params->param4, 5, setup_function11, kTime1831500); + if (timeCheckCallback(kTime1827000, params->param4, 5, kTime1831500)) + break; } label_callback_5: @@ -999,18 +1002,19 @@ label_callback_5: } if (params->param5 != kTimeInvalid) { - UPDATE_PARAM_PROC_TIME(kTimeEnd, !getEntities()->isDistanceBetweenEntities(kEntityFrancois, kEntityPlayer, 2000), params->param5, 75); + if (Entity::updateParameterTime(kTimeEnd, !getEntities()->isDistanceBetweenEntities(kEntityFrancois, kEntityPlayer, 2000), params->param5, 75)) { setCallback(6); setup_playSound("Fra2010"); break; - UPDATE_PARAM_PROC_END + } } label_callback_6: - TIME_CHECK_CALLBACK_3(kTime1782000, params->param6, 7, setup_function14, kObjectCompartmentC, kPosition_6470, "c"); + if (timeCheckCallbackCompartment(kTime1782000, params->param6, 7, kObjectCompartmentC, kPosition_6470, "c")) + break; label_callback_7: - TIME_CHECK_CALLBACK_3(kTime1813500, params->param7, 8, setup_function14, kObjectCompartmentF, kPosition_4070, "f"); + timeCheckCallbackCompartment(kTime1813500, params->param7, 8, kObjectCompartmentF, kPosition_4070, "f"); break; case kActionCallback: @@ -1086,32 +1090,41 @@ IMPLEMENT_FUNCTION(25, Francois, chapter3Handler) } label_callback_2: - TIME_CHECK_CALLBACK(kTime2025000, params->param3, 3, setup_function12); + if (Entity::timeCheckCallback(kTime2025000, params->param3, 3, WRAP_SETUP_FUNCTION(Francois, setup_function12))) + break; label_callback_3: - TIME_CHECK_CALLBACK(kTime2052000, params->param4, 4, setup_function12); + if (Entity::timeCheckCallback(kTime2052000, params->param4, 4, WRAP_SETUP_FUNCTION(Francois, setup_function12))) + break; label_callback_4: - TIME_CHECK_CALLBACK(kTime2079000, params->param5, 5, setup_function12); + if (Entity::timeCheckCallback(kTime2079000, params->param5, 5, WRAP_SETUP_FUNCTION(Francois, setup_function12))) + break; label_callback_5: - TIME_CHECK_CALLBACK(kTime2092500, params->param6, 6, setup_function12); + if (Entity::timeCheckCallback(kTime2092500, params->param6, 6, WRAP_SETUP_FUNCTION(Francois, setup_function12))) + break; label_callback_6: - TIME_CHECK_CALLBACK(kTime2173500, params->param7, 7, setup_function12); + if (Entity::timeCheckCallback(kTime2173500, params->param7, 7, WRAP_SETUP_FUNCTION(Francois, setup_function12))) + break; label_callback_7: - TIME_CHECK_CALLBACK(kTime2182500, params->param8, 8, setup_function12); + if (Entity::timeCheckCallback(kTime2182500, params->param8, 8, WRAP_SETUP_FUNCTION(Francois, setup_function12))) + break; label_callback_8: - TIME_CHECK_CALLBACK(kTime2241000, CURRENT_PARAM(1, 1), 9, setup_function12); + if (Entity::timeCheckCallback(kTime2241000, CURRENT_PARAM(1, 1), 9, WRAP_SETUP_FUNCTION(Francois, setup_function12))) + break; label_callback_9: if (!getInventory()->hasItem(kItemWhistle) && getInventory()->get(kItemWhistle)->location != kObjectLocation3) { - TIME_CHECK_CALLBACK_1(kTime2011500, CURRENT_PARAM(1, 2), 10, setup_function11, kTime2016000); + if (timeCheckCallback(kTime2011500, CURRENT_PARAM(1, 2), 10, kTime2016000)) + break; label_callback_10: - TIME_CHECK_CALLBACK_1(kTime2115000, CURRENT_PARAM(1, 3), 11, setup_function11, kTime2119500); + if (timeCheckCallback(kTime2115000, CURRENT_PARAM(1, 3), 11, kTime2119500)) + break; } label_callback_11: @@ -1129,13 +1142,15 @@ label_callback_11: } label_callback_12: - TIME_CHECK_CALLBACK_3(kTime2040300, CURRENT_PARAM(1, 5), 13, setup_function14, kObjectCompartmentE, kPosition_4840, "e"); + if (timeCheckCallbackCompartment(kTime2040300, CURRENT_PARAM(1, 5), 13, kObjectCompartmentE, kPosition_4840, "e")) + break; label_callback_13: - TIME_CHECK_CALLBACK_3(kTime2040300, CURRENT_PARAM(1, 6), 14, setup_function14, kObjectCompartmentF, kPosition_4070, "f"); + if (timeCheckCallbackCompartment(kTime2040300, CURRENT_PARAM(1, 6), 14, kObjectCompartmentF, kPosition_4070, "f")) + break; label_callback_14: - TIME_CHECK_CALLBACK_3(kTime2040300, CURRENT_PARAM(1, 7), 15, setup_function14, kObjectCompartmentB, kPosition_7500, "b"); + timeCheckCallbackCompartment(kTime2040300, CURRENT_PARAM(1, 7), 15, kObjectCompartmentB, kPosition_7500, "b"); } } break; @@ -1291,4 +1306,33 @@ IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// IMPLEMENT_NULL_FUNCTION(31, Francois) + +////////////////////////////////////////////////////////////////////////// +// Helper functions +////////////////////////////////////////////////////////////////////////// +bool Francois::timeCheckCallbackCompartment(TimeValue timeValue, uint ¶meter, byte callback, ObjectIndex compartment, EntityPosition position, const char* sequenceSuffix) { + if (getState()->time > timeValue && !parameter) { + parameter = 1; + setCallback(callback); + setup_function14(compartment, position, sequenceSuffix); + + return true; + } + + return false; +} + +bool Francois::timeCheckCallback(TimeValue timeValue, uint ¶meter, byte callback, TimeValue timeValue2) { + if (getState()->time > timeValue && !parameter) { + parameter = 1; + setCallback(callback); + setup_function11(timeValue2); + + return true; + } + + return false; +} + + } // End of namespace LastExpress diff --git a/engines/lastexpress/entities/francois.h b/engines/lastexpress/entities/francois.h index 19eca6fb50..51270fa4b6 100644 --- a/engines/lastexpress/entities/francois.h +++ b/engines/lastexpress/entities/francois.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_FRANCOIS_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { @@ -160,6 +159,10 @@ public: DECLARE_FUNCTION(function30) DECLARE_NULL_FUNCTION() + +private: + bool timeCheckCallbackCompartment(TimeValue timeValue, uint ¶meter, byte callback, ObjectIndex compartment, EntityPosition position, const char* sequenceSuffix); + bool timeCheckCallback(TimeValue timeValue, uint ¶meter, byte callback, TimeValue timeValue2); }; } // End of namespace LastExpress diff --git a/engines/lastexpress/entities/gendarmes.cpp b/engines/lastexpress/entities/gendarmes.cpp index daa50956d3..a912fa4ecb 100644 --- a/engines/lastexpress/entities/gendarmes.cpp +++ b/engines/lastexpress/entities/gendarmes.cpp @@ -31,7 +31,6 @@ #include "lastexpress/game/state.h" #include "lastexpress/lastexpress.h" -#include "lastexpress/helpers.h" namespace LastExpress { @@ -67,7 +66,7 @@ IMPLEMENT_FUNCTION(2, Gendarmes, chapter1) break; case kActionNone: - TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler); + Entity::timeCheck(kTimeChapter1, params->param1, WRAP_SETUP_FUNCTION(Gendarmes, setup_chapter1Handler)); break; case kActionDefault: @@ -195,7 +194,7 @@ IMPLEMENT_FUNCTION_IISS(9, Gendarmes, function9, CarIndex, EntityPosition) break; case 1: - CALLBACK_ACTION(); + callbackAction(); break; case 2: @@ -233,7 +232,7 @@ IMPLEMENT_FUNCTION_IISS(9, Gendarmes, function9, CarIndex, EntityPosition) case 6: getData()->location = kLocationOutsideCompartment; getEntities()->exitCompartment(kEntityGendarmes, (ObjectIndex)parameters2->param5); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -267,11 +266,12 @@ IMPLEMENT_FUNCTION_III(10, Gendarmes, function10, CarIndex, EntityPosition, Obje getSound()->playSound(kEntityGendarmes, "POL1046A", kFlagDefault); } - UPDATE_PARAM(params->param7, getState()->timeTicks, 300); + if (!Entity::updateParameter(params->param7, getState()->timeTicks, 300)) + break; if (!params->param4 && getEntities()->isOutsideAlexeiWindow()) { getObjects()->update((ObjectIndex)params->param3, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); } else { if (getEntities()->isOutsideAlexeiWindow()) getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49); @@ -322,7 +322,7 @@ IMPLEMENT_FUNCTION_III(10, Gendarmes, function10, CarIndex, EntityPosition, Obje getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true); getObjects()->update((ObjectIndex)params->param3, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); break; case 4: @@ -330,7 +330,7 @@ IMPLEMENT_FUNCTION_III(10, Gendarmes, function10, CarIndex, EntityPosition, Obje getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverPolice1, true); getObjects()->update((ObjectIndex)params->param3, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); break; case 5: @@ -552,13 +552,14 @@ void Gendarmes::arrest(const SavePoint &savepoint, bool shouldPlaySound, SoundFl case kActionNone: if (checkCallback) { EXPOSE_PARAMS(EntityData::EntityParametersIIII); - TIME_CHECK_CALLBACK_ACTION(params->param1, params->param2); + if (Entity::timeCheckCallbackAction((TimeValue)params->param1, params->param2)) + break; } if (shouldUpdateEntity) { EXPOSE_PARAMS(EntityData::EntityParametersIIII); if (getEntities()->updateEntity(kEntityGendarmes, (CarIndex)params->param1, (EntityPosition)params->param2)) { - CALLBACK_ACTION(); + callbackAction(); break; } } @@ -582,7 +583,7 @@ void Gendarmes::arrest(const SavePoint &savepoint, bool shouldPlaySound, SoundFl break; case kActionExitCompartment: - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: @@ -599,7 +600,7 @@ void Gendarmes::arrest(const SavePoint &savepoint, bool shouldPlaySound, SoundFl if (shouldUpdateEntity) { EXPOSE_PARAMS(EntityData::EntityParametersIIII); if (getEntities()->updateEntity(kEntityGendarmes, (CarIndex)params->param1, (EntityPosition)params->param2)) { - CALLBACK_ACTION(); + callbackAction(); break; } } diff --git a/engines/lastexpress/entities/gendarmes.h b/engines/lastexpress/entities/gendarmes.h index d999cfc1fd..a761643531 100644 --- a/engines/lastexpress/entities/gendarmes.h +++ b/engines/lastexpress/entities/gendarmes.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_GENDARMES_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" #include "lastexpress/sound/sound.h" diff --git a/engines/lastexpress/entities/hadija.cpp b/engines/lastexpress/entities/hadija.cpp index 8ec972b939..e9abcd888a 100644 --- a/engines/lastexpress/entities/hadija.cpp +++ b/engines/lastexpress/entities/hadija.cpp @@ -28,10 +28,7 @@ #include "lastexpress/game/savepoint.h" #include "lastexpress/game/state.h" -#include "lastexpress/sound/sound.h" - #include "lastexpress/lastexpress.h" -#include "lastexpress/helpers.h" namespace LastExpress { @@ -89,22 +86,22 @@ IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// IMPLEMENT_FUNCTION(6, Hadija, compartment6) - COMPARTMENT_TO(Hadija, kObjectCompartment6, kPosition_4070, "619Cf", "619Df"); + Entity::goToCompartment(savepoint, kObjectCompartment6, kPosition_4070, "619Cf", "619Df"); IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// IMPLEMENT_FUNCTION(7, Hadija, compartment8) - COMPARTMENT_TO(Hadija, kObjectCompartment8, kPosition_2740, "619Ch", "619Dh"); + Entity::goToCompartment(savepoint, kObjectCompartment8, kPosition_2740, "619Ch", "619Dh"); IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// IMPLEMENT_FUNCTION(8, Hadija, compartment6to8) - COMPARTMENT_FROM_TO(Hadija, kObjectCompartment6, kPosition_4070, "619Bf", kObjectCompartment8, kPosition_2740, "619Ah"); + Entity::goToCompartmentFromCompartment(savepoint, kObjectCompartment6, kPosition_4070, "619Bf", kObjectCompartment8, kPosition_2740, "619Ah"); IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// IMPLEMENT_FUNCTION(9, Hadija, compartment8to6) - COMPARTMENT_FROM_TO(Hadija, kObjectCompartment8, kPosition_2740, "619Bh", kObjectCompartment6, kPosition_4070, "619Af"); + Entity::goToCompartmentFromCompartment(savepoint, kObjectCompartment8, kPosition_2740, "619Bh", kObjectCompartment6, kPosition_4070, "619Af"); IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// @@ -114,7 +111,7 @@ IMPLEMENT_FUNCTION(10, Hadija, chapter1) break; case kActionNone: - TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler); + Entity::timeCheck(kTimeChapter1, params->param1, WRAP_SETUP_FUNCTION(Hadija, setup_chapter1Handler)); break; case kActionDefault: @@ -133,10 +130,12 @@ IMPLEMENT_FUNCTION(11, Hadija, chapter1Handler) break; case kActionNone: - TIME_CHECK_PLAYSOUND_UPDATEPOSITION(kTimeParisEpernay, params->param1, 1, "Har1100", kPosition_4840); + if (Entity::timeCheckPlaySoundUpdatePosition(kTimeParisEpernay, params->param1, 1, "Har1100", kPosition_4840)) + break; label_callback1: - TIME_CHECK_CALLBACK(kTime1084500, params->param2, 2, setup_compartment6to8); + if (Entity::timeCheckCallback(kTime1084500, params->param2, 2, WRAP_SETUP_FUNCTION(Hadija, setup_compartment6to8))) + break; label_callback2: if (params->param3 != kTimeInvalid && getState()->time > kTime1093500) { @@ -164,7 +163,8 @@ label_callback2: } label_callback3: - TIME_CHECK_CALLBACK(kTime1156500, params->param4, 4, setup_compartment8to6); + if (Entity::timeCheckCallback(kTime1156500, params->param4, 4, WRAP_SETUP_FUNCTION(Hadija, setup_compartment8to6))) + break; label_callback4: if (params->param5 != kTimeInvalid && getState()->time > kTime1165500) { @@ -254,7 +254,7 @@ IMPLEMENT_FUNCTION(14, Hadija, chapter2Handler) } if (params->param2 == kTimeInvalid || getState()->time <= kTime1786500) { - TIME_CHECK_CALLBACK(kTime1822500, params->param3, 2, setup_compartment8to6); + Entity::timeCheckCallback(kTime1822500, params->param3, 2, WRAP_SETUP_FUNCTION(Hadija, setup_compartment8to6)); break; } @@ -264,7 +264,7 @@ IMPLEMENT_FUNCTION(14, Hadija, chapter2Handler) params->param2 = (uint)getState()->time + 75; if (params->param2 >= getState()->time) { - TIME_CHECK_CALLBACK(kTime1822500, params->param3, 2, setup_compartment8to6); + Entity::timeCheckCallback(kTime1822500, params->param3, 2, WRAP_SETUP_FUNCTION(Hadija, setup_compartment8to6)); break; } } @@ -281,7 +281,7 @@ IMPLEMENT_FUNCTION(14, Hadija, chapter2Handler) break; case 1: - TIME_CHECK_CALLBACK(kTime1822500, params->param3, 2, setup_compartment8to6); + Entity::timeCheckCallback(kTime1822500, params->param3, 2, WRAP_SETUP_FUNCTION(Hadija, setup_compartment8to6)); break; case 2: @@ -321,20 +321,26 @@ IMPLEMENT_FUNCTION(16, Hadija, chapter3Handler) break; case kActionNone: - TIME_CHECK_CALLBACK(kTime1998000, params->param1, 1, setup_compartment6to8); + if (Entity::timeCheckCallback(kTime1998000, params->param1, 1, WRAP_SETUP_FUNCTION(Hadija, setup_compartment6to8))) + break; label_callback1: - TIME_CHECK_CALLBACK(kTime2020500, params->param2, 2, setup_compartment8to6); + if (Entity::timeCheckCallback(kTime2020500, params->param2, 2, WRAP_SETUP_FUNCTION(Hadija, setup_compartment8to6))) + break; label_callback2: - TIME_CHECK_CALLBACK(kTime2079000, params->param3, 3, setup_compartment6to8); + if (Entity::timeCheckCallback(kTime2079000, params->param3, 3, WRAP_SETUP_FUNCTION(Hadija, setup_compartment6to8))) + break; label_callback3: - TIME_CHECK_CALLBACK(kTime2187000, params->param4, 4, setup_compartment8to6); + if (Entity::timeCheckCallback(kTime2187000, params->param4, 4, WRAP_SETUP_FUNCTION(Hadija, setup_compartment8to6))) + break; label_callback4: - if (params->param5 != kTimeInvalid && getState()->time > kTime2196000) - TIME_CHECK_CAR(kTime2254500, params->param5, 5, setup_compartment6); + if (params->param5 != kTimeInvalid && getState()->time > kTime2196000) { + if (Entity::timeCheckCar(kTime2254500, params->param5, 5, WRAP_SETUP_FUNCTION(Hadija, setup_compartment6))) + break; + } break; case kActionDefault: @@ -387,18 +393,24 @@ IMPLEMENT_FUNCTION(18, Hadija, chapter4Handler) break; case kActionNone: - if (params->param1 != kTimeInvalid) - TIME_CHECK_CAR(kTime1714500, params->param1, 1, setup_compartment6); + if (params->param1 != kTimeInvalid) { + if (Entity::timeCheckCar(kTime1714500, params->param1, 1, WRAP_SETUP_FUNCTION(Hadija, setup_compartment6))) + break; + } label_callback1: - TIME_CHECK_CALLBACK(kTime2367000, params->param2, 2, setup_compartment6to8); + if (Entity::timeCheckCallback(kTime2367000, params->param2, 2, WRAP_SETUP_FUNCTION(Hadija, setup_compartment6to8))) + break; label_callback2: - TIME_CHECK_CALLBACK(kTime2421000, params->param3, 3, setup_compartment8to6); + if (Entity::timeCheckCallback(kTime2421000, params->param3, 3, WRAP_SETUP_FUNCTION(Hadija, setup_compartment8to6))) + break; label_callback3: - if (params->param4 != kTimeInvalid && getState()->time > kTime2425500) - TIME_CHECK_CAR(kTime2484000, params->param4, 4, setup_compartment6); + if (params->param4 != kTimeInvalid && getState()->time > kTime2425500) { + if (Entity::timeCheckCar(kTime2484000, params->param4, 4, WRAP_SETUP_FUNCTION(Hadija, setup_compartment6))) + break; + } break; case kActionCallback: @@ -468,7 +480,9 @@ IMPLEMENT_FUNCTION(22, Hadija, function22) break; case kActionNone: - UPDATE_PARAM(params->param1, getState()->time, 2700); + if (!Entity::updateParameter(params->param1, getState()->time, 2700)) + break; + setup_function23(); break; diff --git a/engines/lastexpress/entities/hadija.h b/engines/lastexpress/entities/hadija.h index d2495955e0..545f21ee03 100644 --- a/engines/lastexpress/entities/hadija.h +++ b/engines/lastexpress/entities/hadija.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_HADIJA_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/ivo.cpp b/engines/lastexpress/entities/ivo.cpp index f2261b438c..c53f4689fb 100644 --- a/engines/lastexpress/entities/ivo.cpp +++ b/engines/lastexpress/entities/ivo.cpp @@ -32,10 +32,7 @@ #include "lastexpress/game/scenes.h" #include "lastexpress/game/state.h" -#include "lastexpress/sound/sound.h" - #include "lastexpress/lastexpress.h" -#include "lastexpress/helpers.h" namespace LastExpress { @@ -184,7 +181,7 @@ IMPLEMENT_FUNCTION(11, Ivo, function11) getData()->location = kLocationInsideCompartment; getEntities()->clearSequences(kEntityIvo); - CALLBACK_ACTION(); + callbackAction(); break; case 4: @@ -193,7 +190,7 @@ IMPLEMENT_FUNCTION(11, Ivo, function11) getData()->location = kLocationInsideCompartment; getEntities()->clearSequences(kEntityIvo); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -210,7 +207,7 @@ IMPLEMENT_FUNCTION(12, Ivo, sitAtTableWithSalko) getEntities()->clearSequences(kEntitySalko); getSavePoints()->push(kEntityIvo, kEntityTables2, kAction136455232); - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: @@ -231,7 +228,7 @@ IMPLEMENT_FUNCTION(13, Ivo, leaveTableWithSalko) getSavePoints()->push(kEntityIvo, kEntityTables2, kActionDrawTablesWithChairs, "009E"); getEntities()->clearSequences(kEntitySalko); - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: @@ -249,7 +246,7 @@ IMPLEMENT_FUNCTION(14, Ivo, chapter1) break; case kActionNone: - TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler); + Entity::timeCheck(kTimeChapter1, params->param1, WRAP_SETUP_FUNCTION(Ivo, setup_chapter1Handler)); break; case kActionDefault: @@ -374,7 +371,7 @@ IMPLEMENT_FUNCTION(18, Ivo, chapter2) break; case kActionNone: - TIME_CHECK(kTime1777500, params->param1, setup_function19); + Entity::timeCheck(kTime1777500, params->param1, WRAP_SETUP_FUNCTION(Ivo, setup_function19)); break; case kActionDefault: diff --git a/engines/lastexpress/entities/ivo.h b/engines/lastexpress/entities/ivo.h index cd5cb7b6be..75814336e0 100644 --- a/engines/lastexpress/entities/ivo.h +++ b/engines/lastexpress/entities/ivo.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_IVO_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/kahina.cpp b/engines/lastexpress/entities/kahina.cpp index 2918b1e8bd..7860836a26 100644 --- a/engines/lastexpress/entities/kahina.cpp +++ b/engines/lastexpress/entities/kahina.cpp @@ -32,10 +32,8 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" #include "lastexpress/lastexpress.h" -#include "lastexpress/helpers.h" namespace LastExpress { @@ -90,7 +88,7 @@ IMPLEMENT_FUNCTION_END IMPLEMENT_FUNCTION_I(4, Kahina, updateFromTime, uint32) if (savepoint.action == kAction137503360) { ENTITY_PARAM(0, 2) = 1; - CALLBACK_ACTION(); + callbackAction(); } Entity::updateFromTime(savepoint); @@ -111,7 +109,7 @@ IMPLEMENT_FUNCTION_I(6, Kahina, function6, TimeValue) if (params->param1 < getState()->time && !params->param2) { params->param2 = 1; - CALLBACK_ACTION(); + callbackAction(); break; } @@ -141,7 +139,7 @@ IMPLEMENT_FUNCTION_I(6, Kahina, function6, TimeValue) case 1: if (ENTITY_PARAM(0, 1) || ENTITY_PARAM(0, 2)) { - CALLBACK_ACTION(); + callbackAction(); break; } @@ -151,7 +149,7 @@ IMPLEMENT_FUNCTION_I(6, Kahina, function6, TimeValue) case 2: case 3: if (ENTITY_PARAM(0, 1) || ENTITY_PARAM(0, 2)) { - CALLBACK_ACTION(); + callbackAction(); break; } @@ -163,7 +161,7 @@ IMPLEMENT_FUNCTION_I(6, Kahina, function6, TimeValue) case 4: if (ENTITY_PARAM(0, 2)) { - CALLBACK_ACTION(); + callbackAction(); break; } @@ -173,7 +171,7 @@ IMPLEMENT_FUNCTION_I(6, Kahina, function6, TimeValue) case 5: if (ENTITY_PARAM(0, 1) || ENTITY_PARAM(0, 2)) { - CALLBACK_ACTION(); + callbackAction(); break; } @@ -185,7 +183,7 @@ IMPLEMENT_FUNCTION_I(6, Kahina, function6, TimeValue) case kAction137503360: ENTITY_PARAM(0, 2) = 1; - CALLBACK_ACTION(); + callbackAction(); break; } IMPLEMENT_FUNCTION_END @@ -198,12 +196,12 @@ IMPLEMENT_FUNCTION_II(7, Kahina, updateEntity2, CarIndex, EntityPosition) case kActionNone: if (getEntities()->updateEntity(_entityIndex, (CarIndex)params->param1, (EntityPosition)params->param2)) - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: if (getEntities()->updateEntity(_entityIndex, (CarIndex)params->param1, (EntityPosition)params->param2)) { - CALLBACK_ACTION(); + callbackAction(); } else if (getEntities()->isDistanceBetweenEntities(kEntityKahina, kEntityPlayer, 1000) && !getEntities()->isInGreenCarEntrance(kEntityPlayer) && !getEntities()->isInsideCompartments(kEntityPlayer) @@ -211,14 +209,14 @@ IMPLEMENT_FUNCTION_II(7, Kahina, updateEntity2, CarIndex, EntityPosition) if (getData()->car == kCarGreenSleeping || getData()->car == kCarRedSleeping) { ENTITY_PARAM(0, 1) = 1; - CALLBACK_ACTION(); + callbackAction(); } } break; case kAction137503360: ENTITY_PARAM(0, 2) = 1; - CALLBACK_ACTION(); + callbackAction(); break; } IMPLEMENT_FUNCTION_END @@ -249,7 +247,7 @@ IMPLEMENT_FUNCTION(10, Kahina, chapter1) break; case kActionNone: - TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler); + Entity::timeCheck(kTimeChapter1, params->param1, WRAP_SETUP_FUNCTION(Kahina, setup_chapter1Handler)); break; case kActionDefault: @@ -269,7 +267,7 @@ IMPLEMENT_FUNCTION(11, Kahina, chapter1Handler) return; if (getProgress().jacket != kJacketOriginal) - TIME_CHECK_SAVEPOINT(kTime1107000, params->param1, kEntityKahina, kEntityMertens, kAction238732837); + Entity::timeCheckSavepoint(kTime1107000, params->param1, kEntityKahina, kEntityMertens, kAction238732837); if (getProgress().eventMertensKronosInvitation) setup_function12(); @@ -282,7 +280,7 @@ IMPLEMENT_FUNCTION(12, Kahina, function12) break; case kActionNone: - TIME_CHECK(kTime1485000, params->param2, setup_function13); + Entity::timeCheck(kTime1485000, params->param2, WRAP_SETUP_FUNCTION(Kahina, setup_function13)); break; case kActionKnock: @@ -372,12 +370,12 @@ IMPLEMENT_FUNCTION(14, Kahina, function14) case kActionExitCompartment: getEntities()->exitCompartment(kEntityKahina, kObjectCompartmentF); - CALLBACK_ACTION(); + callbackAction(); break; case kAction4: getEntities()->exitCompartment(kEntityKahina, kObjectCompartmentF); - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: @@ -396,10 +394,10 @@ IMPLEMENT_FUNCTION(15, Kahina, function15) case kActionNone: if (params->param2 != kTimeInvalid) { - UPDATE_PARAM_PROC_TIME(params->param1, !getEntities()->isPlayerInCar(kCarRedSleeping), params->param2, 0) + if (Entity::updateParameterTime((TimeValue)params->param1, !getEntities()->isPlayerInCar(kCarRedSleeping), params->param2, 0)) { setCallback(9); setup_updateEntity(kCarRedSleeping, kPosition_4070); - UPDATE_PARAM_PROC_END + } } break; @@ -542,7 +540,7 @@ IMPLEMENT_FUNCTION(15, Kahina, function15) case 17: getEntities()->clearSequences(kEntityKahina); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -582,18 +580,18 @@ IMPLEMENT_FUNCTION(17, Kahina, chapter2Handler) case kActionNone: if (params->param1) { - UPDATE_PARAM_PROC(params->param2, getState()->time, 9000) + if (Entity::updateParameter(params->param2, getState()->time, 9000)) { params->param1 = 1; params->param2 = 0; - UPDATE_PARAM_PROC_END + } } if (getEvent(kEventKahinaAskSpeakFirebird) && getEvent(kEventKronosConversationFirebird) && getEntities()->isInsideTrainCar(kEntityPlayer, kCarKronos)) { - UPDATE_PARAM_PROC(params->param3, getState()->time, 900) + if (Entity::updateParameter(params->param3, getState()->time, 900)) { setCallback(1); setup_savegame(kSavegameTypeEvent, kEventKronosConversationFirebird); break; - UPDATE_PARAM_PROC_END + } } label_callback_3: @@ -729,7 +727,7 @@ IMPLEMENT_FUNCTION_II(19, Kahina, function19, CarIndex, EntityPosition) RESET_ENTITY_STATE(kEntityKahina, Kahina, setup_function22); if (getEntities()->updateEntity(kEntityKahina, (CarIndex)params->param1, (EntityPosition)params->param2)) - CALLBACK_ACTION(); + callbackAction(); break; case kActionExcuseMeCath: @@ -745,7 +743,7 @@ IMPLEMENT_FUNCTION_II(19, Kahina, function19, CarIndex, EntityPosition) case kActionDefault: if (getEntities()->updateEntity(kEntityKahina, (CarIndex)params->param1, (EntityPosition)params->param2)) - CALLBACK_ACTION(); + callbackAction(); break; } IMPLEMENT_FUNCTION_END @@ -787,16 +785,17 @@ label_callback_2: } if (!params->param1) { - UPDATE_PARAM_PROC(params->param3, getState()->time, 9000) + if (Entity::updateParameter(params->param3, getState()->time, 9000)) { params->param1 = 1; params->param3 = 0; - UPDATE_PARAM_PROC_END + } } if (getEvent(kEventKahinaAskSpeakFirebird) && !getEvent(kEventKronosConversationFirebird) && getEntities()->isInsideTrainCar(kEntityPlayer, kCarKronos)) { - UPDATE_PARAM(params->param4, getState()->time, 900); + if (!Entity::updateParameter(params->param4, getState()->time, 900)) + break; setCallback(3); setup_savegame(kSavegameTypeEvent, kEventKronosConversationFirebird); @@ -928,11 +927,11 @@ IMPLEMENT_FUNCTION(21, Kahina, function21) params->param3 = (uint)getState()->time + 4500; if (params->param6 != kTimeInvalid) { - UPDATE_PARAM_PROC_TIME(params->param3, (getEntities()->isPlayerPosition(kCarKronos, 80) || getEntities()->isPlayerPosition(kCarKronos, 88)), params->param5, 0) + if (Entity::updateParameterTime((TimeValue)params->param3, (getEntities()->isPlayerPosition(kCarKronos, 80) || getEntities()->isPlayerPosition(kCarKronos, 88)), params->param5, 0)) { setCallback(2); setup_function23(); break; - UPDATE_PARAM_PROC_END + } } } @@ -943,14 +942,14 @@ label_callback_2: params->param4 = (uint)getState()->time + 4500; if (params->param6 != kTimeInvalid) { - UPDATE_PARAM_PROC_TIME(params->param3, (getEntities()->isPlayerPosition(kCarKronos, 80) || getEntities()->isPlayerPosition(kCarKronos, 88)), params->param6, 0) + if (Entity::updateParameterTime((TimeValue)params->param3, (getEntities()->isPlayerPosition(kCarKronos, 80) || getEntities()->isPlayerPosition(kCarKronos, 88)), params->param6, 0)) { getSound()->playSound(kEntityPlayer, "LIB014", getSound()->getSoundFlag(kEntityKahina)); getSound()->playSound(kEntityPlayer, "LIB015", getSound()->getSoundFlag(kEntityKahina)); getEntities()->drawSequenceLeft(kEntityKahina, "202a"); params->param2 = 0; - UPDATE_PARAM_PROC_END + } } } @@ -1125,7 +1124,7 @@ IMPLEMENT_FUNCTION(23, Kahina, function23) case 7: getEntities()->clearSequences(kEntityKahina); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1262,7 +1261,7 @@ IMPLEMENT_FUNCTION(25, Kahina, function25) getProgress().field_78 = 1; ENTITY_PARAM(0, 3) = 0; - CALLBACK_ACTION(); + callbackAction(); break; case kActionCallback: @@ -1303,7 +1302,7 @@ IMPLEMENT_FUNCTION(25, Kahina, function25) case 13: getEntities()->clearSequences(kEntityKahina); - CALLBACK_ACTION(); + callbackAction(); break; case 6: @@ -1387,7 +1386,7 @@ IMPLEMENT_FUNCTION(26, Kahina, function26) getInventory()->setLocationAndProcess(kItemBriefcase, kObjectLocation2); getProgress().field_78 = 1; - CALLBACK_ACTION(); + callbackAction(); break; case kActionCallback: @@ -1429,7 +1428,7 @@ IMPLEMENT_FUNCTION(26, Kahina, function26) case 9: getEntities()->clearSequences(kEntityKahina); - CALLBACK_ACTION(); + callbackAction(); break; case 6: diff --git a/engines/lastexpress/entities/kahina.h b/engines/lastexpress/entities/kahina.h index b25053e339..7479cf76f9 100644 --- a/engines/lastexpress/entities/kahina.h +++ b/engines/lastexpress/entities/kahina.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_KAHINA_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/kronos.cpp b/engines/lastexpress/entities/kronos.cpp index 134dce9c81..26ce3ca424 100644 --- a/engines/lastexpress/entities/kronos.cpp +++ b/engines/lastexpress/entities/kronos.cpp @@ -39,10 +39,8 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" #include "lastexpress/lastexpress.h" -#include "lastexpress/helpers.h" namespace LastExpress { @@ -128,7 +126,7 @@ IMPLEMENT_FUNCTION(7, Kronos, chapter1) break; case kActionNone: - TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler); + Entity::timeCheck(kTimeChapter1, params->param1, WRAP_SETUP_FUNCTION(Kronos, setup_chapter1Handler)); break; case kActionDefault: @@ -149,7 +147,7 @@ IMPLEMENT_FUNCTION(8, Kronos, chapter1Handler) break; case kActionNone: - TIME_CHECK(kTime1489500, params->param2, setup_function11); + Entity::timeCheck(kTime1489500, params->param2, WRAP_SETUP_FUNCTION(Kronos, setup_function11)); break; case kAction171849314: @@ -191,7 +189,7 @@ IMPLEMENT_FUNCTION(10, Kronos, function10) break; case kActionNone: - TIME_CHECK(kTime1489500, params->param1, setup_function11); + Entity::timeCheck(kTime1489500, params->param1, WRAP_SETUP_FUNCTION(Kronos, setup_function11)); break; case kActionDefault: @@ -295,10 +293,10 @@ IMPLEMENT_FUNCTION(15, Kronos, function15) case kActionNone: if (params->param1 && !getEntities()->isInSalon(kEntityBoutarel)) { - UPDATE_PARAM_PROC(params->param2, getState()->timeTicks, 75) + if (Entity::updateParameter(params->param2, getState()->timeTicks, 75)) { setup_function16(); break; - UPDATE_PARAM_PROC_END + } } if (params->param3 != kTimeInvalid && getState()->time > kTime2002500) { @@ -407,8 +405,7 @@ IMPLEMENT_FUNCTION(18, Kronos, function18) params->param2 = 1; } - TIME_CHECK(kTime2106000, params->param3, setup_function19) - else { + if (!Entity::timeCheck(kTime2106000, params->param3, WRAP_SETUP_FUNCTION(Kronos, setup_function19))) { if (params->param1 && getEntities()->isInKronosSanctum(kEntityPlayer)) { setCallback(1); setup_savegame(kSavegameTypeEvent, kEventKahinaPunchSuite4); @@ -528,9 +525,9 @@ IMPLEMENT_FUNCTION(20, Kronos, function20) } if (CURRENT_PARAM(1, 2) != kTimeInvalid && params->param7 < getState()->time) { - UPDATE_PARAM_PROC_TIME(params->param8, !params->param1, CURRENT_PARAM(1, 2), 450) + if (Entity::updateParameterTime((TimeValue)params->param8, !params->param1, CURRENT_PARAM(1, 2), 450)) { getSavePoints()->push(kEntityKronos, kEntityKahina, kAction237555748); - UPDATE_PARAM_PROC_END + } } if (!params->param1) diff --git a/engines/lastexpress/entities/kronos.h b/engines/lastexpress/entities/kronos.h index 4c61b98620..a7693fcd46 100644 --- a/engines/lastexpress/entities/kronos.h +++ b/engines/lastexpress/entities/kronos.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_KRONOS_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/mahmud.cpp b/engines/lastexpress/entities/mahmud.cpp index 0e67b45cd2..af86ef8cdd 100644 --- a/engines/lastexpress/entities/mahmud.cpp +++ b/engines/lastexpress/entities/mahmud.cpp @@ -34,10 +34,8 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" #include "lastexpress/lastexpress.h" -#include "lastexpress/helpers.h" namespace LastExpress { @@ -86,7 +84,8 @@ IMPLEMENT_FUNCTION_SIII(4, Mahmud, enterExitCompartment2, ObjectIndex, uint32, O break; case kActionNone: - UPDATE_PARAM(params->param7, getState()->timeTicks, params->param5); + if (!Entity::updateParameter(params->param7, getState()->timeTicks, params->param5)) + break; if (!getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingUp)) getScenes()->loadSceneFromObject((ObjectIndex)params->param6, true); @@ -95,7 +94,7 @@ IMPLEMENT_FUNCTION_SIII(4, Mahmud, enterExitCompartment2, ObjectIndex, uint32, O case kActionExitCompartment: getEntities()->exitCompartment(kEntityMahmud, (ObjectIndex)params->param4); - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: @@ -146,7 +145,8 @@ IMPLEMENT_FUNCTION_II(10, Mahmud, function10, ObjectIndex, bool) break; case kActionNone: - UPDATE_PARAM(params->param6, getState()->time, 13500); + if (!Entity::updateParameter(params->param6, getState()->time, 13500)) + break; getObjects()->update(kObjectCompartment5, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand); getObjects()->update(kObjectCompartment6, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand); @@ -267,7 +267,7 @@ IMPLEMENT_FUNCTION_II(10, Mahmud, function10, ObjectIndex, bool) getData()->location = kLocationInsideCompartment; getEntities()->clearSequences(kEntityMahmud); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -392,7 +392,7 @@ IMPLEMENT_FUNCTION(11, Mahmud, function11) getEntities()->clearSequences(kEntityMahmud); getObjects()->update(kObjectCompartment4, kEntityMahmud, kObjectLocation3, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -471,7 +471,7 @@ IMPLEMENT_FUNCTION(12, Mahmud, function12) getData()->location = kLocationInsideCompartment; getEntities()->clearSequences(kEntityMahmud); - CALLBACK_ACTION(); + callbackAction(); break; } @@ -537,7 +537,7 @@ IMPLEMENT_FUNCTION(13, Mahmud, function13) getData()->location = kLocationInsideCompartment; getEntities()->clearSequences(kEntityMahmud); - CALLBACK_ACTION(); + callbackAction(); break; } @@ -560,7 +560,8 @@ IMPLEMENT_FUNCTION(14, Mahmud, chaptersHandler) if (!params->param2 && getProgress().chapter == kChapter1) { - TIME_CHECK_CALLBACK(kTime1098000, params->param6, 1, setup_function13); + if (Entity::timeCheckCallback(kTime1098000, params->param6, 1, WRAP_SETUP_FUNCTION(Mahmud, setup_function13))) + break; if (!getSoundQueue()->isBuffered("HAR1104") && getState()->time > kTime1167300 && !params->param7) { params->param7 = 1; @@ -572,7 +573,8 @@ IMPLEMENT_FUNCTION(14, Mahmud, chaptersHandler) } if (params->param5) { - UPDATE_PARAM(params->param8, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param8, getState()->timeTicks, 75)) + break; params->param4 = 1; params->param5 = 0; @@ -732,7 +734,7 @@ IMPLEMENT_FUNCTION(15, Mahmud, chapter1) break; case kActionNone: - TIME_CHECK(kTimeChapter1, params->param1, setup_chaptersHandler); + Entity::timeCheck(kTimeChapter1, params->param1, WRAP_SETUP_FUNCTION(Mahmud, setup_chaptersHandler)); break; case kActionDefault: diff --git a/engines/lastexpress/entities/mahmud.h b/engines/lastexpress/entities/mahmud.h index 5feb95cba5..685100f078 100644 --- a/engines/lastexpress/entities/mahmud.h +++ b/engines/lastexpress/entities/mahmud.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_MAHMUD_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/max.cpp b/engines/lastexpress/entities/max.cpp index eacc38bf60..abd2aae9e4 100644 --- a/engines/lastexpress/entities/max.cpp +++ b/engines/lastexpress/entities/max.cpp @@ -31,10 +31,8 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" #include "lastexpress/lastexpress.h" -#include "lastexpress/helpers.h" namespace LastExpress { @@ -91,7 +89,8 @@ IMPLEMENT_FUNCTION(6, Max, chapter12_handler) break; case kActionNone: - UPDATE_PARAM(params->param2, getState()->time, params->param1); + if (!Entity::updateParameter(params->param2, getState()->time, params->param1)) + break; if (!getSoundQueue()->isBuffered(kEntityMax)) getSound()->playSound(kEntityMax, "Max1122"); @@ -125,7 +124,8 @@ IMPLEMENT_FUNCTION(7, Max, function7) break; case kActionNone: - UPDATE_PARAM(params->param2, getState()->time, params->param1) + if (!Entity::updateParameter(params->param2, getState()->time, params->param1)) + break; if (!getSoundQueue()->isBuffered(kEntityMax)) getSound()->playSound(kEntityMax, "Max1122"); @@ -186,7 +186,7 @@ IMPLEMENT_FUNCTION(7, Max, function7) case kAction101687594: getEntities()->clearSequences(kEntityMax); - CALLBACK_ACTION(); + callbackAction(); break; case kAction122358304: @@ -195,7 +195,7 @@ IMPLEMENT_FUNCTION(7, Max, function7) getObjects()->update(kObjectCompartmentF, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand); getObjects()->update(kObject53, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); break; case kAction158007856: @@ -214,7 +214,8 @@ IMPLEMENT_FUNCTION(8, Max, chapter4Handler) break; case kActionNone: - UPDATE_PARAM(params->param3, getState()->time, params->param2); + if (!Entity::updateParameter(params->param3, getState()->time, params->param2)) + break; if (!getSoundQueue()->isBuffered(kEntityMax)) getSound()->playSound(kEntityMax, "Max3101"); @@ -323,7 +324,7 @@ IMPLEMENT_FUNCTION(10, Max, chapter1) break; case kActionNone: - TIME_CHECK(kTimeChapter1, params->param1, setup_chapter12_handler); + Entity::timeCheck(kTimeChapter1, params->param1, WRAP_SETUP_FUNCTION(Max, setup_chapter12_handler)); break; case kActionDefault: @@ -392,7 +393,8 @@ IMPLEMENT_FUNCTION(13, Max, chapter3Handler) break; } - UPDATE_PARAM(params->param3, getState()->time, params->param1); + if (!Entity::updateParameter(params->param3, getState()->time, params->param1)) + break; if (!getSoundQueue()->isBuffered(kEntityMax)) getSound()->playSound(kEntityMax, "Max1122"); @@ -514,7 +516,8 @@ IMPLEMENT_FUNCTION(15, Max, function15) } if (!params->param1) { - UPDATE_PARAM(params->param3, getState()->time, 900); + if (!Entity::updateParameter(params->param3, getState()->time, 900)) + break; getSavePoints()->push(kEntityMax, kEntityCoudert, kAction157026693); } diff --git a/engines/lastexpress/entities/max.h b/engines/lastexpress/entities/max.h index 17b58475aa..acd8235e89 100644 --- a/engines/lastexpress/entities/max.h +++ b/engines/lastexpress/entities/max.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_MAX_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/mertens.cpp b/engines/lastexpress/entities/mertens.cpp index d88962fce2..97dd293793 100644 --- a/engines/lastexpress/entities/mertens.cpp +++ b/engines/lastexpress/entities/mertens.cpp @@ -32,21 +32,11 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" #include "lastexpress/lastexpress.h" -#include "lastexpress/helpers.h" namespace LastExpress { -#define SAVEGAME_BLOOD_JACKET() \ - if (getProgress().jacket == kJacketBlood \ - && getEntities()->isDistanceBetweenEntities(kEntityMertens, kEntityPlayer, 1000) \ - && !getEntities()->isInsideCompartments(kEntityPlayer) \ - && !getEntities()->checkFields10(kEntityPlayer)) { \ - setCallback(1); \ - setup_savegame(kSavegameTypeEvent, kEventMertensBloodJacket); \ - } Mertens::Mertens(LastExpressEngine *engine) : Entity(engine, kEntityMertens) { ADD_CALLBACK_FUNCTION(Mertens, reset); @@ -117,11 +107,11 @@ IMPLEMENT_FUNCTION_S(2, Mertens, bloodJacket) break; case kActionNone: - SAVEGAME_BLOOD_JACKET(); + Entity::savegameBloodJacket(); break; case kActionExitCompartment: - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: @@ -144,7 +134,7 @@ IMPLEMENT_FUNCTION_SI(3, Mertens, enterExitCompartment, ObjectIndex) break; case kActionNone: - SAVEGAME_BLOOD_JACKET(); + Entity::savegameBloodJacket(); return; case kActionCallback: @@ -165,12 +155,12 @@ IMPLEMENT_FUNCTION_SI(4, Mertens, enterExitCompartment2, ObjectIndex) break; case kActionNone: - SAVEGAME_BLOOD_JACKET(); + Entity::savegameBloodJacket(); return; case kAction4: getEntities()->exitCompartment(kEntityMertens, (ObjectIndex)params->param4); - CALLBACK_ACTION(); + callbackAction(); return; case kActionCallback: @@ -191,13 +181,13 @@ IMPLEMENT_FUNCTION_SIII(5, Mertens, enterExitCompartment3, ObjectIndex, EntityPo break; case kActionNone: - SAVEGAME_BLOOD_JACKET(); + Entity::savegameBloodJacket(); break; case kActionExitCompartment: getEntities()->exitCompartment(_entityIndex, (ObjectIndex)params->param4); getData()->entityPosition = (EntityPosition)params->param5; - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: @@ -229,15 +219,15 @@ IMPLEMENT_FUNCTION(6, Mertens, callbackActionOnDirection) case kActionNone: if (getData()->direction != kDirectionRight) { - CALLBACK_ACTION(); + callbackAction(); break; } - SAVEGAME_BLOOD_JACKET(); + Entity::savegameBloodJacket(); break; case kActionExitCompartment: - CALLBACK_ACTION(); + callbackAction(); break; case kActionCallback: @@ -256,11 +246,11 @@ IMPLEMENT_FUNCTION_S(7, Mertens, playSound) break; case kActionNone: - SAVEGAME_BLOOD_JACKET(); + Entity::savegameBloodJacket(); break; case kActionEndSound: - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: @@ -283,11 +273,11 @@ IMPLEMENT_FUNCTION_S(8, Mertens, playSound16) break; case kActionNone: - SAVEGAME_BLOOD_JACKET(); + Entity::savegameBloodJacket(); break; case kActionEndSound: - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: @@ -310,14 +300,6 @@ IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// IMPLEMENT_FUNCTION_II(10, Mertens, updateEntity, CarIndex, EntityPosition) - -#define LOADSCENE_FROM_POSITION() \ - if (getData()->direction != kDirectionUp) { \ - getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + 750)); \ - } else { \ - getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition - 750), true); \ - } - switch (savepoint.action) { default: break; @@ -333,7 +315,7 @@ IMPLEMENT_FUNCTION_II(10, Mertens, updateEntity, CarIndex, EntityPosition) || getEntities()->checkFields10(kEntityPlayer)) { if (getEntities()->updateEntity(kEntityMertens, (CarIndex)params->param1, (EntityPosition)params->param2)) { getData()->inventoryItem = kItemNone; - CALLBACK_ACTION(); + callbackAction(); } break; } @@ -364,7 +346,7 @@ IMPLEMENT_FUNCTION_II(10, Mertens, updateEntity, CarIndex, EntityPosition) if (getEntities()->updateEntity(kEntityMertens, (CarIndex)params->param1, (EntityPosition)params->param2)) { getData()->inventoryItem = kItemNone; - CALLBACK_ACTION(); + callbackAction(); } break; @@ -395,7 +377,7 @@ IMPLEMENT_FUNCTION_II(10, Mertens, updateEntity, CarIndex, EntityPosition) params->param3 = 1; if (getEntities()->updateEntity(kEntityMertens, (CarIndex)params->param1, (EntityPosition)params->param2)) - CALLBACK_ACTION(); + callbackAction(); break; case kActionCallback: @@ -416,7 +398,7 @@ IMPLEMENT_FUNCTION_II(10, Mertens, updateEntity, CarIndex, EntityPosition) ENTITY_PARAM(0, 7) = 0; if (params->param1 != 3 || (params->param2 != kPosition_8200 && params->param2 != kPosition_9510)) { - LOADSCENE_FROM_POSITION(); + loadSceneFromPosition(); break; } @@ -428,7 +410,7 @@ IMPLEMENT_FUNCTION_II(10, Mertens, updateEntity, CarIndex, EntityPosition) getEntities()->updateEntity(kEntityMertens, kCarGreenSleeping, kPosition_2000); getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + 750)); - CALLBACK_ACTION(); + callbackAction(); break; case 3: @@ -444,35 +426,33 @@ IMPLEMENT_FUNCTION_II(10, Mertens, updateEntity, CarIndex, EntityPosition) getEntities()->updateEntity(kEntityMertens, kCarGreenSleeping, kPosition_2000); getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + 750)); - CALLBACK_ACTION(); + callbackAction(); break; } - LOADSCENE_FROM_POSITION(); + loadSceneFromPosition(); break; case 4: getAction()->playAnimation(kEventMertensKronosConcertInvitation); ENTITY_PARAM(2, 4) = 0; - LOADSCENE_FROM_POSITION(); + loadSceneFromPosition(); break; case 5: getAction()->playAnimation(getData()->entityPosition < getEntityData(kEntityPlayer)->entityPosition ? kEventMertensAskTylerCompartmentD : kEventMertensAskTylerCompartment); - LOADSCENE_FROM_POSITION(); + loadSceneFromPosition(); break; case 6: getAction()->playAnimation(kEventMertensDontMakeBed); - LOADSCENE_FROM_POSITION(); + loadSceneFromPosition(); ENTITY_PARAM(0, 4) = 0; break; } break; } - -#undef LOADSCENE_FROM_POSITION IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// @@ -482,11 +462,12 @@ IMPLEMENT_FUNCTION_I(11, Mertens, function11, uint32) break; case kActionNone: - SAVEGAME_BLOOD_JACKET(); + Entity::savegameBloodJacket(); - UPDATE_PARAM(params->param2, getState()->time, params->param1) + if (!Entity::updateParameter(params->param2, getState()->time, params->param1)) + break; - CALLBACK_ACTION(); + callbackAction(); break; case kActionCallback: @@ -506,7 +487,7 @@ IMPLEMENT_FUNCTION_I(12, Mertens, bonsoir, EntityIndex) return; if (getSoundQueue()->isBuffered(kEntityMertens)) { - CALLBACK_ACTION(); + callbackAction(); return; } @@ -540,7 +521,7 @@ IMPLEMENT_FUNCTION_I(12, Mertens, bonsoir, EntityIndex) getSound()->playSound(kEntityMertens, "CON1112G"); } - CALLBACK_ACTION(); + callbackAction(); IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// @@ -550,23 +531,23 @@ IMPLEMENT_FUNCTION_II(13, Mertens, function13, bool, bool) break; case kActionNone: - SAVEGAME_BLOOD_JACKET(); + Entity::savegameBloodJacket(); if (!params->param2 && !params->param3) { - UPDATE_PARAM_PROC(params->param4, getState()->timeTicks, 75) + if (Entity::updateParameter(params->param4, getState()->timeTicks, 75)) { getData()->inventoryItem = kItemNone; setCallback(5); setup_function18(); break; - UPDATE_PARAM_PROC_END + } } - UPDATE_PARAM_PROC(params->param5, getState()->timeTicks, 225) + if (Entity::updateParameter(params->param5, getState()->timeTicks, 225)) { getData()->inventoryItem = kItemNone; setCallback(6); setup_function18(); break; - UPDATE_PARAM_PROC_END + } getData()->inventoryItem = (getProgress().chapter == kChapter1 && !ENTITY_PARAM(2, 1) @@ -640,7 +621,7 @@ IMPLEMENT_FUNCTION_II(13, Mertens, function13, bool, bool) case 6: case 9: case 10: - CALLBACK_ACTION(); + callbackAction(); break; case 7: @@ -672,7 +653,7 @@ IMPLEMENT_FUNCTION_I(14, Mertens, function14, EntityIndex) break; case kActionNone: - SAVEGAME_BLOOD_JACKET(); + Entity::savegameBloodJacket(); break; case kActionDefault: @@ -719,7 +700,7 @@ IMPLEMENT_FUNCTION_I(14, Mertens, function14, EntityIndex) break; case 5: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -783,7 +764,7 @@ IMPLEMENT_FUNCTION_I(15, Mertens, function15, bool) break; case 6: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -860,7 +841,7 @@ IMPLEMENT_FUNCTION_I(16, Mertens, function16, bool) break; case 6: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -888,7 +869,7 @@ IMPLEMENT_FUNCTION(17, Mertens, function17) getScenes()->loadSceneFromItemPosition(kItem7); ENTITY_PARAM(2, 1) = 1; - CALLBACK_ACTION(); + callbackAction(); break; } @@ -926,7 +907,7 @@ IMPLEMENT_FUNCTION(17, Mertens, function17) break; case 2: - CALLBACK_ACTION(); + callbackAction(); break; case 3: @@ -944,7 +925,7 @@ IMPLEMENT_FUNCTION(17, Mertens, function17) getSavePoints()->push(kEntityMertens, kEntityMertens, kActionDrawScene); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -970,7 +951,7 @@ IMPLEMENT_FUNCTION(18, Mertens, function18) getInventory()->setLocationAndProcess(kItem7, kObjectLocation1); ENTITY_PARAM(2, 1) = 1; - CALLBACK_ACTION(); + callbackAction(); break; } @@ -978,7 +959,7 @@ IMPLEMENT_FUNCTION(18, Mertens, function18) getScenes()->loadSceneFromItemPosition(kItem7); ENTITY_PARAM(2, 1) = 1; - CALLBACK_ACTION(); + callbackAction(); break; } @@ -1009,7 +990,7 @@ IMPLEMENT_FUNCTION(18, Mertens, function18) ENTITY_PARAM(0, 1) = 0; getData()->inventoryItem = kItemNone; - CALLBACK_ACTION(); + callbackAction(); } break; } @@ -1025,7 +1006,7 @@ IMPLEMENT_FUNCTION(19, Mertens, function19) if (ENTITY_PARAM(2, 1)) { getInventory()->setLocationAndProcess(kItem7, kObjectLocation1); ENTITY_PARAM(2, 1) = 0; - CALLBACK_ACTION(); + callbackAction(); } else { setCallback(1); setup_bloodJacket("601C"); @@ -1039,7 +1020,7 @@ IMPLEMENT_FUNCTION(19, Mertens, function19) if (!getEntities()->isPlayerPosition(kCarGreenSleeping, 2)) getData()->entityPosition = kPosition_2088; - CALLBACK_ACTION(); + callbackAction(); } break; } @@ -1057,7 +1038,7 @@ IMPLEMENT_FUNCTION(20, Mertens, function20) if (ENTITY_PARAM(2, 1)) { ENTITY_PARAM(2, 1) = 0; - CALLBACK_ACTION(); + callbackAction(); } else { setCallback(1); setup_bloodJacket("601C"); @@ -1066,7 +1047,7 @@ IMPLEMENT_FUNCTION(20, Mertens, function20) case kActionCallback: if (getCallback() == 1) - CALLBACK_ACTION(); + callbackAction(); break; } IMPLEMENT_FUNCTION_END @@ -1078,11 +1059,12 @@ IMPLEMENT_FUNCTION_II(21, Mertens, function21, ObjectIndex, ObjectIndex) break; case kActionNone: - UPDATE_PARAM_PROC(CURRENT_PARAM(1, 4), getState()->time, 300) + if (Entity::updateParameter(CURRENT_PARAM(1, 4), getState()->time, 300)) { getSound()->playSound(kEntityPlayer, "ZFX1004", getSound()->getSoundFlag(kEntityMertens)); - UPDATE_PARAM_PROC_END + } - UPDATE_PARAM(CURRENT_PARAM(1, 5), getState()->time, 900); + if (!Entity::updateParameter(CURRENT_PARAM(1, 5), getState()->time, 900)) + break; // Update objects getObjects()->updateLocation2((ObjectIndex)params->param1, kObjectLocation1); @@ -1092,7 +1074,7 @@ IMPLEMENT_FUNCTION_II(21, Mertens, function21, ObjectIndex, ObjectIndex) if (params->param2) getObjects()->update((ObjectIndex)params->param2, (EntityIndex)params->param8, (ObjectLocation)CURRENT_PARAM(1, 1), (CursorStyle)CURRENT_PARAM(1, 2), (CursorStyle)CURRENT_PARAM(1, 3)); - CALLBACK_ACTION(); + callbackAction(); break; case kActionKnock: @@ -1222,7 +1204,7 @@ IMPLEMENT_FUNCTION(22, Mertens, function22) break; case 9: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1291,7 +1273,7 @@ IMPLEMENT_FUNCTION(23, Mertens, function23) case 6: getData()->location = kLocationOutsideCompartment; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1306,7 +1288,8 @@ IMPLEMENT_FUNCTION(24, Mertens, function24) case kActionNone: if (!params->param1) { - UPDATE_PARAM(params->param2, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param2, getState()->timeTicks, 75)) + break; setCallback(3); setup_enterExitCompartment3("601Rc", kObjectCompartment3, kPosition_6470, kPosition_6130); @@ -1351,7 +1334,7 @@ IMPLEMENT_FUNCTION(24, Mertens, function24) case 5: getData()->location = kLocationOutsideCompartment; - CALLBACK_ACTION(); + callbackAction(); break; case 6: @@ -1380,7 +1363,7 @@ IMPLEMENT_FUNCTION(24, Mertens, function24) break; case 9: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1409,7 +1392,8 @@ IMPLEMENT_FUNCTION(25, Mertens, function25) case kActionNone: if (!params->param1) { - UPDATE_PARAM(params->param2, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param2, getState()->timeTicks, 75)) + break; setCallback(3); setup_enterExitCompartment3("601Zb", kObjectCompartment2, kPosition_7500, kPositionNone); @@ -1458,7 +1442,7 @@ IMPLEMENT_FUNCTION(25, Mertens, function25) case 5: getData()->location = kLocationOutsideCompartment; - CALLBACK_ACTION(); + callbackAction(); break; case 6: @@ -1492,7 +1476,7 @@ IMPLEMENT_FUNCTION(25, Mertens, function25) break; case 9: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1547,7 +1531,7 @@ IMPLEMENT_FUNCTION_I(26, Mertens, function26, bool) case 2: getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); break; case 3: @@ -1613,7 +1597,7 @@ IMPLEMENT_FUNCTION_I(26, Mertens, function26, bool) getData()->location = kLocationOutsideCompartment; getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1628,17 +1612,17 @@ IMPLEMENT_FUNCTION_I(27, Mertens, tylerCompartment, MertensActionType) case kActionNone: if (getProgress().field_14 == 29) { - CALLBACK_ACTION(); + callbackAction(); break; } - UPDATE_PARAM_PROC(params->param2, getState()->timeTicks, 150) + if (Entity::updateParameter(params->param2, getState()->timeTicks, 150)) { getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorNormal, kCursorNormal); setCallback(10); setup_playSound16("CON1018A"); break; - UPDATE_PARAM_PROC_END + } label_callback10: if (!params->param3) @@ -1646,7 +1630,8 @@ label_callback10: if (params->param3 >= getState()->timeTicks) { label_callback11: - UPDATE_PARAM(params->param4, getState()->timeTicks, 375); + if (!Entity::updateParameter(params->param4, getState()->timeTicks, 375)) + break; getSound()->playSound(kEntityPlayer, "LIB033"); @@ -1680,7 +1665,7 @@ label_callback11: getSound()->playSound(kEntityPlayer, "LIB015"); getScenes()->loadScene(kScene41); - CALLBACK_ACTION(); + callbackAction(); break; } } else { @@ -1738,7 +1723,7 @@ label_callback11: getSound()->playSound(kEntityPlayer, "LIB015"); getScenes()->loadScene(kScene41); - CALLBACK_ACTION(); + callbackAction(); break; } } else { @@ -1763,7 +1748,7 @@ label_callback11: default: getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); break; case 1: @@ -1821,7 +1806,7 @@ label_callback11: getSound()->playSound(kEntityPlayer, "LIB015"); getScenes()->loadScene(kScene41); - CALLBACK_ACTION(); + callbackAction(); break; } } else { @@ -1930,7 +1915,7 @@ label_callback11: getData()->location = kLocationOutsideCompartment; getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); break; case 8: @@ -1957,7 +1942,7 @@ label_callback11: case 19: case 22: case 28: - CALLBACK_ACTION(); + callbackAction(); break; case 15: @@ -1969,7 +1954,7 @@ label_callback11: getSound()->playSound(kEntityPlayer, "LIB015"); getScenes()->loadScene(kScene41); - CALLBACK_ACTION(); + callbackAction(); break; case 16: @@ -1981,27 +1966,27 @@ label_callback11: getSound()->playSound(kEntityPlayer, "LIB015"); getScenes()->loadScene(kScene41); - CALLBACK_ACTION(); + callbackAction(); break; case 23: getProgress().eventMertensAugustWaiting = true; getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); break; case 24: getProgress().eventMertensKronosInvitation = true; getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); break; case 25: getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -2053,7 +2038,7 @@ IMPLEMENT_FUNCTION_S(28, Mertens, function28) break; case 4: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -2115,7 +2100,7 @@ IMPLEMENT_FUNCTION_SS(29, Mertens, function29) break; case 4: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -2140,14 +2125,14 @@ IMPLEMENT_FUNCTION_I(30, Mertens, function30, MertensActionType) case kActionDefault: switch (params->param1) { default: - CALLBACK_ACTION(); + callbackAction(); return; case 1: params->param2 = kPosition_8200; if (getProgress().field_14) { - CALLBACK_ACTION(); + callbackAction(); return; } @@ -2273,7 +2258,7 @@ IMPLEMENT_FUNCTION_I(30, Mertens, function30, MertensActionType) break; case 9: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -2312,7 +2297,7 @@ IMPLEMENT_FUNCTION_I(31, Mertens, function31, MertensActionType) case 2: case 3: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -2360,7 +2345,7 @@ IMPLEMENT_FUNCTION(32, Mertens, function32) break; case 5: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -2382,7 +2367,7 @@ IMPLEMENT_FUNCTION(33, Mertens, function33) setCallback(ENTITY_PARAM(0, 8) ? 1 : 3); setup_updateEntity(kCarGreenSleeping, ENTITY_PARAM(0, 8) ? kPosition_1500 : kPosition_540); } else { - CALLBACK_ACTION(); + callbackAction(); } break; @@ -2401,7 +2386,7 @@ IMPLEMENT_FUNCTION(33, Mertens, function33) case 2: ENTITY_PARAM(1, 8) = 0; - CALLBACK_ACTION(); + callbackAction(); break; case 3: @@ -2480,7 +2465,7 @@ IMPLEMENT_FUNCTION(33, Mertens, function33) break; } - CALLBACK_ACTION(); + callbackAction(); break; case 12: @@ -2493,13 +2478,13 @@ IMPLEMENT_FUNCTION(33, Mertens, function33) break; } - CALLBACK_ACTION(); + callbackAction(); break; case 13: ENTITY_PARAM(2, 2) = 0; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -2513,7 +2498,7 @@ IMPLEMENT_FUNCTION(34, Mertens, chapter1) break; case kActionNone: - TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler); + Entity::timeCheck(kTimeChapter1, params->param1, WRAP_SETUP_FUNCTION(Mertens, setup_chapter1Handler)); break; case kActionDefault: @@ -2548,7 +2533,7 @@ IMPLEMENT_FUNCTION(35, Mertens, function35) case kActionDefault: if (getProgress().field_14 == 29) { - CALLBACK_ACTION(); + callbackAction(); break; } else { getProgress().field_14 = 3; @@ -2589,7 +2574,7 @@ IMPLEMENT_FUNCTION(35, Mertens, function35) break; case 4: - CALLBACK_ACTION(); + callbackAction(); break; case 5: @@ -2611,7 +2596,7 @@ IMPLEMENT_FUNCTION(35, Mertens, function35) break; case 7: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -2626,7 +2611,7 @@ IMPLEMENT_FUNCTION(36, Mertens, function36) case kActionDefault: if (getProgress().field_14 == 29) { - CALLBACK_ACTION(); + callbackAction(); } else { getProgress().field_14 = 3; @@ -2712,7 +2697,7 @@ IMPLEMENT_FUNCTION(36, Mertens, function36) case 6: case 9: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -2767,7 +2752,7 @@ IMPLEMENT_FUNCTION(37, Mertens, function37) break; case 4: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -2791,12 +2776,12 @@ IMPLEMENT_FUNCTION(38, Mertens, function38) case kActionDefault: if (!ENTITY_PARAM(0, 4)) { - CALLBACK_ACTION(); + callbackAction(); break; } if (getProgress().field_14 == 29) { - CALLBACK_ACTION(); + callbackAction(); } else { setCallback(1); setup_updateEntity(kCarGreenSleeping, kPosition_8200); @@ -2810,7 +2795,7 @@ IMPLEMENT_FUNCTION(38, Mertens, function38) case 1: if (!ENTITY_PARAM(0, 4)) { - CALLBACK_ACTION(); + callbackAction(); break; } @@ -2820,7 +2805,7 @@ IMPLEMENT_FUNCTION(38, Mertens, function38) case 2: ENTITY_PARAM(0, 4) = 0; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -2894,7 +2879,7 @@ IMPLEMENT_FUNCTION(39, Mertens, function39) break; case 10: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -2940,7 +2925,7 @@ IMPLEMENT_FUNCTION(40, Mertens, function40) case 5: ENTITY_PARAM(0, 6) = 1; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -3013,7 +2998,7 @@ IMPLEMENT_FUNCTION(42, Mertens, function42) getData()->inventoryItem = kItemInvalid; if (!params->param2) { - TIME_CHECK_SAVEPOINT(kTime1125000, params->param3, kEntityMertens, kEntityMahmud, kAction170483072); + Entity::timeCheckSavepoint(kTime1125000, params->param3, kEntityMertens, kEntityMahmud, kAction170483072); if (params->param4 != kTimeInvalid && getState()->time > kTimeCityChalons) { @@ -3040,11 +3025,11 @@ IMPLEMENT_FUNCTION(42, Mertens, function42) label_callback_8: if (getState()->time > kTime1215000 && !ENTITY_PARAM(0, 1) && !ENTITY_PARAM(2, 1)) { - UPDATE_PARAM_PROC(params->param5, getState()->time, 2700) + if (Entity::updateParameter(params->param5, getState()->time, 2700)) { getEntities()->drawSequenceLeft(kEntityMertens, "601E"); ENTITY_PARAM(0, 1) = 1; params->param5 = 0; - UPDATE_PARAM_PROC_END + } } if (ENTITY_PARAM(0, 8)) { @@ -3547,19 +3532,23 @@ label_callback_5: } label_callback_6: - TIME_CHECK_CALLBACK_1(kTime1971000, params->param1, 7, setup_function28, "CON3012"); + if (Entity::timeCheckCallback(kTime1971000, params->param1, 7, "CON3012", WRAP_SETUP_FUNCTION_S(Mertens, setup_function28))) + break; label_callback_7: - TIME_CHECK_CALLBACK(kTime2117700, params->param2, 8, setup_function32); + if (Entity::timeCheckCallback(kTime2117700, params->param2, 8, WRAP_SETUP_FUNCTION(Mertens, setup_function32))) + break; label_callback_8: - TIME_CHECK_CALLBACK_1(kTime2124000, params->param3, 9, setup_function28, "CON2010"); + if (Entity::timeCheckCallback(kTime2124000, params->param3, 9, "CON2010", WRAP_SETUP_FUNCTION_S(Mertens, setup_function28))) + break; label_callback_9: - TIME_CHECK_CALLBACK(kTime2146500, params->param4, 10, setup_function32); + if (Entity::timeCheckCallback(kTime2146500, params->param4, 10, WRAP_SETUP_FUNCTION(Mertens, setup_function32))) + break; label_callback_10: - TIME_CHECK_CALLBACK(kTime2169000, params->param5, 11, setup_function32); + Entity::timeCheckCallback(kTime2169000, params->param5, 11, WRAP_SETUP_FUNCTION(Mertens, setup_function32)); break; case kAction11: @@ -3742,21 +3731,26 @@ label_callback_3: label_callback_4: if (!params->param1) { - TIME_CHECK_CALLBACK(kTime2403000, params->param2, 5, setup_function49); + if (Entity::timeCheckCallback(kTime2403000, params->param2, 5, WRAP_SETUP_FUNCTION(Mertens, setup_function49))) + break; label_callback_5: - TIME_CHECK_CALLBACK(kTime2430000, params->param3, 6, setup_function32); + if (Entity::timeCheckCallback(kTime2430000, params->param3, 6, WRAP_SETUP_FUNCTION(Mertens, setup_function32))) + break; label_callback_6: - TIME_CHECK_CALLBACK(kTime2439000, params->param4, 7, setup_function32); + if (Entity::timeCheckCallback(kTime2439000, params->param4, 7, WRAP_SETUP_FUNCTION(Mertens, setup_function32))) + break; label_callback_7: - TIME_CHECK_CALLBACK(kTime2448000, params->param5, 8, setup_function32); + if (Entity::timeCheckCallback(kTime2448000, params->param5, 8, WRAP_SETUP_FUNCTION(Mertens, setup_function32))) + break; } label_callback_8: if (getState()->time > kTime2538000 && !ENTITY_PARAM(0, 1) && !ENTITY_PARAM(2, 1)) { - UPDATE_PARAM(params->param6, getState()->time, 2700); + if (!Entity::updateParameter(params->param6, getState()->time, 2700)) + break; getEntities()->drawSequenceLeft(kEntityMertens, "601E"); @@ -3913,7 +3907,7 @@ IMPLEMENT_FUNCTION(49, Mertens, function49) break; case 11: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -4010,7 +4004,8 @@ IMPLEMENT_FUNCTION(53, Mertens, function53) case kActionNone: if (params->param1) { - UPDATE_PARAM(params->param4, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param4, getState()->timeTicks, 75)) + break; params->param1 = 0; params->param2 = 0; @@ -4108,4 +4103,15 @@ IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// IMPLEMENT_NULL_FUNCTION(54, Mertens) +////////////////////////////////////////////////////////////////////////// +// Helper functions +////////////////////////////////////////////////////////////////////////// + +void Mertens::loadSceneFromPosition() { + if (getData()->direction != kDirectionUp) + getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + 750)); + else + getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition - 750), true); +} + } // End of namespace LastExpress diff --git a/engines/lastexpress/entities/mertens.h b/engines/lastexpress/entities/mertens.h index 31b7a97dcd..4cc58fd4ba 100644 --- a/engines/lastexpress/entities/mertens.h +++ b/engines/lastexpress/entities/mertens.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_MERTENS_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { @@ -210,6 +209,9 @@ public: DECLARE_FUNCTION(function53) DECLARE_NULL_FUNCTION() + +private: + void loadSceneFromPosition(); }; } // End of namespace LastExpress diff --git a/engines/lastexpress/entities/milos.cpp b/engines/lastexpress/entities/milos.cpp index ff3d2b6744..519a613497 100644 --- a/engines/lastexpress/entities/milos.cpp +++ b/engines/lastexpress/entities/milos.cpp @@ -36,10 +36,8 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" #include "lastexpress/lastexpress.h" -#include "lastexpress/helpers.h" namespace LastExpress { @@ -135,7 +133,7 @@ IMPLEMENT_FUNCTION_II(10, Milos, enterCompartmentDialog, CarIndex, EntityPositio case kActionNone: case kActionDefault: if (getEntities()->updateEntity(kEntityMilos, (CarIndex)params->param1, (EntityPosition)params->param2)) - CALLBACK_ACTION(); + callbackAction(); break; case kActionExcuseMeCath: @@ -173,16 +171,16 @@ IMPLEMENT_FUNCTION_I(11, Milos, function11, TimeValue) if (!params->param5 && params->param1 < getState()->time && !params->param7) { params->param7 = 1; - CALLBACK_ACTION(); + callbackAction(); break; } if (params->param2) { - UPDATE_PARAM_PROC(params->param8, getState()->timeTicks, 75) + if (Entity::updateParameter(params->param8, getState()->timeTicks, 75)) { params->param2 = 0; params->param3 = 1; getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation1, kCursorNormal, kCursorNormal); - UPDATE_PARAM_PROC_END + } } params->param8 = 0; @@ -191,10 +189,10 @@ IMPLEMENT_FUNCTION_I(11, Milos, function11, TimeValue) break; if (params->param6) { - UPDATE_PARAM_PROC(CURRENT_PARAM(1, 1), getState()->time, 4500) + if (Entity::updateParameter(CURRENT_PARAM(1, 1), getState()->time, 4500)) { params->param6 = 0; CURRENT_PARAM(1, 1) = 0; - UPDATE_PARAM_PROC_END + } } if (!getProgress().field_CC) { @@ -364,7 +362,7 @@ IMPLEMENT_FUNCTION(12, Milos, chapter1) break; case kActionNone: - TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler); + Entity::timeCheck(kTimeChapter1, params->param1, WRAP_SETUP_FUNCTION(Milos, setup_chapter1Handler)); break; case kActionDefault: @@ -394,7 +392,7 @@ IMPLEMENT_FUNCTION(13, Milos, function13) getEntities()->clearSequences(kEntityIvo); getEntities()->clearSequences(kEntitySalko); - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: @@ -422,7 +420,7 @@ IMPLEMENT_FUNCTION(14, Milos, function14) getEntities()->exitCompartment(kEntityMilos, kObjectCompartment1, true); getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); } break; } @@ -436,7 +434,8 @@ IMPLEMENT_FUNCTION(14, Milos, function14) if (CURRENT_PARAM(1, 1) < getState()->timeTicks) { if (getObjects()->get(kObjectCompartment1).location == kObjectLocation1) { - UPDATE_PARAM(CURRENT_PARAM(1, 2), getState()->timeTicks, 75); + if (!Entity::updateParameter(CURRENT_PARAM(1, 2), getState()->timeTicks, 75)) + break; getObjects()->update(kObjectCompartment1, kEntityMilos, getObjects()->get(kObjectCompartment1).location, kCursorNormal, kCursorNormal); @@ -474,7 +473,7 @@ IMPLEMENT_FUNCTION(14, Milos, function14) getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); break; } } else { @@ -507,7 +506,8 @@ IMPLEMENT_FUNCTION(14, Milos, function14) } label_callback_12: - UPDATE_PARAM(CURRENT_PARAM(1, 4), getState()->timeTicks, 75); + if (!Entity::updateParameter(CURRENT_PARAM(1, 4), getState()->timeTicks, 75)) + break; getEntities()->exitCompartment(kEntityMilos, kObjectCompartment1, true); @@ -593,7 +593,7 @@ label_callback_12: getData()->location = kLocationOutsideCompartment; getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); break; case 2: @@ -633,7 +633,7 @@ label_callback_12: getScenes()->loadScene(kScene41); getData()->location = kLocationOutsideCompartment; - CALLBACK_ACTION(); + callbackAction(); break; case 6: @@ -722,7 +722,7 @@ IMPLEMENT_FUNCTION(15, Milos, chapter1Handler) break; case kActionNone: - TIME_CHECK_SAVEPOINT(kTime1071000, params->param3, kEntityMilos, kEntityServers1, kAction223002560); + Entity::timeCheckSavepoint(kTime1071000, params->param3, kEntityMilos, kEntityServers1, kAction223002560); if (getState()->time > kTime1089000 && getEntities()->isSomebodyInsideRestaurantOrSalon()) { setup_function16(); @@ -730,15 +730,16 @@ IMPLEMENT_FUNCTION(15, Milos, chapter1Handler) } if (getEntities()->isPlayerPosition(kCarRestaurant, 61) && !params->param1) { - UPDATE_PARAM_PROC(params->param4, getState()->timeTicks, 45) + if (Entity::updateParameter(params->param4, getState()->timeTicks, 45)) { setCallback(1); setup_draw("009C"); break; - UPDATE_PARAM_PROC_END + } } if (getEntities()->isPlayerPosition(kCarRestaurant, 70) && !params->param2) { - UPDATE_PARAM(params->param5, getState()->timeTicks, 45); + if (!Entity::updateParameter(params->param5, getState()->timeTicks, 45)) + break; setCallback(2); setup_draw("009C"); @@ -953,7 +954,8 @@ IMPLEMENT_FUNCTION(21, Milos, function21) break; case kActionNone: - UPDATE_PARAM(params->param2, getState()->time, 4500); + if (!Entity::updateParameter(params->param2, getState()->time, 4500)) + break; params->param1 = 1; break; @@ -1118,7 +1120,8 @@ IMPLEMENT_FUNCTION(24, Milos, function24) } if (params->param1) { - UPDATE_PARAM(params->param5, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param5, getState()->timeTicks, 75)) + break; params->param1 = 0; params->param2 = 1; @@ -1284,14 +1287,15 @@ IMPLEMENT_FUNCTION(25, Milos, function25) case kActionNone: if (!getEvent(kEventMilosCompartmentVisitTyler) && !getProgress().field_54 && !ENTITY_PARAM(0, 4)) { - UPDATE_PARAM_PROC(params->param3, getState()->time, 13500) + if (Entity::updateParameter(params->param3, getState()->time, 13500)) { getSavePoints()->push(kEntityMilos, kEntityVesna, kAction155913424); params->param3 = 0; - UPDATE_PARAM_PROC_END + } } if (params->param1) { - UPDATE_PARAM(params->param4, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param4, getState()->timeTicks, 75)) + break; params->param1 = 0; params->param2 = 1; @@ -1386,7 +1390,7 @@ IMPLEMENT_FUNCTION_I(26, Milos, function26, TimeValue) case kActionNone: if (params->param1 < getState()->time && !params->param2) { - CALLBACK_ACTION(); + callbackAction(); break; } @@ -1415,7 +1419,7 @@ IMPLEMENT_FUNCTION_I(26, Milos, function26, TimeValue) case 1: if (ENTITY_PARAM(0, 2)) { - CALLBACK_ACTION(); + callbackAction(); break; } @@ -1425,7 +1429,7 @@ IMPLEMENT_FUNCTION_I(26, Milos, function26, TimeValue) case 2: case 3: if (ENTITY_PARAM(0, 2)) { - CALLBACK_ACTION(); + callbackAction(); break; } @@ -1442,7 +1446,7 @@ IMPLEMENT_FUNCTION_I(26, Milos, function26, TimeValue) case 5: if (ENTITY_PARAM(0, 2)) { - CALLBACK_ACTION(); + callbackAction(); break; } @@ -1461,7 +1465,7 @@ IMPLEMENT_FUNCTION_II(27, Milos, function27, CarIndex, EntityPosition) case kActionNone: if (getEntities()->updateEntity(kEntityMilos, (CarIndex)params->param1, (EntityPosition)params->param2)) { - CALLBACK_ACTION(); + callbackAction(); break; } @@ -1472,14 +1476,14 @@ IMPLEMENT_FUNCTION_II(27, Milos, function27, CarIndex, EntityPosition) if (getData()->car == kCarRedSleeping || getData()->car == kCarGreenSleeping) { ENTITY_PARAM(0, 2) = 1; - CALLBACK_ACTION(); + callbackAction(); } } break; case kActionDefault: if (getEntities()->updateEntity(kEntityMilos, (CarIndex)params->param1, (EntityPosition)params->param2)) - CALLBACK_ACTION(); + callbackAction(); break; } IMPLEMENT_FUNCTION_END @@ -1536,7 +1540,7 @@ IMPLEMENT_FUNCTION(29, Milos, chapter4Handler) TIME_CHECK_PLAYSOUND_MILOS(kTime2370600, params->param5, "Mil4015"); - TIME_CHECK_SAVEPOINT(kTime2407500, params->param6, kEntityMilos, kEntityVesna, kAction55996766); + Entity::timeCheckSavepoint(kTime2407500, params->param6, kEntityMilos, kEntityVesna, kAction55996766); break; case kActionCallback: diff --git a/engines/lastexpress/entities/milos.h b/engines/lastexpress/entities/milos.h index d58d717f8a..e8f95dc5ff 100644 --- a/engines/lastexpress/entities/milos.h +++ b/engines/lastexpress/entities/milos.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_MILOS_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/mmeboutarel.cpp b/engines/lastexpress/entities/mmeboutarel.cpp index 9ca10ca374..950644cb8f 100644 --- a/engines/lastexpress/entities/mmeboutarel.cpp +++ b/engines/lastexpress/entities/mmeboutarel.cpp @@ -31,10 +31,8 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" #include "lastexpress/lastexpress.h" -#include "lastexpress/helpers.h" namespace LastExpress { @@ -124,7 +122,7 @@ IMPLEMENT_FUNCTION_S(8, MmeBoutarel, function8) if (!getEntities()->isPlayerPosition(kCarRedSleeping, 2)) getData()->entityPosition = kPosition_2088; - CALLBACK_ACTION(); + callbackAction(); } break; @@ -211,7 +209,7 @@ IMPLEMENT_FUNCTION(9, MmeBoutarel, function9) getData()->location = kLocationInsideCompartment; getEntities()->clearSequences(kEntityMmeBoutarel); - CALLBACK_ACTION(); + callbackAction(); break; case 5: @@ -220,7 +218,7 @@ IMPLEMENT_FUNCTION(9, MmeBoutarel, function9) getData()->location = kLocationInsideCompartment; getEntities()->clearSequences(kEntityMmeBoutarel); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -246,7 +244,7 @@ IMPLEMENT_FUNCTION(10, MmeBoutarel, chapter1) break; case kActionNone: - TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler); + Entity::timeCheck(kTimeChapter1, params->param1, WRAP_SETUP_FUNCTION(MmeBoutarel, setup_chapter1Handler)); break; case kActionDefault: @@ -312,7 +310,7 @@ IMPLEMENT_FUNCTION(11, MmeBoutarel, function11) break; case 4: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -402,7 +400,7 @@ IMPLEMENT_FUNCTION(13, MmeBoutarel, function13) case kActionNone: if (!getSoundQueue()->isBuffered(kEntityMmeBoutarel) && params->param6 != kTimeInvalid) { - UPDATE_PARAM_PROC_TIME(params->param1, !getEntities()->isDistanceBetweenEntities(kEntityMmeBoutarel, kEntityPlayer, 2000), params->param6, 0) + if (Entity::updateParameterTime((TimeValue)params->param1, !getEntities()->isDistanceBetweenEntities(kEntityMmeBoutarel, kEntityPlayer, 2000), params->param6, 0)) { getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorNormal); getObjects()->update(kObject51, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorNormal); @@ -414,22 +412,24 @@ IMPLEMENT_FUNCTION(13, MmeBoutarel, function13) setCallback(1); setup_playSound("MME1037"); break; - UPDATE_PARAM_PROC_END + } } label_callback_1: if (getProgress().field_24 && params->param7 != kTimeInvalid) { - UPDATE_PARAM_PROC_TIME(kTime1093500, (!params->param5 || !getEntities()->isPlayerInCar(kCarRedSleeping)), params->param7, 0) + if (Entity::updateParameterTime(kTime1093500, (!params->param5 || !getEntities()->isPlayerInCar(kCarRedSleeping)), params->param7, 0)) { setCallback(2); setup_function11(); break; - UPDATE_PARAM_PROC_END + } } - TIME_CHECK(kTime1094400, params->param8, setup_function14); + if (Entity::timeCheck(kTime1094400, params->param8, WRAP_SETUP_FUNCTION(MmeBoutarel, setup_function14))) + break; if (params->param4) { - UPDATE_PARAM(CURRENT_PARAM(1, 1), getState()->timeTicks, 75); + if (!Entity::updateParameter(CURRENT_PARAM(1, 1), getState()->timeTicks, 75)) + break; params->param3 = 1; params->param4 = 0; @@ -589,7 +589,8 @@ IMPLEMENT_FUNCTION(15, MmeBoutarel, function15) label_callback_5: if (params->param2) { - UPDATE_PARAM(params->param5, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param5, getState()->timeTicks, 75)) + break; params->param1 = 1; params->param2 = 0; @@ -1018,7 +1019,8 @@ IMPLEMENT_FUNCTION(23, MmeBoutarel, chapter4Handler) case kActionNone: if (params->param1) { - UPDATE_PARAM(params->param2, getState()->time, 900); + if (!Entity::updateParameter(params->param2, getState()->time, 900)) + break; getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue); @@ -1064,10 +1066,12 @@ IMPLEMENT_FUNCTION(24, MmeBoutarel, function24) break; case kActionNone: - TIME_CHECK(kTime2470500, params->param4, setup_function25); + if (Entity::timeCheck(kTime2470500, params->param4, WRAP_SETUP_FUNCTION(MmeBoutarel, setup_function25))) + break; if (params->param2) { - UPDATE_PARAM(params->param5, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param5, getState()->timeTicks, 75)) + break; params->param1 = 1; params->param2 = 0; @@ -1210,7 +1214,8 @@ IMPLEMENT_FUNCTION(28, MmeBoutarel, function28) case kActionNone: if (params->param1) { - UPDATE_PARAM(params->param3, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param3, getState()->timeTicks, 75)) + break; params->param1 = 0; params->param2 = 1; diff --git a/engines/lastexpress/entities/mmeboutarel.h b/engines/lastexpress/entities/mmeboutarel.h index 772451ba15..0f6e6349fe 100644 --- a/engines/lastexpress/entities/mmeboutarel.h +++ b/engines/lastexpress/entities/mmeboutarel.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_MMEBOUTAREL_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/pascale.cpp b/engines/lastexpress/entities/pascale.cpp index a191273702..6e9f992390 100644 --- a/engines/lastexpress/entities/pascale.cpp +++ b/engines/lastexpress/entities/pascale.cpp @@ -30,10 +30,8 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" #include "lastexpress/lastexpress.h" -#include "lastexpress/helpers.h" namespace LastExpress { @@ -171,7 +169,7 @@ IMPLEMENT_FUNCTION(8, Pascale, welcomeSophieAndRebecca) getData()->entityPosition = kPosition_5900; ENTITY_PARAM(0, 4) = 0; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -185,8 +183,8 @@ IMPLEMENT_FUNCTION(9, Pascale, sitSophieAndRebecca) break; case kActionExitCompartment: - CALLBACK_ACTION(); - break; + callbackAction(); + break; case kActionDefault: getEntities()->drawSequenceLeft(kEntityPascale, "012C1"); @@ -217,7 +215,7 @@ IMPLEMENT_FUNCTION(10, Pascale, welcomeCath) getScenes()->loadSceneFromPosition(kCarRestaurant, 69); } - CALLBACK_ACTION(); + callbackAction(); break; case kAction4: @@ -239,7 +237,7 @@ IMPLEMENT_FUNCTION(10, Pascale, welcomeCath) getScenes()->loadSceneFromPosition(kCarRestaurant, 69); - CALLBACK_ACTION(); + callbackAction(); } break; } @@ -283,7 +281,7 @@ IMPLEMENT_FUNCTION(11, Pascale, function11) getEntities()->clearSequences(kEntityPascale); getData()->entityPosition = kPosition_5900; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -298,7 +296,7 @@ IMPLEMENT_FUNCTION(12, Pascale, chapter1) case kActionNone: setup_chapter1Handler(); - break; + break; case kActionDefault: getSavePoints()->addData(kEntityPascale, kAction239072064, 0); @@ -366,7 +364,7 @@ IMPLEMENT_FUNCTION(13, Pascale, getMessageFromAugustToTyler) getSavePoints()->push(kEntityPascale, kEntityVerges, kActionDeliverMessageToTyler); ENTITY_PARAM(0, 1) = 0; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -382,7 +380,7 @@ IMPLEMENT_FUNCTION(14, Pascale, sitAnna) case kActionExitCompartment: getEntities()->updatePositionExit(kEntityPascale, kCarRestaurant, 62); - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: @@ -433,7 +431,7 @@ IMPLEMENT_FUNCTION(15, Pascale, welcomeAnna) getData()->entityPosition = kPosition_5900; ENTITY_PARAM(0, 2) = 0; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -486,7 +484,7 @@ IMPLEMENT_FUNCTION(16, Pascale, serveTatianaVassili) getData()->entityPosition = kPosition_5900; ENTITY_PARAM(0, 3) = 0; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -648,7 +646,7 @@ IMPLEMENT_FUNCTION(21, Pascale, chapter3) case kActionNone: setup_chapter3Handler(); - break; + break; case kActionDefault: getEntities()->clearSequences(kEntityPascale); @@ -685,7 +683,7 @@ label_callback: setCallback(2); setup_welcomeSophieAndRebecca(); } - break; + break; case kActionCallback: if (getCallback() == 1) @@ -727,7 +725,7 @@ IMPLEMENT_FUNCTION(23, Pascale, function23) ENTITY_PARAM(0, 7) = 0; getEntities()->clearSequences(kEntityPascale); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -748,7 +746,7 @@ IMPLEMENT_FUNCTION(24, Pascale, welcomeAbbot) break; case kActionExitCompartment: - CALLBACK_ACTION(); + callbackAction(); break; case kAction10: @@ -771,7 +769,7 @@ IMPLEMENT_FUNCTION(25, Pascale, chapter4) case kActionNone: setup_chapter4Handler(); - break; + break; case kActionDefault: getEntities()->clearSequences(kEntityPascale); @@ -953,7 +951,7 @@ IMPLEMENT_FUNCTION(27, Pascale, function27) ENTITY_PARAM(1, 1) = 0; ENTITY_PARAM(1, 2) = 1; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -999,7 +997,7 @@ IMPLEMENT_FUNCTION(28, Pascale, messageFromAnna) getData()->entityPosition = kPosition_5900; ENTITY_PARAM(1, 2) = 0; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1037,7 +1035,7 @@ IMPLEMENT_FUNCTION(29, Pascale, function29) case 2: getData()->entityPosition = kPosition_850; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1075,7 +1073,7 @@ IMPLEMENT_FUNCTION(30, Pascale, function30) case 2: getData()->entityPosition = kPosition_5900; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1090,7 +1088,7 @@ IMPLEMENT_FUNCTION(31, Pascale, chapter5) case kActionNone: setup_chapter5Handler(); - break; + break; case kActionDefault: getEntities()->clearSequences(kEntityPascale); @@ -1117,18 +1115,19 @@ IMPLEMENT_FUNCTION(33, Pascale, function33) case kActionNone: if (params->param4) { - UPDATE_PARAM_PROC(params->param5, getState()->time, 4500) + if (Entity::updateParameter(params->param5, getState()->time, 4500)) { getObjects()->update(kObjectCompartmentG, kEntityPascale, kObjectLocation1, kCursorNormal, kCursorNormal); setCallback(1); setup_playSound("Wat5010"); break; - UPDATE_PARAM_PROC_END + } } label_callback1: if (params->param1) { - UPDATE_PARAM(params->param6, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param6, getState()->timeTicks, 75)) + break; params->param1 = 0; params->param2 = 2; diff --git a/engines/lastexpress/entities/pascale.h b/engines/lastexpress/entities/pascale.h index 333ebcae3e..eaf0f3ba0c 100644 --- a/engines/lastexpress/entities/pascale.h +++ b/engines/lastexpress/entities/pascale.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_PASCALE_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/rebecca.cpp b/engines/lastexpress/entities/rebecca.cpp index b1a176b47e..5bcb6aef85 100644 --- a/engines/lastexpress/entities/rebecca.cpp +++ b/engines/lastexpress/entities/rebecca.cpp @@ -30,10 +30,8 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" #include "lastexpress/lastexpress.h" -#include "lastexpress/helpers.h" namespace LastExpress { @@ -179,7 +177,7 @@ IMPLEMENT_FUNCTION(15, Rebecca, function15) getData()->location = kLocationInsideCompartment; getEntities()->clearSequences(kEntityRebecca); - CALLBACK_ACTION(); + callbackAction(); } break; } @@ -261,7 +259,7 @@ IMPLEMENT_FUNCTION_I(16, Rebecca, function16, bool) getSavePoints()->push(kEntityRebecca, kEntityTables3, kAction136455232); getData()->location = kLocationInsideCompartment; - CALLBACK_ACTION(); + callbackAction(); break; } IMPLEMENT_FUNCTION_END @@ -334,7 +332,7 @@ IMPLEMENT_FUNCTION_I(17, Rebecca, function17, bool) case 5: getData()->location = kLocationInsideCompartment; - CALLBACK_ACTION(); + callbackAction(); break; case 6: @@ -343,7 +341,7 @@ IMPLEMENT_FUNCTION_I(17, Rebecca, function17, bool) getData()->location = kLocationInsideCompartment; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -397,7 +395,7 @@ IMPLEMENT_FUNCTION(18, Rebecca, function18) case 2: case 3: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -473,7 +471,7 @@ IMPLEMENT_FUNCTION(19, Rebecca, function19) case 5: case 6: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -493,21 +491,21 @@ IMPLEMENT_FUNCTION_I(20, Rebecca, function20, TimeValue) getObjects()->update(kObjectCompartmentE, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand); getObjects()->update(kObject52, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); break; } if (!params->param2) { params->param6 = 0; } else { - UPDATE_PARAM_PROC(params->param6, getState()->timeTicks, 75) + if (Entity::updateParameter(params->param6, getState()->timeTicks, 75)) { params->param2 = 0; params->param3 = 1; getObjects()->update(kObjectCompartmentE, kEntityRebecca, kObjectLocation1, kCursorNormal, kCursorNormal); getObjects()->update(kObject52, kEntityRebecca, kObjectLocation1, kCursorNormal, kCursorNormal); params->param6 = 0; - UPDATE_PARAM_PROC_END + } } if (getProgress().chapter == kChapter1 && !ENTITY_PARAM(0, 3)) { @@ -657,7 +655,7 @@ IMPLEMENT_FUNCTION(21, Rebecca, chapter1) break; case kActionNone: - TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler); + Entity::timeCheck(kTimeChapter1, params->param1, WRAP_SETUP_FUNCTION(Rebecca, setup_chapter1Handler)); break; case kActionDefault: @@ -685,7 +683,8 @@ IMPLEMENT_FUNCTION(22, Rebecca, chapter1Handler) break; case kActionNone: - TIME_CHECK_CALLBACK_1(kTime1084500, params->param3, 1, setup_playSound, "REB1015"); + if (Entity::timeCheckCallback(kTime1084500, params->param3, 1, "REB1015", WRAP_SETUP_FUNCTION_S(Rebecca, setup_playSound))) + break; if (params->param4 == kTimeInvalid) goto label_callback_4; @@ -699,7 +698,7 @@ IMPLEMENT_FUNCTION(22, Rebecca, chapter1Handler) if (params->param4 >= getState()->time) { label_callback_4: if (params->param1) { - UPDATE_PARAM_CHECK(params->param5, getState()->time, 900) + if (Entity::updateParameterCheck(params->param5, getState()->time, 900)) { if (getEntities()->isInSalon(kEntityPlayer)) { setCallback(5); setup_playSound("REB1013"); @@ -710,7 +709,9 @@ label_callback_4: label_callback_5: if (params->param2) { - UPDATE_PARAM(params->param6, getState()->timeTicks, 90); + if (!Entity::updateParameter(params->param6, getState()->timeTicks, 90)) + break; + getScenes()->loadSceneFromPosition(kCarRestaurant, 55); } else { params->param6 = 0; @@ -774,7 +775,13 @@ IMPLEMENT_FUNCTION(23, Rebecca, function23) break; case kActionNone: - TIME_CHECK_CALLBACK_2(kTime1111500, params->param2, 3, setup_enterExitCompartment, "623De", kObjectCompartmentE); + if (getState()->time > kTime1111500 && !params->param2) { + params->param2 = 1; + setCallback(3); + setup_enterExitCompartment("623De", kObjectCompartmentE); + + break; + } break; case kActionDefault: @@ -843,12 +850,13 @@ IMPLEMENT_FUNCTION(24, Rebecca, function24) break; case kActionNone: - TIME_CHECK_SAVEPOINT(kTime1134000, params->param2, kEntityRebecca, kEntityServers0, kAction223712416); + Entity::timeCheckSavepoint(kTime1134000, params->param2, kEntityRebecca, kEntityServers0, kAction223712416); if (!params->param1) break; - TIME_CHECK_CALLBACK(kTime1165500, params->param3, 6, setup_function19); + if (Entity::timeCheckCallback(kTime1165500, params->param3, 6, WRAP_SETUP_FUNCTION(Rebecca, setup_function19))) + break; if (params->param4 != kTimeInvalid) { if (getState()->time <= kTime1161000) { @@ -965,10 +973,16 @@ IMPLEMENT_FUNCTION(26, Rebecca, function26) break; case kActionNone: - TIME_CHECK_CALLBACK_3(kTime1224000, params->param2, 1, setup_updatePosition, "118H", kCarRestaurant, 52); + if (getState()->time > kTime1224000 && !params->param2) { + params->param2 = 1; + setCallback(1); + setup_updatePosition("118H", kCarRestaurant, 52); + break; + } if (params->param1) { - UPDATE_PARAM(params->param3, getState()->timeTicks, 90); + if (!Entity::updateParameter(params->param3, getState()->timeTicks, 90)) + break; getScenes()->loadSceneFromPosition(kCarRestaurant, 51); } @@ -1223,7 +1237,7 @@ IMPLEMENT_FUNCTION(34, Rebecca, function34) params->param2 = (uint)getState()->time; if (params->param2 >= getState()->time) { - TIME_CHECK_CALLBACK(kTime2052000, params->param3, 1, setup_function19); + Entity::timeCheckCallback(kTime2052000, params->param3, 1, WRAP_SETUP_FUNCTION(Rebecca, setup_function19)); break; } } @@ -1233,7 +1247,7 @@ IMPLEMENT_FUNCTION(34, Rebecca, function34) getSavePoints()->push(kEntityRebecca, kEntityServers0, kAction223712416); } - TIME_CHECK_CALLBACK(kTime2052000, params->param3, 1, setup_function19); + Entity::timeCheckCallback(kTime2052000, params->param3, 1, WRAP_SETUP_FUNCTION(Rebecca, setup_function19)); break; case kActionEndSound: @@ -1632,7 +1646,7 @@ label_next: label_callback_2: if (params->param2) - TIME_CHECK_CALLBACK(kTime2443500, params->param5, 3, setup_function19); + Entity::timeCheckCallback(kTime2443500, params->param5, 3, WRAP_SETUP_FUNCTION(Rebecca, setup_function19)); break; case kActionEndSound: @@ -1755,7 +1769,8 @@ IMPLEMENT_FUNCTION(48, Rebecca, function48) case kActionNone: if (params->param1) { - UPDATE_PARAM(params->param3, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param3, getState()->timeTicks, 75)) + break; params->param1 = 0; params->param2 = 1; diff --git a/engines/lastexpress/entities/rebecca.h b/engines/lastexpress/entities/rebecca.h index e91ee30d4d..885632f884 100644 --- a/engines/lastexpress/entities/rebecca.h +++ b/engines/lastexpress/entities/rebecca.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_REBECCA_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/salko.cpp b/engines/lastexpress/entities/salko.cpp index 63d995dc42..e8b4b4247b 100644 --- a/engines/lastexpress/entities/salko.cpp +++ b/engines/lastexpress/entities/salko.cpp @@ -33,10 +33,8 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" #include "lastexpress/lastexpress.h" -#include "lastexpress/helpers.h" namespace LastExpress { @@ -137,7 +135,7 @@ IMPLEMENT_FUNCTION_II(7, Salko, function7, CarIndex, EntityPosition) break; case kAction123668192: - CALLBACK_ACTION(); + callbackAction(); break; } IMPLEMENT_FUNCTION_END @@ -158,7 +156,7 @@ IMPLEMENT_FUNCTION(9, Salko, chapter1) break; case kActionNone: - TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler); + Entity::timeCheck(kTimeChapter1, params->param1, WRAP_SETUP_FUNCTION(Salko, setup_chapter1Handler)); break; case kActionDefault: @@ -301,7 +299,8 @@ IMPLEMENT_FUNCTION(15, Salko, chapter3Handler) case kActionNone: if (getState()->time < kTime2200500) { - UPDATE_PARAM(params->param1, getState()->time, 81000); + if (!Entity::updateParameter(params->param1, getState()->time, 81000)) + break; setCallback(1); setup_function16(); @@ -331,7 +330,8 @@ IMPLEMENT_FUNCTION(16, Salko, function16) } label_callback3: - UPDATE_PARAM(params->param1, getState()->time, 4500); + if (!Entity::updateParameter(params->param1, getState()->time, 4500)) + break; getSavePoints()->push(kEntitySalko, kEntitySalko, kAction101169464); break; @@ -390,7 +390,7 @@ label_callback3: getData()->entityPosition = kPosition_2740; getEntities()->clearSequences(kEntitySalko); - CALLBACK_ACTION(); + callbackAction(); break; } break; diff --git a/engines/lastexpress/entities/salko.h b/engines/lastexpress/entities/salko.h index 6308211053..6ca73e39f6 100644 --- a/engines/lastexpress/entities/salko.h +++ b/engines/lastexpress/entities/salko.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_SALKO_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/servers0.cpp b/engines/lastexpress/entities/servers0.cpp index 989bddd662..73e0d34722 100644 --- a/engines/lastexpress/entities/servers0.cpp +++ b/engines/lastexpress/entities/servers0.cpp @@ -28,10 +28,7 @@ #include "lastexpress/game/savepoint.h" #include "lastexpress/game/state.h" -#include "lastexpress/sound/sound.h" - #include "lastexpress/lastexpress.h" -#include "lastexpress/helpers.h" namespace LastExpress { @@ -113,11 +110,11 @@ IMPLEMENT_FUNCTION_NOSETUP(5, Servers0, callbackActionOnDirection) case kActionNone: if (getData()->direction != kDirectionRight) - CALLBACK_ACTION(); + callbackAction(); break; case kActionExitCompartment: - CALLBACK_ACTION(); + callbackAction(); break; case kActionExcuseMeCath: @@ -163,7 +160,7 @@ IMPLEMENT_FUNCTION(7, Servers0, function7) case 2: getEntities()->clearSequences(kEntityServers0); getData()->entityPosition = kPosition_5900; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -220,7 +217,7 @@ IMPLEMENT_FUNCTION(9, Servers0, function9) ENTITY_PARAM(2, 2) = 0; ENTITY_PARAM(1, 6) = 0; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -315,17 +312,17 @@ IMPLEMENT_FUNCTION(20, Servers0, chapter1Handler) case kActionNone: if (params->param2) { - UPDATE_PARAM_PROC(params->param3, getState()->time, 2700); + if (Entity::updateParameter(params->param3, getState()->time, 2700)) { ENTITY_PARAM(0, 4) = 1; params->param2 = 0; - UPDATE_PARAM_PROC_END + } } if (params->param1) { - UPDATE_PARAM_PROC(params->param4, getState()->time, 4500) + if (Entity::updateParameter(params->param4, getState()->time, 4500)) { ENTITY_PARAM(0, 5) = 1; params->param1 = 0; - UPDATE_PARAM_PROC_END + } } if (!getEntities()->isInKitchen(kEntityServers0) && !getEntities()->isSomebodyInsideRestaurantOrSalon()) @@ -486,7 +483,7 @@ IMPLEMENT_FUNCTION(25, Servers0, function25) getEntities()->clearSequences(kEntityServers0); ENTITY_PARAM(1, 3) = 0; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -647,7 +644,7 @@ IMPLEMENT_FUNCTION(29, Servers0, augustAnnaDateOrder) getEntities()->clearSequences(kEntityServers0); ENTITY_PARAM(1, 5) = 0; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -693,7 +690,7 @@ IMPLEMENT_FUNCTION(30, Servers0, function30) getEntities()->clearSequences(kEntityServers0); ENTITY_PARAM(2, 4) = 0; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -736,10 +733,10 @@ IMPLEMENT_FUNCTION(32, Servers0, chapter4Handler) break; case kActionNone: - UPDATE_PARAM_PROC(params->param2, getState()->time, 3600) + if (Entity::updateParameter(params->param2, getState()->time, 3600)) { ENTITY_PARAM(1, 8) = 1; params->param1 = 0; - UPDATE_PARAM_PROC_END + } if (!getEntities()->isInKitchen(kEntityServers1) || !getEntities()->isSomebodyInsideRestaurantOrSalon()) break; @@ -859,7 +856,7 @@ IMPLEMENT_FUNCTION(33, Servers0, augustOrderSteak) getEntities()->clearSequences(kEntityServers0); ENTITY_PARAM(1, 7) = 0; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -902,7 +899,7 @@ IMPLEMENT_FUNCTION(34, Servers0, augustServeDuck) getEntities()->clearSequences(kEntityServers0); ENTITY_PARAM(1, 8) = 0; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -972,7 +969,7 @@ void Servers0::handleServer(const SavePoint &savepoint, const char *name, Entity getSavePoints()->push(kEntityServers0, entity, action); *parameter = 0; - CALLBACK_ACTION(); + callbackAction(); } break; } @@ -1027,7 +1024,7 @@ void Servers0::serveTable(const SavePoint &savepoint, const char *seq1, EntityIn getEntities()->clearSequences(kEntityServers0); *parameter = 0; - CALLBACK_ACTION(); + callbackAction(); break; } break; diff --git a/engines/lastexpress/entities/servers0.h b/engines/lastexpress/entities/servers0.h index 2e9165a410..4c35637d65 100644 --- a/engines/lastexpress/entities/servers0.h +++ b/engines/lastexpress/entities/servers0.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_SERVERS0_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/servers1.cpp b/engines/lastexpress/entities/servers1.cpp index 995fbbc01b..a8f4c0233c 100644 --- a/engines/lastexpress/entities/servers1.cpp +++ b/engines/lastexpress/entities/servers1.cpp @@ -28,10 +28,7 @@ #include "lastexpress/game/savepoint.h" #include "lastexpress/game/state.h" -#include "lastexpress/sound/sound.h" - #include "lastexpress/lastexpress.h" -#include "lastexpress/helpers.h" namespace LastExpress { @@ -143,7 +140,7 @@ IMPLEMENT_FUNCTION(7, Servers1, function7) getData()->entityPosition = kPosition_5900; ENTITY_PARAM(1, 2) = 0; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -219,7 +216,7 @@ IMPLEMENT_FUNCTION(9, Servers1, function9) getData()->entityPosition = kPosition_5900; ENTITY_PARAM(0, 1) = 0; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -265,7 +262,7 @@ IMPLEMENT_FUNCTION(10, Servers1, function10) getData()->entityPosition = kPosition_5900; ENTITY_PARAM(0, 2) = 0; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -469,7 +466,7 @@ IMPLEMENT_FUNCTION(20, Servers1, function20) getEntities()->drawSequenceLeft(kEntityServers1, "BLANK"); ENTITY_PARAM(0, 7) = 0; - CALLBACK_ACTION(); + callbackAction(); } break; } @@ -566,10 +563,10 @@ IMPLEMENT_FUNCTION(26, Servers1, chapter4Handler) case kActionNone: if (params->param2) { - UPDATE_PARAM_PROC(params->param2, getState()->time, 900) + if (Entity::updateParameter(params->param2, getState()->time, 900)) { ENTITY_PARAM(1, 5) = 1; params->param1 = 0; - UPDATE_PARAM_PROC_END + } } if (!getEntities()->isInKitchen(kEntityServers1) || !getEntities()->isSomebodyInsideRestaurantOrSalon()) @@ -705,7 +702,7 @@ void Servers1::serveTable(const SavePoint &savepoint, const char *seq1, EntityIn if (parameter2 != NULL) *parameter2 = 0; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -775,7 +772,7 @@ void Servers1::serveSalon(const SavePoint &savepoint, const char *seq1, const ch getData()->entityPosition = kPosition_5900; *parameter = 0; - CALLBACK_ACTION(); + callbackAction(); break; } break; diff --git a/engines/lastexpress/entities/servers1.h b/engines/lastexpress/entities/servers1.h index 13b30696f0..c8f667ec5c 100644 --- a/engines/lastexpress/entities/servers1.h +++ b/engines/lastexpress/entities/servers1.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_SERVERS1_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/sophie.cpp b/engines/lastexpress/entities/sophie.cpp index 57bd491949..170090005f 100644 --- a/engines/lastexpress/entities/sophie.cpp +++ b/engines/lastexpress/entities/sophie.cpp @@ -27,38 +27,10 @@ #include "lastexpress/game/savepoint.h" #include "lastexpress/game/state.h" -#include "lastexpress/sound/sound.h" - -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" namespace LastExpress { -#define CHAPTER_IMPLEMENTATION() \ - switch (savepoint.action) { \ - default: \ - break; \ - case kActionNone: \ - setup_chaptersHandler(); \ - break; \ - case kActionDefault: \ - getEntities()->clearSequences(kEntitySophie); \ - getData()->entityPosition = kPosition_4840; \ - getData()->location = kLocationInsideCompartment; \ - getData()->car = kCarRedSleeping; \ - getData()->clothes = kClothesDefault; \ - getData()->inventoryItem = kItemNone; \ - break; \ - } - -#define DEFAULT_ACTION_IMPLEMENTATION() \ - if (savepoint.action == kActionDefault) { \ - getData()->entityPosition = kPosition_4840; \ - getData()->location = kLocationInsideCompartment; \ - getData()->car = kCarRedSleeping; \ - getEntities()->clearSequences(kEntitySophie); \ - } - Sophie::Sophie(LastExpressEngine *engine) : Entity(engine, kEntitySophie) { ADD_CALLBACK_FUNCTION(Sophie, reset); ADD_CALLBACK_FUNCTION(Sophie, updateEntity); @@ -123,7 +95,7 @@ IMPLEMENT_FUNCTION_II(2, Sophie, updateEntity, CarIndex, EntityPosition) break; case kAction123668192: - CALLBACK_ACTION(); + callbackAction(); break; } IMPLEMENT_FUNCTION_END @@ -207,7 +179,7 @@ IMPLEMENT_FUNCTION(4, Sophie, chapter1) break; case kActionNone: - TIME_CHECK(kTimeChapter1, params->param1, setup_chaptersHandler); + Entity::timeCheck(kTimeChapter1, params->param1, WRAP_SETUP_FUNCTION(Sophie, setup_chaptersHandler)); break; case kActionDefault: @@ -220,27 +192,27 @@ IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// IMPLEMENT_FUNCTION(5, Sophie, function5) - DEFAULT_ACTION_IMPLEMENTATION() + handleAction(savepoint); IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// IMPLEMENT_FUNCTION(6, Sophie, chapter2) - CHAPTER_IMPLEMENTATION() + handleChapter(savepoint); IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// IMPLEMENT_FUNCTION(7, Sophie, chapter3) - CHAPTER_IMPLEMENTATION() + handleChapter(savepoint); IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// IMPLEMENT_FUNCTION(8, Sophie, chapter4) - CHAPTER_IMPLEMENTATION() + handleChapter(savepoint); IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// IMPLEMENT_FUNCTION(9, Sophie, function9) - DEFAULT_ACTION_IMPLEMENTATION() + handleAction(savepoint); IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// @@ -274,4 +246,37 @@ IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// IMPLEMENT_NULL_FUNCTION(12, Sophie) +////////////////////////////////////////////////////////////////////////// +// Helpers functions +////////////////////////////////////////////////////////////////////////// + +void Sophie::handleAction(const SavePoint &savepoint) { + if (savepoint.action == kActionDefault) { + getData()->entityPosition = kPosition_4840; + getData()->location = kLocationInsideCompartment; + getData()->car = kCarRedSleeping; + getEntities()->clearSequences(kEntitySophie); + } +} + +void Sophie::handleChapter(const SavePoint &savepoint) { + switch (savepoint.action) { + default: + break; + + case kActionNone: + setup_chaptersHandler(); + break; + + case kActionDefault: + getEntities()->clearSequences(kEntitySophie); + getData()->entityPosition = kPosition_4840; + getData()->location = kLocationInsideCompartment; + getData()->car = kCarRedSleeping; + getData()->clothes = kClothesDefault; + getData()->inventoryItem = kItemNone; + break; + } +} + } // End of namespace LastExpress diff --git a/engines/lastexpress/entities/sophie.h b/engines/lastexpress/entities/sophie.h index c2ca348027..188788bb9b 100644 --- a/engines/lastexpress/entities/sophie.h +++ b/engines/lastexpress/entities/sophie.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_SOPHIE_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { @@ -88,6 +87,10 @@ public: DECLARE_FUNCTION(chapter5Handler) DECLARE_NULL_FUNCTION() + +private: + void handleAction(const SavePoint &savepoint); + void handleChapter(const SavePoint &savepoint); }; } // End of namespace LastExpress diff --git a/engines/lastexpress/entities/tables.cpp b/engines/lastexpress/entities/tables.cpp index 06ea4c597c..4f8d2b954d 100644 --- a/engines/lastexpress/entities/tables.cpp +++ b/engines/lastexpress/entities/tables.cpp @@ -29,10 +29,8 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" #include "lastexpress/lastexpress.h" -#include "lastexpress/helpers.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/tables.h b/engines/lastexpress/entities/tables.h index 7fcfc0499e..c213aac978 100644 --- a/engines/lastexpress/entities/tables.h +++ b/engines/lastexpress/entities/tables.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_TABLES_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/tatiana.cpp b/engines/lastexpress/entities/tatiana.cpp index c8901b3e30..432def6253 100644 --- a/engines/lastexpress/entities/tatiana.cpp +++ b/engines/lastexpress/entities/tatiana.cpp @@ -35,10 +35,8 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" #include "lastexpress/lastexpress.h" -#include "lastexpress/helpers.h" namespace LastExpress { @@ -192,7 +190,7 @@ IMPLEMENT_FUNCTION(14, Tatiana, function14) getData()->location = kLocationInsideCompartment; getEntities()->clearSequences(kEntityTatiana); - CALLBACK_ACTION(); + callbackAction(); } break; @@ -228,7 +226,7 @@ IMPLEMENT_FUNCTION(15, Tatiana, function15) getEntities()->exitCompartment(kEntityTatiana, kObjectCompartmentB, true); getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); break; } IMPLEMENT_FUNCTION_END @@ -246,12 +244,13 @@ IMPLEMENT_FUNCTION_I(16, Tatiana, function16, uint32) getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand); getObjects()->update(kObject49, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); break; } if (params->param2) { - UPDATE_PARAM(params->param5, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param5, getState()->timeTicks, 75)) + break; params->param2 = 0; params->param3 = 1; @@ -342,7 +341,7 @@ IMPLEMENT_FUNCTION(17, Tatiana, chapter1) break; case kActionNone: - TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler); + Entity::timeCheck(kTimeChapter1, params->param1, WRAP_SETUP_FUNCTION(Tatiana, setup_chapter1Handler)); break; case kActionDefault: @@ -375,10 +374,10 @@ IMPLEMENT_FUNCTION(18, Tatiana, function18) } if (!params->param1) { - UPDATE_PARAM_PROC(params->param3, getState()->time, 4500) + if (Entity::updateParameter(params->param3, getState()->time, 4500)) { getEntities()->drawSequenceRight(kEntityTatiana, "806DS"); params->param1 = 1; - UPDATE_PARAM_PROC_END + } } } @@ -386,14 +385,14 @@ IMPLEMENT_FUNCTION(18, Tatiana, function18) getSavePoints()->push(kEntityTatiana, kEntityAlexei, kAction157159392); getEntities()->clearSequences(kEntityTatiana); - CALLBACK_ACTION(); + callbackAction(); } break; case kActionExitCompartment: getSavePoints()->push(kEntityTatiana, kEntityAlexei, kAction188784532); - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: @@ -425,27 +424,29 @@ IMPLEMENT_FUNCTION(19, Tatiana, chapter1Handler) if (getSoundQueue()->isBuffered(kEntityTatiana) || !params->param4 || params->param3 == 2 || getSoundQueue()->isBuffered("TAT1066")) goto label_tatiana_chapter1_2; - UPDATE_PARAM_PROC(params->param5, getState()->timeTicks, 450) + if (Entity::updateParameter(params->param5, getState()->timeTicks, 450)) { getSound()->playSound(kEntityTatiana, params->param3 ? "TAT1069B" : "TAT1069A"); getProgress().field_64 = 1; params->param3++; params->param5 = 0; - UPDATE_PARAM_PROC_END + } if (getEntities()->isPlayerPosition(kCarRestaurant, 71)) { - UPDATE_PARAM_PROC(params->param6, getState()->timeTicks, 75) + if (Entity::updateParameter(params->param6, getState()->timeTicks, 75)) { getSound()->playSound(kEntityTatiana, params->param3 ? "TAT1069B" : "TAT1069A"); getProgress().field_64 = 1; params->param3++; params->param6 = 0; - UPDATE_PARAM_PROC_END + } } label_tatiana_chapter1_2: - TIME_CHECK_SAVEPOINT(kTime1084500, params->param7, kEntityTatiana, kEntityPascale, kAction257489762); + Entity::timeCheckSavepoint(kTime1084500, params->param7, kEntityTatiana, kEntityPascale, kAction257489762); if (params->param1) { - UPDATE_PARAM(params->param8, getState()->timeTicks, 90); + if (!Entity::updateParameter(params->param8, getState()->timeTicks, 90)) + break; + getScenes()->loadSceneFromPosition(kCarRestaurant, 65); } else { params->param8 = 0; @@ -614,7 +615,7 @@ IMPLEMENT_FUNCTION(22, Tatiana, function22) if (params->param1 == kTimeInvalid || getState()->time <= kTime1179000) goto label_update; - UPDATE_PARAM_PROC_TIME(kTime1233000, ((!getEvent(kEventTatianaAskMatchSpeakRussian) && !getEvent(kEventTatianaAskMatch)) || getEntities()->isInGreenCarEntrance(kEntityPlayer)), params->param1, 0) + if (Entity::updateParameterTime(kTime1233000, ((!getEvent(kEventTatianaAskMatchSpeakRussian) && !getEvent(kEventTatianaAskMatch)) || getEntities()->isInGreenCarEntrance(kEntityPlayer)), params->param1, 0)) { label_update: if (!getEvent(kEventTatianaAskMatchSpeakRussian) && !getEvent(kEventTatianaAskMatch) @@ -623,7 +624,7 @@ label_update: getObjects()->update(kObject25, kEntityTatiana, kObjectLocation1, kCursorNormal, kCursorForward); getObjects()->update(kObjectTrainTimeTable, kEntityTatiana, kObjectLocation1, kCursorNormal, kCursorForward); } - UPDATE_PARAM_PROC_END + } params->param1 = kTimeInvalid; @@ -1022,7 +1023,7 @@ IMPLEMENT_FUNCTION(32, Tatiana, chapter3Handler) } if (parameters->param4 && parameters->param5) { - UPDATE_PARAM_CHECK(parameters->param4, getState()->time, 6300) + if (Entity::updateParameterCheck(parameters->param4, getState()->time, 6300)) { if (getEntities()->isSomebodyInsideRestaurantOrSalon()) { getData()->location = kLocationOutsideCompartment; @@ -1283,18 +1284,19 @@ IMPLEMENT_FUNCTION(37, Tatiana, function37) params->param3 = (uint)getState()->time + 900; if (params->param4 != kTimeInvalid && params->param3 < getState()->time) { - UPDATE_PARAM_PROC_TIME(kTime2227500, !getEntities()->isPlayerInCar(kCarRedSleeping), params->param4, 450) + if (Entity::updateParameterTime(kTime2227500, !getEntities()->isPlayerInCar(kCarRedSleeping), params->param4, 450)) { getProgress().field_5C = 1; if (getEntities()->isInsideCompartment(kEntityAnna, kCarRedSleeping, kPosition_4070)) { setup_function38(); break; } - UPDATE_PARAM_PROC_END + } } } if (params->param1) { - UPDATE_PARAM(params->param5, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param5, getState()->timeTicks, 75)) + break; getObjects()->update(kObjectCompartmentB, kEntityTatiana, kObjectLocation1, kCursorNormal, kCursorNormal); getObjects()->update(kObject49, kEntityTatiana, kObjectLocation1, kCursorNormal, kCursorNormal); @@ -1403,7 +1405,8 @@ IMPLEMENT_FUNCTION(38, Tatiana, function38) break; case kActionNone: - UPDATE_PARAM(params->param1, getState()->time, 450); + if (!Entity::updateParameter(params->param1, getState()->time, 450)) + break; getEntities()->exitCompartment(kEntityTatiana, kObjectCompartmentF, true); @@ -1535,7 +1538,7 @@ IMPLEMENT_FUNCTION(40, Tatiana, function40) if (getEntities()->isInsideTrainCar(kEntityPlayer, kCarKronos) || getData()->car != getEntityData(kEntityPlayer)->car || getEntities()->updateEntity(kEntityTatiana, kCarKronos, kPosition_9270)) - CALLBACK_ACTION(); + callbackAction(); break; case kActionExcuseMe: @@ -1547,7 +1550,7 @@ IMPLEMENT_FUNCTION(40, Tatiana, function40) case kActionDefault: if (getEntities()->updateEntity(kEntityTatiana, kCarKronos, kPosition_9270)) - CALLBACK_ACTION(); + callbackAction(); break; } IMPLEMENT_FUNCTION_END @@ -1595,7 +1598,7 @@ IMPLEMENT_FUNCTION(41, Tatiana, function41) } getEntities()->clearSequences(kEntityTatiana); - CALLBACK_ACTION(); + callbackAction(); } break; @@ -1629,7 +1632,7 @@ IMPLEMENT_FUNCTION(41, Tatiana, function41) case 6: getEntities()->clearSequences(kEntityTatiana); - CALLBACK_ACTION(); + callbackAction(); break; case 4: @@ -1952,7 +1955,8 @@ IMPLEMENT_FUNCTION(48, Tatiana, function48) if (!params->param1 || getSoundQueue()->isBuffered(kEntityTatiana)) goto label_end; - UPDATE_PARAM_GOTO(params->param2, getState()->timeTicks, 5 * (3 * rnd(5) + 30), label_end); + if (!Entity::updateParameter(params->param2, getState()->timeTicks, 5 * (3 * rnd(5) + 30))) + goto label_end; getSound()->playSound(kEntityTatiana, "LIB012", kFlagDefault); params->param2 = 0; @@ -2199,7 +2203,8 @@ IMPLEMENT_FUNCTION(54, Tatiana, function54) } if (params->param1 > 3) { - UPDATE_PARAM(params->param3, getState()->timeTicks, 225); + if (!Entity::updateParameter(params->param3, getState()->timeTicks, 225)) + break; params->param1 = 0; params->param3 = 0; diff --git a/engines/lastexpress/entities/tatiana.h b/engines/lastexpress/entities/tatiana.h index c457d49b1e..8ec69db5e9 100644 --- a/engines/lastexpress/entities/tatiana.h +++ b/engines/lastexpress/entities/tatiana.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_TATIANA_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/train.cpp b/engines/lastexpress/entities/train.cpp index bced1da62b..e3f530ef25 100644 --- a/engines/lastexpress/entities/train.cpp +++ b/engines/lastexpress/entities/train.cpp @@ -32,10 +32,8 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" #include "lastexpress/lastexpress.h" -#include "lastexpress/helpers.h" namespace LastExpress { @@ -269,18 +267,20 @@ IMPLEMENT_FUNCTION(8, Train, process) if ((getEntities()->isPlayerInCar(kCarGreenSleeping) || getEntities()->isPlayerInCar(kCarRedSleeping)) && params->param4 && !params->param5) { - params->param4 -= 1; + params->param4 -= 1; - if (!params->param4 && getProgress().jacket == kJacketGreen) { + if (!params->param4 && getProgress().jacket == kJacketGreen) { - getAction()->playAnimation(isNight() ? kEventCathSmokeNight : kEventCathSmokeDay); - params->param5 = 1; - getScenes()->processScene(); - } + getAction()->playAnimation(isNight() ? kEventCathSmokeNight : kEventCathSmokeDay); + params->param5 = 1; + getScenes()->processScene(); + } } if (params->param6) { - UPDATE_PARAM_GOTO(params1->param7, getState()->time, 900, label_process); + if (!Entity::updateParameter(params1->param7, getState()->time, 900)) + goto label_process; + getScenes()->loadSceneFromPosition(kCarRestaurant, 58); } @@ -552,7 +552,7 @@ void Train::handleCompartmentAction() { ENTITY_PARAM(0, 8) = params->param1; - CALLBACK_ACTION(); + callbackAction(); } ////////////////////////////////////////////////////////////////////////// diff --git a/engines/lastexpress/entities/train.h b/engines/lastexpress/entities/train.h index ea9e1d7a07..4b8bc10c1a 100644 --- a/engines/lastexpress/entities/train.h +++ b/engines/lastexpress/entities/train.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_TRAIN_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/vassili.cpp b/engines/lastexpress/entities/vassili.cpp index 22f41afa92..4695f8831f 100644 --- a/engines/lastexpress/entities/vassili.cpp +++ b/engines/lastexpress/entities/vassili.cpp @@ -35,10 +35,8 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" #include "lastexpress/lastexpress.h" -#include "lastexpress/helpers.h" namespace LastExpress { @@ -85,7 +83,7 @@ IMPLEMENT_FUNCTION(4, Vassili, chapter1) break; case kActionNone: - TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler); + Entity::timeCheck(kTimeChapter1, params->param1, WRAP_SETUP_FUNCTION(Vassili, setup_chapter1Handler)); break; case kActionDefault: @@ -146,7 +144,8 @@ IMPLEMENT_FUNCTION(6, Vassili, function6) case kActionNone: if (getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_8200)) { - UPDATE_PARAM_GOTO(params->param3, getState()->timeTicks, params->param1, label_function7); + if (!Entity::updateParameter(params->param3, getState()->timeTicks, params->param1)) + goto label_function7; setCallback(1); setup_draw("303B"); @@ -402,7 +401,8 @@ IMPLEMENT_FUNCTION(13, Vassili, sleeping) case kActionNone: if (getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_8200)) { - UPDATE_PARAM(params->param3, getState()->timeTicks, params->param1); + if (!Entity::updateParameter(params->param3, getState()->timeTicks, params->param1)) + break; setCallback(1); setup_draw("303B"); @@ -461,7 +461,8 @@ IMPLEMENT_FUNCTION(15, Vassili, stealEgg) case kActionNone: if (getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_8200)) { - UPDATE_PARAM(params->param3, getState()->timeTicks, params->param1); + if (!Entity::updateParameter(params->param3, getState()->timeTicks, params->param1)) + break; setCallback(1); setup_draw("303B"); @@ -545,7 +546,8 @@ IMPLEMENT_FUNCTION(17, Vassili, chapter4Handler) case kActionNone: if (getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_8200)) { - UPDATE_PARAM(params->param3, getState()->timeTicks, params->param1); + if (!Entity::updateParameter(params->param3, getState()->timeTicks, params->param1)) + break; setCallback(1); setup_draw("303B"); diff --git a/engines/lastexpress/entities/vassili.h b/engines/lastexpress/entities/vassili.h index 843a065174..d006770f7b 100644 --- a/engines/lastexpress/entities/vassili.h +++ b/engines/lastexpress/entities/vassili.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_VASSILI_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/verges.cpp b/engines/lastexpress/entities/verges.cpp index 8246f85145..867f122d8f 100644 --- a/engines/lastexpress/entities/verges.cpp +++ b/engines/lastexpress/entities/verges.cpp @@ -32,10 +32,8 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" #include "lastexpress/lastexpress.h" -#include "lastexpress/helpers.h" namespace LastExpress { @@ -102,11 +100,11 @@ IMPLEMENT_FUNCTION(3, Verges, callbackActionOnDirection) case kActionNone: if (getData()->direction != kDirectionRight) - CALLBACK_ACTION(); + callbackAction(); break; case kActionExitCompartment: - CALLBACK_ACTION(); + callbackAction(); break; case kActionExcuseMeCath: @@ -219,7 +217,7 @@ switch (savepoint.action) { break; case 6: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -241,12 +239,13 @@ IMPLEMENT_FUNCTION_IIS(10, Verges, function10, CarIndex, EntityPosition) } if (getEntities()->updateEntity(kEntityVerges, (CarIndex)params->param1, (EntityPosition)params->param2)) { - CALLBACK_ACTION(); + callbackAction(); break; } if (params->param6) { - UPDATE_PARAM(params->param8, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param8, getState()->timeTicks, 75)) + break; getSound()->playSound(kEntityVerges, (char *)¶ms->seq); @@ -266,7 +265,7 @@ IMPLEMENT_FUNCTION_IIS(10, Verges, function10, CarIndex, EntityPosition) } if (getEntities()->updateEntity(kEntityVerges, (CarIndex)params->param1, (EntityPosition)params->param2)) - CALLBACK_ACTION(); + callbackAction(); break; } IMPLEMENT_FUNCTION_END @@ -337,7 +336,7 @@ IMPLEMENT_FUNCTION(11, Verges, function11) getObjects()->update(kObject104, kEntityVerges, kObjectLocationNone, kCursorNormal, kCursorHand); getObjects()->update(kObject105, kEntityVerges, kObjectLocationNone, kCursorNormal, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); break; } } @@ -397,7 +396,7 @@ IMPLEMENT_FUNCTION(12, Verges, function12) getData()->entityPosition = kPosition_850; getEntities()->clearSequences(kEntityVerges); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -438,7 +437,7 @@ IMPLEMENT_FUNCTION_I(13, Verges, function13, bool) getEntities()->clearSequences(kEntityVerges); getScenes()->loadSceneFromPosition(kCarBaggage, 91); - CALLBACK_ACTION(); + callbackAction(); } break; } @@ -462,7 +461,7 @@ IMPLEMENT_FUNCTION_IS(15, Verges, function15, EntityIndex) if (!getEntities()->isPlayerPosition(kCarGreenSleeping, 2) && !getEntities()->isPlayerPosition(kCarRedSleeping, 2)) getData()->entityPosition = kPosition_2088; - CALLBACK_ACTION(); + callbackAction(); } break; @@ -499,7 +498,7 @@ IMPLEMENT_FUNCTION_ISS(16, Verges, function16, EntityIndex) if (!getEntities()->isPlayerPosition(kCarGreenSleeping, 2) && !getEntities()->isPlayerPosition(kCarRedSleeping, 2)) getData()->entityPosition = kPosition_2088; - CALLBACK_ACTION(); + callbackAction(); } break; @@ -559,7 +558,7 @@ IMPLEMENT_FUNCTION(17, Verges, function17) case 4: ENTITY_PARAM(0, 3) = 0; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -573,7 +572,7 @@ IMPLEMENT_FUNCTION(18, Verges, chapter1) break; case kActionNone: - TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler); + Entity::timeCheck(kTimeChapter1, params->param1, WRAP_SETUP_FUNCTION(Verges, setup_chapter1Handler)); break; case kActionDefault: @@ -651,7 +650,7 @@ IMPLEMENT_FUNCTION(22, Verges, function22) break; case 5: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -697,7 +696,7 @@ IMPLEMENT_FUNCTION(24, Verges, policeGettingOffTrain) break; case kActionEndSound: - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: @@ -818,7 +817,7 @@ IMPLEMENT_FUNCTION(25, Verges, function25) case 11: ENTITY_PARAM(0, 7) = 0; - CALLBACK_ACTION(); + callbackAction(); break; case 6: @@ -908,10 +907,12 @@ label_callback3: if (params->param6) goto label_callback12; - TIME_CHECK_CALLBACK_1(kTimeChapter1, params->param7, 4, setup_function9, "TRA1001"); + if (Entity::timeCheckCallback(kTimeChapter1, params->param7, 4, "TRA1001", WRAP_SETUP_FUNCTION_S(Verges, setup_function9))) + break; label_callback4: - TIME_CHECK_CALLBACK(kTime1089000, params->param8, 5, setup_function12); + if (Entity::timeCheckCallback(kTime1089000, params->param8, 5, WRAP_SETUP_FUNCTION(Verges, setup_function12))) + break; params->param8 = 1; @@ -922,16 +923,20 @@ label_callback4: } label_callback8: - TIME_CHECK_CALLBACK_1(kTime1107000, CURRENT_PARAM(1, 1), 9, setup_function9, "TRA1001A"); + if (Entity::timeCheckCallback(kTime1107000, CURRENT_PARAM(1, 1), 9, "TRA1001A", WRAP_SETUP_FUNCTION_S(Verges, setup_function9))) + break; label_callback9: - TIME_CHECK_CALLBACK_1(kTime1134000, CURRENT_PARAM(1, 2), 10, setup_function9, "TRA1002"); + if (Entity::timeCheckCallback(kTime1134000, CURRENT_PARAM(1, 2), 10, "TRA1002", WRAP_SETUP_FUNCTION_S(Verges, setup_function9))) + break; label_callback10: - TIME_CHECK_CALLBACK_1(kTime1165500, CURRENT_PARAM(1, 3), 11, setup_function9, "TRA1003"); + if (Entity::timeCheckCallback(kTime1165500, CURRENT_PARAM(1, 3), 11, "TRA1003", WRAP_SETUP_FUNCTION_S(Verges, setup_function9))) + break; label_callback11: - TIME_CHECK_CALLBACK_1(kTime1225800, CURRENT_PARAM(1, 4), 12, setup_function9, "TRA1004"); + if (Entity::timeCheckCallback(kTime1225800, CURRENT_PARAM(1, 4), 12, "TRA1004", WRAP_SETUP_FUNCTION_S(Verges, setup_function9))) + break; label_callback12: if (ENTITY_PARAM(0, 5) && !params->param2) { @@ -1083,7 +1088,8 @@ IMPLEMENT_FUNCTION(28, Verges, chapter2Handler) } label_callback_1: - TIME_CHECK_CALLBACK_1(kTime1818900, params->param1, 2, setup_function9, "Tra2177"); + if (Entity::timeCheckCallback(kTime1818900, params->param1, 2, "Tra2177", WRAP_SETUP_FUNCTION_S(Verges, setup_function9))) + break; label_callback_2: if (params->param2 == kTimeInvalid || !getState()->time) @@ -1224,7 +1230,7 @@ IMPLEMENT_FUNCTION_S(30, Verges, function30) break; case 4: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1266,7 +1272,7 @@ IMPLEMENT_FUNCTION(31, Verges, function31) getProgress().field_48 = 1; ENTITY_PARAM(0, 4) = 0; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1280,7 +1286,12 @@ IMPLEMENT_FUNCTION(32, Verges, function32) break; case kActionNone: - TIME_CHECK_CALLBACK_3(kTime2263500, params->param1, 5, setup_function10, kCarRedSleeping, kPosition_9460, "TRA3006"); + if (getState()->time > kTime2263500 && !params->param1) { + params->param1 = 1; + setCallback(5); + setup_function10(kCarRedSleeping, kPosition_9460, "TRA3006"); + break; + } break; case kActionDefault: @@ -1343,7 +1354,7 @@ IMPLEMENT_FUNCTION(32, Verges, function32) break; case 6: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1440,25 +1451,31 @@ label_callback_2: } label_callback_3: - TIME_CHECK_CALLBACK_1(kTime1971000, params->param1, 4, setup_function9, "Tra3001"); + if (Entity::timeCheckCallback(kTime1971000, params->param1, 4, "Tra3001", WRAP_SETUP_FUNCTION_S(Verges, setup_function9))) + break; label_callback_4: - TIME_CHECK_CALLBACK_1(kTime1998000, params->param2, 5, setup_function9, "Tra3010a"); + if (Entity::timeCheckCallback(kTime1998000, params->param2, 5, "Tra3010a", WRAP_SETUP_FUNCTION_S(Verges, setup_function9))) + break; label_callback_5: - TIME_CHECK_CALLBACK(kTime2016000, params->param3, 6, setup_function35); + if (Entity::timeCheckCallback(kTime2016000, params->param3, 6, WRAP_SETUP_FUNCTION(Verges, setup_function35))) + break; label_callback_6: - TIME_CHECK_CALLBACK_1(kTime2070000, params->param4, 7, setup_function9, "Tra3002"); + if (Entity::timeCheckCallback(kTime2070000, params->param4, 7, "Tra3002", WRAP_SETUP_FUNCTION_S(Verges, setup_function9))) + break; label_callback_7: - TIME_CHECK_CALLBACK_1(kTime2142000, params->param5, 8, setup_function9, "Tra3003"); + if (Entity::timeCheckCallback(kTime2142000, params->param5, 8, "Tra3003", WRAP_SETUP_FUNCTION_S(Verges, setup_function9))) + break; label_callback_8: - TIME_CHECK_CALLBACK_1(kTime2173500, params->param6, 9, setup_function30, "Tra3012"); + if (Entity::timeCheckCallback(kTime2173500, params->param6, 9, "Tra3012", WRAP_SETUP_FUNCTION_S(Verges, setup_function30))) + break; label_callback_9: - TIME_CHECK_CALLBACK(kTime2218500, params->param7, 10, setup_function32); + Entity::timeCheckCallback(kTime2218500, params->param7, 10, WRAP_SETUP_FUNCTION(Verges, setup_function32)); break; case kActionOpenDoor: @@ -1548,7 +1565,7 @@ IMPLEMENT_FUNCTION(35, Verges, function35) break; case 6: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1605,27 +1622,32 @@ label_callback_1: } label_callback_2: - TIME_CHECK_CALLBACK_1(kTime2349000, params->param1, 3, setup_function9, "Tra1001"); + if (Entity::timeCheckCallback(kTime2349000, params->param1, 3, "Tra1001", WRAP_SETUP_FUNCTION_S(Verges, setup_function9))) + break; label_callback_3: - TIME_CHECK_CALLBACK_1(kTime2378700, params->param2, 4, setup_function9, "Tra4001"); + if (Entity::timeCheckCallback(kTime2378700, params->param2, 4, "Tra4001", WRAP_SETUP_FUNCTION_S(Verges, setup_function9))) + break; label_callback_4: - TIME_CHECK_CALLBACK_1(kTime2403000, params->param3, 5, setup_function9, "Tra1001A"); + if (Entity::timeCheckCallback(kTime2403000, params->param3, 5, "Tra1001A", WRAP_SETUP_FUNCTION_S(Verges, setup_function9))) + break; label_callback_5: - TIME_CHECK_CALLBACK_1(kTime2414700, params->param4, 6, setup_function9, "Tra4002"); + if (Entity::timeCheckCallback(kTime2414700, params->param4, 6, "Tra4002", WRAP_SETUP_FUNCTION_S(Verges, setup_function9))) + break; label_callback_6: - TIME_CHECK_CALLBACK_1(kTime2484000, params->param5, 7, setup_function9, "Tra4003"); + if (Entity::timeCheckCallback(kTime2484000, params->param5, 7, "Tra4003", WRAP_SETUP_FUNCTION_S(Verges, setup_function9))) + break; label_callback_7: - TIME_CHECK_CALLBACK_1(kTime2511000, params->param6, 8, setup_function9, "Tra4004"); + if (Entity::timeCheckCallback(kTime2511000, params->param6, 8, "Tra4004", WRAP_SETUP_FUNCTION_S(Verges, setup_function9))) + break; } label_callback_8: - TIME_CHECK_CALLBACK_1(kTime2538000, params->param7, 9, setup_function9, "Tra4005"); - + Entity::timeCheckCallback(kTime2538000, params->param7, 9, "Tra4005", WRAP_SETUP_FUNCTION_S(Verges, setup_function9)); break; case kActionOpenDoor: @@ -1887,7 +1909,7 @@ void Verges::talk(const SavePoint &savepoint, const char *sound1, const char *so break; case 6: - CALLBACK_ACTION(); + callbackAction(); break; } break; diff --git a/engines/lastexpress/entities/verges.h b/engines/lastexpress/entities/verges.h index 17a3c8ac40..82381043d3 100644 --- a/engines/lastexpress/entities/verges.h +++ b/engines/lastexpress/entities/verges.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_VERGES_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/vesna.cpp b/engines/lastexpress/entities/vesna.cpp index 7a1f1d3195..f29bce8b2b 100644 --- a/engines/lastexpress/entities/vesna.cpp +++ b/engines/lastexpress/entities/vesna.cpp @@ -32,10 +32,7 @@ #include "lastexpress/game/scenes.h" #include "lastexpress/game/state.h" -#include "lastexpress/sound/sound.h" - #include "lastexpress/lastexpress.h" -#include "lastexpress/helpers.h" namespace LastExpress { @@ -134,7 +131,7 @@ IMPLEMENT_FUNCTION_II(7, Vesna, updateEntity2, CarIndex, EntityPosition) break; case kAction123668192: - CALLBACK_ACTION(); + callbackAction(); break; } IMPLEMENT_FUNCTION_END @@ -165,7 +162,8 @@ IMPLEMENT_FUNCTION(11, Vesna, function11) case kActionNone: if (parameters->param3) { - UPDATE_PARAM(parameters->param7, getState()->timeTicks, 75); + if (!Entity::updateParameter(parameters->param7, getState()->timeTicks, 75)) + break; parameters->param2 = 1; parameters->param3 = 0; @@ -245,7 +243,7 @@ IMPLEMENT_FUNCTION(11, Vesna, function11) case kAction55996766: case kAction101687594: - CALLBACK_ACTION(); + callbackAction(); break; } IMPLEMENT_FUNCTION_END @@ -257,7 +255,7 @@ IMPLEMENT_FUNCTION(12, Vesna, chapter1) break; case kActionNone: - TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler); + Entity::timeCheck(kTimeChapter1, params->param1, WRAP_SETUP_FUNCTION(Vesna, setup_chapter1Handler)); break; case kActionDefault: @@ -458,7 +456,7 @@ IMPLEMENT_FUNCTION(18, Vesna, function18) getData()->location = kLocationInsideCompartment; getEntities()->clearSequences(kEntityVesna); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -514,7 +512,8 @@ IMPLEMENT_FUNCTION(20, Vesna, chapter3Handler) } if (parameters->param2) { - UPDATE_PARAM(parameters->param8, getState()->timeTicks, 75); + if (!Entity::updateParameter(parameters->param8, getState()->timeTicks, 75)) + break; parameters->param1 = 1; parameters->param2 = 0; @@ -711,7 +710,7 @@ IMPLEMENT_FUNCTION(21, Vesna, function21) getData()->location = kLocationInsideCompartment; getEntities()->clearSequences(kEntityVesna); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1083,13 +1082,14 @@ IMPLEMENT_FUNCTION(30, Vesna, function30) case kActionNone: if (!params->param1) { - UPDATE_PARAM_PROC(params->param3, getState()->timeTicks, 120) + if (Entity::updateParameter(params->param3, getState()->timeTicks, 120)) { getSound()->playSound(kEntityVesna, "Ves50001", kFlagDefault); params->param1 = 1; - UPDATE_PARAM_PROC_END + } } - UPDATE_PARAM(params->param4, getState()->timeTicks, 180); + if (!Entity::updateParameter(params->param4, getState()->timeTicks, 180)) + break; setCallback(1); setup_savegame(kSavegameTypeEvent, kEventCathVesnaTrainTopKilled); diff --git a/engines/lastexpress/entities/vesna.h b/engines/lastexpress/entities/vesna.h index a09664096d..025d45882e 100644 --- a/engines/lastexpress/entities/vesna.h +++ b/engines/lastexpress/entities/vesna.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_VESNA_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/yasmin.cpp b/engines/lastexpress/entities/yasmin.cpp index 45e5e11568..1d280f51e0 100644 --- a/engines/lastexpress/entities/yasmin.cpp +++ b/engines/lastexpress/entities/yasmin.cpp @@ -28,10 +28,8 @@ #include "lastexpress/game/savepoint.h" #include "lastexpress/game/state.h" -#include "lastexpress/sound/sound.h" #include "lastexpress/lastexpress.h" -#include "lastexpress/helpers.h" namespace LastExpress { @@ -132,7 +130,7 @@ IMPLEMENT_FUNCTION(6, Yasmin, function6) getData()->location = kLocationInsideCompartment; getEntities()->clearSequences(kEntityYasmin); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -172,7 +170,7 @@ IMPLEMENT_FUNCTION(7, Yasmin, function7) getData()->location = kLocationInsideCompartment; getEntities()->clearSequences(kEntityYasmin); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -186,7 +184,7 @@ IMPLEMENT_FUNCTION(8, Yasmin, chapter1) break; case kActionNone: - TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler); + Entity::timeCheck(kTimeChapter1, params->param1, WRAP_SETUP_FUNCTION(Yasmin, setup_chapter1Handler)); break; case kActionDefault: @@ -204,12 +202,22 @@ IMPLEMENT_FUNCTION(9, Yasmin, chapter1Handler) break; case kActionNone: - TIME_CHECK_CALLBACK(kTime1093500, params->param1, 1, setup_function6); - TIME_CHECK_CALLBACK(kTime1161000, params->param2, 3, setup_function7); - TIME_CHECK_PLAYSOUND_UPDATEPOSITION(kTime1162800, params->param3, 4, "Har1102", kPosition_4070); - TIME_CHECK_CALLBACK_1(kTime1165500, params->param4, 5, setup_playSound, "Har1104"); - TIME_CHECK_CALLBACK_1(kTime1174500, params->param5, 6, setup_playSound, "Har1106"); - TIME_CHECK_CALLBACK(kTime1183500, params->param6, 7, setup_function6); + if (Entity::timeCheckCallback(kTime1093500, params->param1, 1, WRAP_SETUP_FUNCTION(Yasmin, setup_function6))) + break; + + if (Entity::timeCheckCallback(kTime1161000, params->param2, 3, WRAP_SETUP_FUNCTION(Yasmin, setup_function7))) + break; + + if (Entity::timeCheckPlaySoundUpdatePosition(kTime1162800, params->param3, 4, "Har1102", kPosition_4070)) + break; + + if (Entity::timeCheckCallback(kTime1165500, params->param4, 5, "Har1104", WRAP_SETUP_FUNCTION_S(Yasmin, setup_playSound))) + break; + + if (Entity::timeCheckCallback(kTime1174500, params->param5, 6, "Har1106", WRAP_SETUP_FUNCTION_S(Yasmin, setup_playSound))) + break; + + Entity::timeCheckCallback(kTime1183500, params->param6, 7, WRAP_SETUP_FUNCTION(Yasmin, setup_function6)); break; case kActionCallback: @@ -224,23 +232,27 @@ IMPLEMENT_FUNCTION(9, Yasmin, chapter1Handler) break; case 2: - TIME_CHECK_CALLBACK(kTime1161000, params->param2, 3, setup_function7); + if (Entity::timeCheckCallback(kTime1161000, params->param2, 3, WRAP_SETUP_FUNCTION(Yasmin, setup_function7))) + break; // Fallback to case 3 case 3: - TIME_CHECK_PLAYSOUND_UPDATEPOSITION(kTime1162800, params->param3, 4, "Har1102", kPosition_4070); + if (Entity::timeCheckPlaySoundUpdatePosition(kTime1162800, params->param3, 4, "Har1102", kPosition_4070)) + break; // Fallback to case 4 case 4: - TIME_CHECK_CALLBACK_1(kTime1165500, params->param4, 5, setup_playSound, "Har1104"); + if (Entity::timeCheckCallback(kTime1165500, params->param4, 5, "Har1104", WRAP_SETUP_FUNCTION_S(Yasmin, setup_playSound))) + break; // Fallback to case 5 case 5: - TIME_CHECK_CALLBACK_1(kTime1174500, params->param5, 6, setup_playSound, "Har1106"); + if (Entity::timeCheckCallback(kTime1174500, params->param5, 6, "Har1106", WRAP_SETUP_FUNCTION_S(Yasmin, setup_playSound))) + break; // Fallback to case 6 case 6: - TIME_CHECK_CALLBACK(kTime1183500, params->param6, 7, setup_function6); + Entity::timeCheckCallback(kTime1183500, params->param6, 7, WRAP_SETUP_FUNCTION(Yasmin, setup_function6)); break; } break; @@ -281,7 +293,8 @@ IMPLEMENT_FUNCTION(12, Yasmin, chapter2Handler) break; case kActionNone: - TIME_CHECK_CALLBACK(kTime1759500, params->param1, 1, setup_function7); + if (Entity::timeCheckCallback(kTime1759500, params->param1, 1, WRAP_SETUP_FUNCTION(Yasmin, setup_function7))) + break; if (getState()->time > kTime1800000 && !params->param2) { params->param2 = 1; @@ -334,9 +347,13 @@ IMPLEMENT_FUNCTION(14, Yasmin, chapter3Handler) break; case kActionNone: - TIME_CHECK_CALLBACK(kTime2062800, params->param1, 1, setup_function6); - TIME_CHECK_CALLBACK(kTime2106000, params->param2, 2, setup_function7); - TIME_CHECK_CALLBACK(kTime2160000, params->param3, 3, setup_function6); + if (Entity::timeCheckCallback(kTime2062800, params->param1, 1, WRAP_SETUP_FUNCTION(Yasmin, setup_function6))) + break; + + if (Entity::timeCheckCallback(kTime2106000, params->param2, 2, WRAP_SETUP_FUNCTION(Yasmin, setup_function7))) + break; + + Entity::timeCheckCallback(kTime2160000, params->param3, 3, WRAP_SETUP_FUNCTION(Yasmin, setup_function6)); break; case kActionCallback: @@ -345,11 +362,12 @@ IMPLEMENT_FUNCTION(14, Yasmin, chapter3Handler) break; case 1: - TIME_CHECK_CALLBACK(kTime2106000, params->param2, 2, setup_function7); + if (Entity::timeCheckCallback(kTime2106000, params->param2, 2, WRAP_SETUP_FUNCTION(Yasmin, setup_function7))) + break; // Fallback to case 2 case 2: - TIME_CHECK_CALLBACK(kTime2160000, params->param3, 3, setup_function6); + Entity::timeCheckCallback(kTime2160000, params->param3, 3, WRAP_SETUP_FUNCTION(Yasmin, setup_function6)); break; } break; @@ -381,8 +399,10 @@ IMPLEMENT_FUNCTION(16, Yasmin, chapter4Handler) break; case kActionNone: - TIME_CHECK_CALLBACK(kTime2457000, params->param1, 1, setup_function7); - TIME_CHECK_CALLBACK(kTime2479500, params->param2, 3, setup_function6); + if (Entity::timeCheckCallback(kTime2457000, params->param1, 1, WRAP_SETUP_FUNCTION(Yasmin, setup_function7))) + break; + + Entity::timeCheckCallback(kTime2479500, params->param2, 3, WRAP_SETUP_FUNCTION(Yasmin, setup_function6)); break; case kActionCallback: @@ -397,7 +417,7 @@ IMPLEMENT_FUNCTION(16, Yasmin, chapter4Handler) break; case 2: - TIME_CHECK_CALLBACK(kTime2479500, params->param2, 3, setup_function6); + Entity::timeCheckCallback(kTime2479500, params->param2, 3, WRAP_SETUP_FUNCTION(Yasmin, setup_function6)); break; } break; @@ -445,7 +465,9 @@ IMPLEMENT_FUNCTION(20, Yasmin, function20) break; case kActionNone: - UPDATE_PARAM(params->param1, getState()->time, 2700); + if (!Entity::updateParameter(params->param1, getState()->time, 2700)) + break; + setup_function21(); break; @@ -472,7 +494,7 @@ IMPLEMENT_FUNCTION(21, Yasmin, function21) case kActionNone: case kActionDefault: if (getEntities()->updateEntity(kEntityYasmin, (CarIndex)params->param1, (EntityPosition)params->param2)) - CALLBACK_ACTION(); + callbackAction(); break; case kActionExcuseMeCath: diff --git a/engines/lastexpress/entities/yasmin.h b/engines/lastexpress/entities/yasmin.h index e943a8b158..b1413f5c2f 100644 --- a/engines/lastexpress/entities/yasmin.h +++ b/engines/lastexpress/entities/yasmin.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_YASMIN_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/fight/fight.cpp b/engines/lastexpress/fight/fight.cpp index b832d46a60..b00c1732e7 100644 --- a/engines/lastexpress/fight/fight.cpp +++ b/engines/lastexpress/fight/fight.cpp @@ -31,6 +31,7 @@ #include "lastexpress/data/cursor.h" #include "lastexpress/data/sequence.h" +#include "lastexpress/game/entities.h" #include "lastexpress/game/inventory.h" #include "lastexpress/game/logic.h" #include "lastexpress/game/object.h" @@ -38,6 +39,7 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" +#include "lastexpress/sound/sound.h" #include "lastexpress/graphics.h" #include "lastexpress/helpers.h" @@ -53,6 +55,8 @@ Fight::FightData::FightData() { index = 0; isFightRunning = false; + + memset(&sequences, 0, sizeof(sequences)); } Fight::FightData::~FightData() { @@ -399,6 +403,9 @@ end_load: } void Fight::setOpponents() { + if (!_data) + error("[Fight::setOpponents] Data not initialized"); + _data->player->setOpponent(_data->opponent); _data->opponent->setOpponent(_data->player); diff --git a/engines/lastexpress/fight/fighter.cpp b/engines/lastexpress/fight/fighter.cpp index bae7728a2b..4b1cddabd4 100644 --- a/engines/lastexpress/fight/fighter.cpp +++ b/engines/lastexpress/fight/fighter.cpp @@ -53,20 +53,20 @@ Fighter::Fighter(LastExpressEngine *engine) : _engine(engine) { } Fighter::~Fighter() { - clearSequences(); -} - -////////////////////////////////////////////////////////////////////////// -// Cleanup -////////////////////////////////////////////////////////////////////////// -void Fighter::clearSequences() { // The original game resets the function pointers to default values, just before deleting the struct getScenes()->removeAndRedraw(&_frame, false); // Free sequences - for (int i = 0; i < (int)_sequences.size(); i++) + for (uint i = 0; i < _sequences.size(); i++) SAFE_DELETE(_sequences[i]); + + // Zero-out passed pointers + _sequence = NULL; + _opponent = NULL; + _fight = NULL; + + _engine = NULL; } ////////////////////////////////////////////////////////////////////////// @@ -113,6 +113,9 @@ void Fighter::draw() { // Processing ////////////////////////////////////////////////////////////////////////// void Fighter::process() { + if (!_fight) + error("[Fighter::handleAction] Fighter not initialized properly"); + if (!_sequence) { if (_frame) { getScenes()->removeFromQueue(_frame); @@ -188,6 +191,9 @@ void Fighter::process() { // Default actions ////////////////////////////////////////////////////////////////////////// void Fighter::handleAction(FightAction action) { + if (!_opponent || !_fight) + error("[Fighter::handleAction] Fighter not initialized properly"); + switch (action) { default: return; @@ -243,7 +249,10 @@ void Opponent::update() { // Helpers ////////////////////////////////////////////////////////////////////////// bool Fighter::checkFrame(uint32 val) { - return (_frame->getInfo()->field_33 & val); + if (!_frame) + error("[Fighter::checkFrame] Invalid current frame"); + + return (bool)(_frame->getInfo()->field_33 & val); } } // End of namespace LastExpress diff --git a/engines/lastexpress/fight/fighter.h b/engines/lastexpress/fight/fighter.h index e37fe49d86..dad95af186 100644 --- a/engines/lastexpress/fight/fighter.h +++ b/engines/lastexpress/fight/fighter.h @@ -99,9 +99,6 @@ protected: void draw(); void process(); - // Cleanup - void clearSequences(); - // Helpers bool checkFrame(uint32 val); }; diff --git a/engines/lastexpress/game/action.cpp b/engines/lastexpress/game/action.cpp index 98d74dd1a7..60a309518a 100644 --- a/engines/lastexpress/game/action.cpp +++ b/engines/lastexpress/game/action.cpp @@ -29,7 +29,6 @@ #include "lastexpress/entities/abbot.h" #include "lastexpress/entities/anna.h" -#include "lastexpress/entities/entity.h" #include "lastexpress/game/beetle.h" #include "lastexpress/game/entities.h" @@ -42,9 +41,7 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" #include "lastexpress/resource.h" @@ -332,6 +329,22 @@ static const struct { {"8042A", 600} }; +template<class Arg, class Res, class T> +class Functor1MemConst : public Common::Functor1<Arg, Res> { +public: + typedef Res (T::*FuncType)(Arg) const; + + Functor1MemConst(T *t, const FuncType &func) : _t(t), _func(func) {} + + bool isValid() const { return _func != 0 && _t != 0; } + Res operator()(Arg v1) const { + return (_t->*_func)(v1); + } +private: + mutable T *_t; + const FuncType _func; +}; + Action::Action(LastExpressEngine *engine) : _engine(engine) { ADD_ACTION(dummy); ADD_ACTION(inventory); @@ -407,7 +420,7 @@ SceneIndex Action::processHotspot(const SceneHotspot &hotspot) { ////////////////////////////////////////////////////////////////////////// // Action 0 IMPLEMENT_ACTION(dummy) - warning("[Action::action_dummy] Dummy action function called (hotspot action: %d)", hotspot.action); + error("[Action::action_dummy] Dummy action function called (hotspot action: %d)", hotspot.action); return kSceneInvalid; } @@ -1742,14 +1755,14 @@ CursorStyle Action::getCursor(const SceneHotspot &hotspot) const { return kCursorBackward; case SceneHotspot::kActionKnockOnDoor: - warning("================================= DOOR %03d =================================", object); + debugC(2, kLastExpressDebugScenes, "================================= DOOR %03d =================================", object); if (object >= kObjectMax) return kCursorNormal; else return (CursorStyle)getObjects()->get(object).cursor; case SceneHotspot::kAction12: - warning("================================= OBJECT %03d =================================", object); + debugC(2, kLastExpressDebugScenes, "================================= OBJECT %03d =================================", object); if (object >= kObjectMax) return kCursorNormal; @@ -1759,7 +1772,7 @@ CursorStyle Action::getCursor(const SceneHotspot &hotspot) const { return kCursorNormal; case SceneHotspot::kActionPickItem: - warning("================================= ITEM %03d =================================", object); + debugC(2, kLastExpressDebugScenes, "================================= ITEM %03d =================================", object); if (object >= kObjectCompartmentA) return kCursorNormal; @@ -1770,7 +1783,7 @@ CursorStyle Action::getCursor(const SceneHotspot &hotspot) const { return kCursorNormal; case SceneHotspot::kActionDropItem: - warning("================================= ITEM %03d =================================", object); + debugC(2, kLastExpressDebugScenes, "================================= ITEM %03d =================================", object); if (object >= kObjectCompartmentA) return kCursorNormal; @@ -1887,7 +1900,7 @@ LABEL_KEY: // Handle compartment action case SceneHotspot::kActionCompartment: case SceneHotspot::kActionExitCompartment: - warning("================================= DOOR %03d =================================", object); + debugC(2, kLastExpressDebugScenes, "================================= DOOR %03d =================================", object); if (object >= kObjectMax) return kCursorNormal; diff --git a/engines/lastexpress/game/beetle.cpp b/engines/lastexpress/game/beetle.cpp index ab707ddae9..2a72459697 100644 --- a/engines/lastexpress/game/beetle.cpp +++ b/engines/lastexpress/game/beetle.cpp @@ -27,7 +27,6 @@ #include "lastexpress/game/scenes.h" #include "lastexpress/game/state.h" -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" #include "lastexpress/resource.h" @@ -337,26 +336,13 @@ void Beetle::drawUpdate() { } } -#define INVERT_Y() \ - switch (_data->indexes[_data->offset]) { \ - default: \ - break; \ - case 24: \ - case 25: \ - case 26: \ - case 27: \ - case 28: \ - _data->coordY = -_data->coordY; \ - break; \ - } - // Invert direction - INVERT_Y(); + invertDirection(); SequenceFrame *frame = new SequenceFrame(_data->currentSequence, (uint16)_data->currentFrame); updateFrame(frame); - INVERT_Y(); + invertDirection(); getScenes()->addToQueue(frame); @@ -364,6 +350,21 @@ void Beetle::drawUpdate() { _data->frame = frame; } +void Beetle::invertDirection() { + switch (_data->indexes[_data->offset]) { + default: + break; + + case 24: + case 25: + case 26: + case 27: + case 28: + _data->coordY = -_data->coordY; + break; + } +} + void Beetle::move() { if (!_data) error("[Beetle::move] Sequences have not been loaded"); diff --git a/engines/lastexpress/game/beetle.h b/engines/lastexpress/game/beetle.h index d3c47f39e5..034ebbd557 100644 --- a/engines/lastexpress/game/beetle.h +++ b/engines/lastexpress/game/beetle.h @@ -111,6 +111,7 @@ private: void updateFrame(SequenceFrame *frame) const; void updateData(uint32 index); void drawUpdate(); + void invertDirection(); }; } // End of namespace LastExpress diff --git a/engines/lastexpress/game/entities.cpp b/engines/lastexpress/game/entities.cpp index f27087a609..51db635bed 100644 --- a/engines/lastexpress/game/entities.cpp +++ b/engines/lastexpress/game/entities.cpp @@ -27,8 +27,6 @@ #include "lastexpress/data/sequence.h" // Entities -#include "lastexpress/entities/entity.h" - #include "lastexpress/entities/abbot.h" #include "lastexpress/entities/alexei.h" #include "lastexpress/entities/alouan.h" @@ -71,10 +69,8 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" #include "lastexpress/graphics.h" -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" #include "lastexpress/resource.h" @@ -673,11 +669,12 @@ void Entities::executeCallbacks() { ////////////////////////////////////////////////////////////////////////// // Processing ////////////////////////////////////////////////////////////////////////// -#define INCREMENT_DIRECTION_COUNTER() { \ - data->doProcessEntity = false; \ - if (data->direction == kDirectionRight || (data->direction == kDirectionSwitch && data->directionSwitch == kDirectionRight)) \ - ++data->field_4A1; \ - } +void Entities::incrementDirectionCounter(EntityData::EntityCallData *data) { + data->doProcessEntity = false; + + if (data->direction == kDirectionRight || (data->direction == kDirectionSwitch && data->directionSwitch == kDirectionRight)) + ++data->field_4A1; +} void Entities::processEntity(EntityIndex entityIndex) { EntityData::EntityCallData *data = getData(entityIndex); @@ -696,7 +693,7 @@ void Entities::processEntity(EntityIndex entityIndex) { getScenes()->removeAndRedraw(&data->frame, false); getScenes()->removeAndRedraw(&data->frame1, false); - INCREMENT_DIRECTION_COUNTER(); + incrementDirectionCounter(data); return; } @@ -726,7 +723,7 @@ label_nosequence: processFrame(entityIndex, false, true); if (!getFlags()->flag_entities_0 && !data->doProcessEntity) { - INCREMENT_DIRECTION_COUNTER(); + incrementDirectionCounter(data); return; } } else { @@ -744,7 +741,7 @@ label_nosequence: data->position = 0; } - INCREMENT_DIRECTION_COUNTER(); + incrementDirectionCounter(data); } return; } @@ -754,46 +751,44 @@ label_nosequence: if (data->frame->getInfo()->field_30 > (data->field_49B + 1) || (data->direction == kDirectionLeft && data->sequence->count() == 1)) { ++data->field_49B; - } else { - if (data->frame->getInfo()->field_30 > data->field_49B && !data->frame->getInfo()->keepPreviousFrame) { - ++data->field_49B; - } else { - if (data->frame->getInfo()->keepPreviousFrame == 1) - keepPreviousFrame = true; - - // Increment current frame - ++data->currentFrame; + } else if (data->frame->getInfo()->field_30 <= data->field_49B || data->frame->getInfo()->keepPreviousFrame) { + if (data->frame->getInfo()->keepPreviousFrame == 1) + keepPreviousFrame = true; - if (data->currentFrame > (int16)(data->sequence->count() - 1) || (data->field_4A9 && checkSequenceFromPosition(entityIndex))) { + // Increment current frame + ++data->currentFrame; - if (data->direction == kDirectionLeft) { - data->currentFrame = 0; - } else { - keepPreviousFrame = true; - drawNextSequence(entityIndex); + if (data->currentFrame > (int16)(data->sequence->count() - 1) || (data->field_4A9 && checkSequenceFromPosition(entityIndex))) { - if (getFlags()->flag_entities_0 || data->doProcessEntity) - return; + if (data->direction == kDirectionLeft) { + data->currentFrame = 0; + } else { + keepPreviousFrame = true; + drawNextSequence(entityIndex); - if (!data->sequence2) { - updateEntityPosition(entityIndex); - data->doProcessEntity = false; - return; - } + if (getFlags()->flag_entities_0 || data->doProcessEntity) + return; - copySequenceData(entityIndex); + if (!data->sequence2) { + updateEntityPosition(entityIndex); + data->doProcessEntity = false; + return; } + copySequenceData(entityIndex); } - processFrame(entityIndex, keepPreviousFrame, false); - - if (getFlags()->flag_entities_0 || data->doProcessEntity) - return; } + + processFrame(entityIndex, keepPreviousFrame, false); + + if (getFlags()->flag_entities_0 || data->doProcessEntity) + return; + } else { + ++data->field_49B; } - INCREMENT_DIRECTION_COUNTER(); + incrementDirectionCounter(data); } void Entities::computeCurrentFrame(EntityIndex entityIndex) const { @@ -2283,7 +2278,7 @@ label_process_entity: if (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingUp)) { getSavePoints()->push(kEntityPlayer, entity, kActionExcuseMeCath); - } else if (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingDown) || getScenes()->checkCurrentPosition(false)){ + } else if (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingDown) || getScenes()->checkCurrentPosition(false)) { getSavePoints()->push(kEntityPlayer, entity, kActionExcuseMe); if (getScenes()->checkCurrentPosition(false)) diff --git a/engines/lastexpress/game/entities.h b/engines/lastexpress/game/entities.h index eb5eae461f..a9de7931f0 100644 --- a/engines/lastexpress/game/entities.h +++ b/engines/lastexpress/game/entities.h @@ -344,6 +344,7 @@ private: uint _positions[_positionsCount]; void executeCallbacks(); + void incrementDirectionCounter(EntityData::EntityCallData *data); void processEntity(EntityIndex entity); void drawSequence(EntityIndex entity, const char *sequence, EntityDirection direction) const; diff --git a/engines/lastexpress/game/inventory.cpp b/engines/lastexpress/game/inventory.cpp index e417b1ec0d..52c00ece31 100644 --- a/engines/lastexpress/game/inventory.cpp +++ b/engines/lastexpress/game/inventory.cpp @@ -26,7 +26,9 @@ #include "lastexpress/data/scene.h" #include "lastexpress/data/snd.h" +#include "lastexpress/game/entities.h" #include "lastexpress/game/logic.h" +#include "lastexpress/game/savegame.h" #include "lastexpress/game/scenes.h" #include "lastexpress/game/state.h" @@ -44,7 +46,7 @@ namespace LastExpress { Inventory::Inventory(LastExpressEngine *engine) : _engine(engine), _selectedItem(kItemNone), _highlightedItemIndex(0), _itemsShown(0), - _showingHourGlass(false), _blinkingEgg(false), _blinkingTime(0), _blinkingInterval(_defaultBlinkingInterval), _blinkingBrightness(1), + _showingHourGlass(false), _blinkingDirection(1), _blinkingBrightness(0), _useMagnifier(false), _portraitHighlighted(false), _isOpened(false), _eggHightlighted(false), _itemScene(NULL) { //_inventoryRect = Common::Rect(0, 0, 32, 32); @@ -162,13 +164,11 @@ void Inventory::handleMouseEvent(const Common::Event &ev) { getMenu()->show(true, kSavegameTypeIndex, 0); - } else if (ev.type == Common::EVENT_RBUTTONDOWN) { - if (getGlobalTimer()) { - if (getSoundQueue()->isBuffered("TIMER")) - getSoundQueue()->removeFromQueue("TIMER"); + } else if (ev.type == Common::EVENT_RBUTTONDOWN && getGlobalTimer()) { + if (getSoundQueue()->isBuffered("TIMER")) + getSoundQueue()->removeFromQueue("TIMER"); - setGlobalTimer(900); - } + setGlobalTimer(900); } } @@ -180,7 +180,7 @@ void Inventory::handleMouseEvent(const Common::Event &ev) { if (_highlightedItemIndex) drawHighlight(_highlightedItemIndex, true); } else { - // The user released the mouse button, we need to update the inventory state (clear hightlight and items) + // The user released the mouse button, we need to update the inventory state (clear highlight and items) drawItem((CursorStyle)getProgress().portrait, 0, 0, 1); _engine->getGraphicsManager()->clear(GraphicsManager::kBackgroundInventory, Common::Rect(0, 44, 32, (int16)(40 * _itemsShown + 40))); _isOpened = false; @@ -226,12 +226,11 @@ void Inventory::handleMouseEvent(const Common::Event &ev) { if (getFlags()->mouseLeftPressed) { if (getState()->sceneUseBackup) { - if (getState()->sceneBackup2 - && getFirstExaminableItem() == _selectedItem) { - SceneIndex sceneIndex = getState()->sceneBackup2; - getState()->sceneBackup2 = kSceneNone; + if (getState()->sceneBackup2 && getFirstExaminableItem() == _selectedItem) { + SceneIndex sceneIndex = getState()->sceneBackup2; + getState()->sceneBackup2 = kSceneNone; - getScenes()->loadScene(sceneIndex); + getScenes()->loadScene(sceneIndex); } } else { getState()->sceneBackup = getState()->scene; @@ -261,7 +260,7 @@ void Inventory::handleMouseEvent(const Common::Event &ev) { // Change item highlight on list if (getFlags()->mouseLeftPressed) { - uint32 index = ev.mouse.y / 40; + uint32 index = (uint16)ev.mouse.y / 40; if (_highlightedItemIndex && _highlightedItemIndex != index) drawHighlight(_highlightedItemIndex, true); @@ -418,12 +417,12 @@ void Inventory::show() { drawEgg(); } -void Inventory::setPortrait(InventoryItem item) { +void Inventory::setPortrait(InventoryItem item) const { getProgress().portrait = item; drawItem((CursorStyle)getProgress().portrait, 0, 0); } -void Inventory::showHourGlass(){ +void Inventory::showHourGlass() const { if (!getMenu()->isShown()) drawItem(kCursorHourGlass, 608, 448); @@ -613,7 +612,7 @@ void Inventory::examine(InventoryItem item) { } } -void Inventory::drawEgg() { +void Inventory::drawEgg() const { if (!getMenu()->isShown()) drawItem((CursorStyle)(getMenu()->getGameId() + 39), 608, 448, _eggHightlighted ? 0 : 1); @@ -621,40 +620,51 @@ void Inventory::drawEgg() { } // Blinking egg: we need to blink the egg for delta time, with the blinking getting faster until it's always lit. -void Inventory::drawBlinkingEgg() { +void Inventory::drawBlinkingEgg(uint ticks) { + uint globalTimer = getGlobalTimer(); + uint timerValue = (getProgress().jacket == kJacketGreen) ? 450 : 225; - warning("[Inventory::drawBlinkingEgg] Blinking not implemented"); + if (globalTimer == timerValue || globalTimer == 900) { + _blinkingBrightness = 0; + _blinkingDirection = 1; + } - //// TODO show egg (with or without mouseover) + globalTimer = globalTimer <= ticks ? 0 : globalTimer - ticks; + setGlobalTimer(globalTimer); - //// Play timer sound - //if (getGlobalTimer() < 90) { - // if (getGlobalTimer() + ticks >= 90) - // getSound()->playSoundWithSubtitles("TIMER.SND", 50331664, kEntityPlayer); + if (getFlags()->flag_0 + || (globalTimer % 5) == 0 + || (globalTimer <= 500 && (globalTimer % ((globalTimer + 100) / 100)) == 0)) + blinkEgg(); - // if (getSoundQueue()->isBuffered("TIMER")) - // setGlobalTimer(0); - //} + if (globalTimer < 90) { + if ((globalTimer + ticks) >= 90) + getSound()->playSoundWithSubtitles("TIMER", (SoundFlag)(kFlagType13|kFlagDefault), kEntityPlayer); - //// Restore egg to standard brightness - //if (!getGlobalTimer()) { - // - //} + if (!getSoundQueue()->isBuffered("TIMER")) + setGlobalTimer(0); + } + if (globalTimer == 0) { + drawItem((CursorStyle)(getMenu()->getGameId() + 39), 608, 448, _menuEggRect.contains(getCoords()) ? 1 : -1); - //drawItem(608, 448, getMenu()->getGameId() + 39, _blinkingBrightness) + askForRedraw(); - //// TODO if delta time > _blinkingInterval, update egg & ask for redraw then adjust blinking time and remaining time - // + getSaveLoad()->saveGame(kSavegameTypeAuto, kEntityChapters, 0); + } +} - //// Reset values and stop blinking - //if (_blinkingTime == 0) - // blinkEgg(false); +void Inventory::blinkEgg() { + drawItem((CursorStyle)(getMenu()->getGameId() + 39), 608, 448, (_blinkingBrightness == 0) ? -1 : _blinkingBrightness); askForRedraw(); + + _blinkingBrightness += _blinkingDirection; + if (_blinkingBrightness == 0 || _blinkingBrightness == 3) + _blinkingDirection = -_blinkingDirection; } -void Inventory::drawItem(CursorStyle id, uint16 x, uint16 y, int16 brightnessIndex) { +void Inventory::drawItem(CursorStyle id, uint16 x, uint16 y, int16 brightnessIndex) const { Icon icon(id); icon.setPosition(x, y); @@ -678,7 +688,7 @@ void Inventory::drawSelectedItem() { } } -void Inventory::clearSelectedItem() { +void Inventory::clearSelectedItem() const { _engine->getGraphicsManager()->clear(GraphicsManager::kBackgroundInventory, Common::Rect(44, 0, 44 + 32, 32)); } @@ -733,7 +743,7 @@ void Inventory::drawHighlight(uint32 currentIndex, bool reset) { } } -uint32 Inventory::getItemIndex(uint32 currentIndex) { +uint32 Inventory::getItemIndex(uint32 currentIndex) const { uint32 count = 0; for (uint32 i = 1; i < ARRAYSIZE(_entries); i++) { diff --git a/engines/lastexpress/game/inventory.h b/engines/lastexpress/game/inventory.h index b1995adce3..b1019a43c6 100644 --- a/engines/lastexpress/game/inventory.h +++ b/engines/lastexpress/game/inventory.h @@ -106,11 +106,10 @@ public: // UI Control void show(); - void blinkEgg(bool enabled); - void showHourGlass(); - void setPortrait(InventoryItem item); - void drawEgg(); - void drawBlinkingEgg(); + void showHourGlass() const; + void setPortrait(InventoryItem item) const; + void drawEgg() const; + void drawBlinkingEgg(uint ticks = 1); // Handle inventory UI events. void handleMouseEvent(const Common::Event &ev); @@ -133,8 +132,6 @@ public: Common::String toString(); private: - static const uint32 _defaultBlinkingInterval = 250; ///< Default blinking interval in ms - LastExpressEngine *_engine; InventoryEntry _entries[32]; @@ -144,9 +141,7 @@ private: uint32 _itemsShown; bool _showingHourGlass; - bool _blinkingEgg; - uint32 _blinkingTime; - uint32 _blinkingInterval; + int16 _blinkingDirection; uint16 _blinkingBrightness; // Flags @@ -157,8 +152,6 @@ private: Scene *_itemScene; - // Important rects - //Common::Rect _inventoryRect; Common::Rect _menuEggRect; Common::Rect _selectedItemRect; @@ -168,14 +161,15 @@ private: void close(); void examine(InventoryItem item); void drawHighlight(uint32 currentIndex, bool reset); - uint32 getItemIndex(uint32 currentIndex); + uint32 getItemIndex(uint32 currentIndex) const; bool isItemSceneParameter(InventoryItem item) const; - void drawItem(CursorStyle id, uint16 x, uint16 y, int16 brighnessIndex = -1); + void drawItem(CursorStyle id, uint16 x, uint16 y, int16 brighnessIndex = -1) const; + void blinkEgg(); void drawSelectedItem(); - void clearSelectedItem(); + void clearSelectedItem() const; }; } // End of namespace LastExpress diff --git a/engines/lastexpress/game/logic.cpp b/engines/lastexpress/game/logic.cpp index aeac8cff98..1696f100ff 100644 --- a/engines/lastexpress/game/logic.cpp +++ b/engines/lastexpress/game/logic.cpp @@ -47,10 +47,8 @@ #include "lastexpress/menu/menu.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" #include "lastexpress/graphics.h" -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" #include "lastexpress/resource.h" @@ -88,16 +86,6 @@ Logic::~Logic() { ////////////////////////////////////////////////////////////////////////// // Event Handling ////////////////////////////////////////////////////////////////////////// -#define REDRAW_CURSOR() { \ - if (getInventory()->isMagnifierInUse()) \ - _engine->getCursor()->setStyle(kCursorMagnifier); \ - if (getInventory()->isPortraitHighlighted() \ - || getInventory()->isOpened() \ - || getInventory()->isEggHighlighted()) \ - _engine->getCursor()->setStyle(kCursorNormal); \ - return; \ -} - void Logic::eventMouse(const Common::Event &ev) { bool hotspotHandled = false; @@ -168,7 +156,9 @@ void Logic::eventMouse(const Common::Event &ev) { getInventory()->unselectItem(); } - REDRAW_CURSOR() + redrawCursor(); + + return; } // Handle match case @@ -194,7 +184,9 @@ void Logic::eventMouse(const Common::Event &ev) { getScenes()->processScene(); } - REDRAW_CURSOR() + redrawCursor(); + + return; } // Handle entity item case @@ -315,7 +307,7 @@ void Logic::eventTick(const Common::Event &) { ////////////////////////////////////////////////////////////////////////// // Draw the blinking egg if needed if (getGlobalTimer() && !getFlags()->shouldDrawEggOrHourGlass) - getInventory()->drawBlinkingEgg(); + getInventory()->drawBlinkingEgg(ticks); ////////////////////////////////////////////////////////////////////////// // Adjust time and save game if needed @@ -411,9 +403,12 @@ void Logic::eventTick(const Common::Event &) { * Resets the game state. */ void Logic::resetState() { - getState()->scene = kSceneDefault; + getScenes()->setCoordinates(Common::Rect(80, 0, 559, 479)); + + SAFE_DELETE(_entities); + _entities = new Entities(_engine); - warning("[Logic::resetState] Not implemented! You need to restart the engine until this is implemented."); + _state->reset(); } /** @@ -595,4 +590,14 @@ void Logic::updateCursor(bool) const { /* the cursor is always updated, even whe _engine->getCursor()->setStyle(style); } +void Logic::redrawCursor() const { + if (getInventory()->isMagnifierInUse()) + _engine->getCursor()->setStyle(kCursorMagnifier); + + if (getInventory()->isPortraitHighlighted() + || getInventory()->isOpened() + || getInventory()->isEggHighlighted()) + _engine->getCursor()->setStyle(kCursorNormal); +} + } // End of namespace LastExpress diff --git a/engines/lastexpress/game/logic.h b/engines/lastexpress/game/logic.h index 8b7dcef942..efb8f1e1a3 100644 --- a/engines/lastexpress/game/logic.h +++ b/engines/lastexpress/game/logic.h @@ -25,8 +25,6 @@ #include "lastexpress/shared.h" -#include "lastexpress/game/entities.h" - #include "lastexpress/eventhandler.h" #include "common/events.h" @@ -75,6 +73,7 @@ private: void switchChapter() const; void showCredits() const; + void redrawCursor() const; // Flags & Members bool _flagActionPerformed; diff --git a/engines/lastexpress/game/object.cpp b/engines/lastexpress/game/object.cpp index d9e9e4279a..48df91ea6d 100644 --- a/engines/lastexpress/game/object.cpp +++ b/engines/lastexpress/game/object.cpp @@ -22,11 +22,11 @@ #include "lastexpress/game/object.h" +#include "lastexpress/game/entities.h" #include "lastexpress/game/logic.h" #include "lastexpress/game/scenes.h" #include "lastexpress/game/state.h" -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" namespace LastExpress { diff --git a/engines/lastexpress/game/savegame.cpp b/engines/lastexpress/game/savegame.cpp index 9c464feb6e..360e99146a 100644 --- a/engines/lastexpress/game/savegame.cpp +++ b/engines/lastexpress/game/savegame.cpp @@ -22,6 +22,7 @@ #include "lastexpress/game/savegame.h" +#include "lastexpress/game/entities.h" #include "lastexpress/game/inventory.h" #include "lastexpress/game/logic.h" #include "lastexpress/game/object.h" @@ -34,13 +35,13 @@ #include "lastexpress/debug.h" #include "lastexpress/lastexpress.h" -#include "lastexpress/helpers.h" #include "common/file.h" -#include "common/system.h" namespace LastExpress { +#define DISABLE_COMPRESSION 1 + // Names of savegames static const struct { const char *saveFile; @@ -54,6 +55,296 @@ static const struct { }; ////////////////////////////////////////////////////////////////////////// +// SavegameStream +////////////////////////////////////////////////////////////////////////// + +uint32 SavegameStream::write(const void *dataPtr, uint32 dataSize) { +#if !DISABLE_COMPRESSION + if (_enableCompression) + return writeCompressed(dataPtr, dataSize); +#endif + + return Common::MemoryWriteStreamDynamic::write(dataPtr, dataSize); +} + +uint32 SavegameStream::read(void *dataPtr, uint32 dataSize) { +#if !DISABLE_COMPRESSION + if (_enableCompression) + return readCompressed(dataPtr, dataSize); +#endif + + return readUncompressed(dataPtr, dataSize); +} + +uint32 SavegameStream::readUncompressed(void *dataPtr, uint32 dataSize) { + if ((int32)dataSize > size() - pos()) { + dataSize = size() - pos(); + _eos = true; + } + memcpy(dataPtr, getData() + pos(), dataSize); + + seek(dataSize, SEEK_CUR); + + return dataSize; +} + +void SavegameStream::writeBuffer(uint8 value, bool onlyValue) { + if (_bufferOffset == -1) + _bufferOffset = 0; + + if (_bufferOffset == 256) { + _bufferOffset = 0; + Common::MemoryWriteStreamDynamic::write(_buffer, 256); + } + + if (onlyValue || value < 0xFB) + _buffer[_bufferOffset] = value; + else + _buffer[_bufferOffset] = 0xFE; + + _offset++; + _bufferOffset++; + + if (!onlyValue && value >= 0xFB) + { + if (_bufferOffset == 256) { + _bufferOffset = 0; + Common::MemoryWriteStreamDynamic::write(_buffer, 256); + } + + _buffer[_bufferOffset] = value; + + _bufferOffset++; + _offset++; + } +} + +uint8 SavegameStream::readBuffer() { + if (_bufferOffset == -1 || _bufferOffset >= 256) { + readUncompressed(_buffer, 256); + _bufferOffset = 0; + } + + byte val = _buffer[_bufferOffset]; + _bufferOffset++; + + return val; +} + +uint32 SavegameStream::process() { + _enableCompression = !_enableCompression; + +#if DISABLE_COMPRESSION + return 0; +#else + switch (_status) { + default: + break; + + case kStatusReading: + _status = kStatusReady; + if (_bufferOffset != -1 && _bufferOffset != 256) { + seek(_bufferOffset - 256, SEEK_CUR); + _bufferOffset = -1; + } + break; + + case kStatusWriting: + switch (_valueCount) { + default: + break; + + case 1: + writeBuffer(_previousValue, false); + break; + + case 2: + if (_previousValue) { + writeBuffer(0xFF); + writeBuffer(_repeatCount); + writeBuffer(_previousValue); + break; + } + + if (_repeatCount == 3) { + writeBuffer(0xFB); + break; + } + + if (_repeatCount == 255) { + writeBuffer(0xFC); + break; + } + + writeBuffer(0xFD); + writeBuffer(_repeatCount); + break; + } + + if (_bufferOffset != -1 && _bufferOffset != 0) { + Common::MemoryWriteStreamDynamic::write(_buffer, _bufferOffset); + _bufferOffset = -1; + } + break; + } + + _status = kStatusReady; + _valueCount = 0; + uint32 offset = _offset; + _offset = 0; + + return offset; +#endif +} + +uint32 SavegameStream::writeCompressed(const void *dataPtr, uint32 dataSize) { + if (_status == kStatusReading) + error("[SavegameStream::writeCompressed] Error: Compression buffer is in read mode."); + + _status = kStatusWriting; + const byte *data = (const byte *)dataPtr; + + while (dataSize) { + switch (_valueCount) { + default: + error("[SavegameStream::writeCompressed] Invalid value count (%d)", _valueCount); + + case 0: + _previousValue = *data++; + _valueCount = 1; + break; + + case 1: + if (*data != _previousValue) { + writeBuffer(_previousValue, false); + _previousValue = *data; + } else { + _valueCount = 2; + _repeatCount = 2; + } + + ++data; + break; + + case 2: + if (*data != _previousValue || _repeatCount >= 255) { + if (_previousValue) { + writeBuffer(0xFF, true); + writeBuffer(_repeatCount, true); + writeBuffer(_previousValue, true); + + _previousValue = *data++; + _valueCount = 1; + break; + } + + if (_repeatCount == 3) { + writeBuffer(0xFB, true); + + _previousValue = *data++; + _valueCount = 1; + break; + } + + if (_repeatCount == -1) { + writeBuffer(0xFC, true); + + _previousValue = *data++; + _valueCount = 1; + break; + } + + writeBuffer(0xFD, true); + writeBuffer(_repeatCount, true); + + _previousValue = *data++; + _valueCount = 1; + } + + ++data; + ++_repeatCount; + break; + } + + --dataSize; + } + + return _offset; +} + +uint32 SavegameStream::readCompressed(void *dataPtr, uint32 dataSize) { + if (_status == kStatusWriting) + error("[SavegameStream::writeCompressed] Error: Compression buffer is in write mode."); + + _status = kStatusReady; + byte *data = (byte *)dataPtr; + + while (dataSize) { + switch (_valueCount) { + default: + error("[SavegameStream::readCompressed] Invalid value count (%d)", _valueCount); + + case 0: + case 1: { + // Read control code + byte control = readBuffer(); + + switch (control) { + default: + // Data value + *data++ = control; + break; + + case 0xFB: + _repeatCount = 2; + _previousValue = 0; + *data++ = 0; + _valueCount = 2; + break; + + case 0xFC: + _repeatCount = 254; + _previousValue = 0; + *data++ = 0; + _valueCount = 2; + break; + + case 0xFD: + _repeatCount = readBuffer() - 1; + _previousValue = 0; + *data++ = 0; + _valueCount = 2; + break; + + case 0xFE: + *data++ = readBuffer(); + break; + + case 0xFF: + _repeatCount = readBuffer() - 1; + _previousValue = readBuffer(); + *data++ = _previousValue; + _valueCount = 2; + break; + } + } + break; + + case 2: + *data++ = _previousValue; + _repeatCount--; + if (!_repeatCount) + _valueCount = 1; + break; + } + + --dataSize; + } + + return _offset; +} + +////////////////////////////////////////////////////////////////////////// // Constructors ////////////////////////////////////////////////////////////////////////// @@ -81,6 +372,7 @@ void SaveLoad::flushStream(GameId id) { error("[SaveLoad::flushStream] Savegame stream is invalid"); save->write(_savegame->getData(), (uint32)_savegame->size()); + save->finalize(); delete save; } @@ -258,7 +550,7 @@ void SaveLoad::saveGame(SavegameType type, EntityIndex entity, uint32 value) { entry.saveLoadWithSerializer(ser); if (!entry.isValid()) { - warning("[SaveLoad::saveGame] Invalid entry. This savegame might be corrupted"); + error("[SaveLoad::saveGame] Invalid entry. This savegame might be corrupted"); _savegame->seek(header.offset); } else if (getState()->time < entry.time || (type == kSavegameTypeTickInterval && getState()->time == entry.time)) { // Not ready to save a game, skipping! @@ -341,16 +633,46 @@ bool SaveLoad::loadMainHeader(Common::InSaveFile *stream, SavegameMainHeader *he ////////////////////////////////////////////////////////////////////////// // Entries ////////////////////////////////////////////////////////////////////////// -void SaveLoad::writeEntry(SavegameType type, EntityIndex entity, uint32 value) { -#define WRITE_ENTRY(name, func, val) { \ - uint32 _prevPosition = (uint32)_savegame->pos(); \ - func; \ - uint32 _count = (uint32)_savegame->pos() - _prevPosition; \ - debugC(kLastExpressDebugSavegame, "Savegame: Writing " #name ": %d bytes", _count); \ - if (_count != val)\ - error("[SaveLoad::writeEntry] Number of bytes written (%d) differ from expected count (%d)", _count, val); \ +uint32 SaveLoad::writeValue(Common::Serializer &ser, const char *name, Common::Functor1<Common::Serializer &, void> *function, uint size) { + debugC(kLastExpressDebugSavegame, "Savegame: Writing %s: %u bytes", name, size); + + uint32 prevPosition = (uint32)_savegame->pos(); + + // Serialize data into our buffer + (*function)(ser); + + uint32 count = (uint32)_savegame->pos() - prevPosition; + +#if DISABLE_COMPRESSION + if (count != size) + error("[SaveLoad::writeValue] %s - Number of bytes written (%d) differ from expected count (%d)", name, count, size); +#endif + + return count; } +uint32 SaveLoad::readValue(Common::Serializer &ser, const char *name, Common::Functor1<Common::Serializer &, void> *function, uint size) { + debugC(kLastExpressDebugSavegame, "Savegame: Reading %s: %u bytes", name, size); + + uint32 prevPosition = (uint32)_savegame->pos(); + + (*function)(ser); + + uint32 count = (uint32)_savegame->pos() - prevPosition; + +#if DISABLE_COMPRESSION + if (size != 0 && count != size) + error("[SaveLoad::readValue] %s - Number of bytes read (%d) differ from expected count (%d)", name, count, size); +#endif + + return count; +} + +void SaveLoad::syncEntity(Common::Serializer &ser) { + ser.syncAsUint32LE(_entity); +} + +void SaveLoad::writeEntry(SavegameType type, EntityIndex entity, uint32 value) { if (!_savegame) error("[SaveLoad::writeEntry] Savegame stream is invalid"); @@ -369,18 +691,22 @@ void SaveLoad::writeEntry(SavegameType type, EntityIndex entity, uint32 value) { header.saveLoadWithSerializer(ser); // Write game data - WRITE_ENTRY("entity index", ser.syncAsUint32LE(entity), 4); - WRITE_ENTRY("state", getState()->saveLoadWithSerializer(ser), 4 + 4 + 4 + 4 + 1 + 4 + 4); - WRITE_ENTRY("selected item", getInventory()->saveSelectedItem(ser), 4); - WRITE_ENTRY("positions", getEntities()->savePositions(ser), 4 * 1000); - WRITE_ENTRY("compartments", getEntities()->saveCompartments(ser), 4 * 16 * 2); - WRITE_ENTRY("progress", getProgress().saveLoadWithSerializer(ser), 4 * 128); - WRITE_ENTRY("events", getState()->syncEvents(ser), 512); - WRITE_ENTRY("inventory", getInventory()->saveLoadWithSerializer(ser), 7 * 32); - WRITE_ENTRY("objects", getObjects()->saveLoadWithSerializer(ser), 5 * 128); - WRITE_ENTRY("entities", getEntities()->saveLoadWithSerializer(ser), 1262 * 40); - WRITE_ENTRY("sound", getSoundQueue()->saveLoadWithSerializer(ser), 3 * 4 + getSoundQueue()->count() * 64); - WRITE_ENTRY("savepoints", getSavePoints()->saveLoadWithSerializer(ser), 128 * 16 + 4 + getSavePoints()->count() * 16); + _entity = entity; + + _savegame->process(); + writeValue(ser, "entity index", WRAP_SYNC_FUNCTION(this, SaveLoad, syncEntity), 4); + writeValue(ser, "state", WRAP_SYNC_FUNCTION(getState(), State::GameState, saveLoadWithSerializer), 4 + 4 + 4 + 4 + 1 + 4 + 4); + writeValue(ser, "selected item", WRAP_SYNC_FUNCTION(getInventory(), Inventory, saveSelectedItem), 4); + writeValue(ser, "positions", WRAP_SYNC_FUNCTION(getEntities(), Entities, savePositions), 4 * 1000); + writeValue(ser, "compartments", WRAP_SYNC_FUNCTION(getEntities(), Entities, saveCompartments), 4 * 16 * 2); + writeValue(ser, "progress", WRAP_SYNC_FUNCTION(&getProgress(), State::GameProgress, saveLoadWithSerializer), 4 * 128); + writeValue(ser, "events", WRAP_SYNC_FUNCTION(getState(), State::GameState, syncEvents), 512); + writeValue(ser, "inventory", WRAP_SYNC_FUNCTION(getInventory(), Inventory, saveLoadWithSerializer), 7 * 32); + writeValue(ser, "objects", WRAP_SYNC_FUNCTION(getObjects(), Objects, saveLoadWithSerializer), 5 * 128); + writeValue(ser, "entities", WRAP_SYNC_FUNCTION(getEntities(), Entities, saveLoadWithSerializer), 1262 * 40); + writeValue(ser, "sound", WRAP_SYNC_FUNCTION(getSoundQueue(), SoundQueue, saveLoadWithSerializer), 3 * 4 + getSoundQueue()->count() * 64); + writeValue(ser, "savepoints", WRAP_SYNC_FUNCTION(getSavePoints(), SavePoints, saveLoadWithSerializer), 128 * 16 + 4 + getSavePoints()->count() * 16); + _savegame->process(); header.offset = (uint32)_savegame->pos() - (originalPosition + 32); @@ -406,22 +732,6 @@ void SaveLoad::writeEntry(SavegameType type, EntityIndex entity, uint32 value) { } void SaveLoad::readEntry(SavegameType *type, EntityIndex *entity, uint32 *val, bool keepIndex) { -#define LOAD_ENTRY(name, func, val) { \ - uint32 _prevPosition = (uint32)_savegame->pos(); \ - func; \ - uint32 _count = (uint32)_savegame->pos() - _prevPosition; \ - debugC(kLastExpressDebugSavegame, "Savegame: Reading " #name ": %d bytes", _count); \ - if (_count != val) \ - error("[SaveLoad::readEntry] Number of bytes read (%d) differ from expected count (%d)", _count, val); \ -} - -#define LOAD_ENTRY_ONLY(name, func) { \ - uint32 _prevPosition = (uint32)_savegame->pos(); \ - func; \ - uint32 _count = (uint32)_savegame->pos() - _prevPosition; \ - debugC(kLastExpressDebugSavegame, "Savegame: Reading " #name ": %d bytes", _count); \ -} - if (!type || !entity || !val) error("[SaveLoad::readEntry] Invalid parameters passed"); @@ -444,20 +754,23 @@ void SaveLoad::readEntry(SavegameType *type, EntityIndex *entity, uint32 *val, b uint32 originalPosition = (uint32)_savegame->pos(); // Load game data - LOAD_ENTRY("entity index", ser.syncAsUint32LE(*entity), 4); - LOAD_ENTRY("state", getState()->saveLoadWithSerializer(ser), 4 + 4 + 4 + 4 + 1 + 4 + 4); - LOAD_ENTRY("selected item", getInventory()->saveSelectedItem(ser), 4); - LOAD_ENTRY("positions", getEntities()->savePositions(ser), 4 * 1000); - LOAD_ENTRY("compartments", getEntities()->saveCompartments(ser), 4 * 16 * 2); - LOAD_ENTRY("progress", getProgress().saveLoadWithSerializer(ser), 4 * 128); - LOAD_ENTRY("events", getState()->syncEvents(ser), 512); - LOAD_ENTRY("inventory", getInventory()->saveLoadWithSerializer(ser), 7 * 32); - LOAD_ENTRY("objects", getObjects()->saveLoadWithSerializer(ser), 5 * 128); - LOAD_ENTRY("entities", getEntities()->saveLoadWithSerializer(ser), 1262 * 40); - LOAD_ENTRY_ONLY("sound", getSoundQueue()->saveLoadWithSerializer(ser)); - LOAD_ENTRY_ONLY("savepoints", getSavePoints()->saveLoadWithSerializer(ser)); + _savegame->process(); + readValue(ser, "entity index", WRAP_SYNC_FUNCTION(this, SaveLoad, syncEntity), 4); + readValue(ser, "state", WRAP_SYNC_FUNCTION(getState(), State::GameState, saveLoadWithSerializer), 4 + 4 + 4 + 4 + 1 + 4 + 4); + readValue(ser, "selected item", WRAP_SYNC_FUNCTION(getInventory(), Inventory, saveSelectedItem), 4); + readValue(ser, "positions", WRAP_SYNC_FUNCTION(getEntities(), Entities, savePositions), 4 * 1000); + readValue(ser, "compartments", WRAP_SYNC_FUNCTION(getEntities(), Entities, saveCompartments), 4 * 16 * 2); + readValue(ser, "progress", WRAP_SYNC_FUNCTION(&getProgress(), State::GameProgress, saveLoadWithSerializer), 4 * 128); + readValue(ser, "events", WRAP_SYNC_FUNCTION(getState(), State::GameState, syncEvents), 512); + readValue(ser, "inventory", WRAP_SYNC_FUNCTION(getInventory(), Inventory, saveLoadWithSerializer), 7 * 32); + readValue(ser, "objects", WRAP_SYNC_FUNCTION(getObjects(), Objects, saveLoadWithSerializer), 5 * 128); + readValue(ser, "entities", WRAP_SYNC_FUNCTION(getEntities(), Entities, saveLoadWithSerializer), 1262 * 40); + readValue(ser, "sound", WRAP_SYNC_FUNCTION(getSoundQueue(), SoundQueue, saveLoadWithSerializer)); + readValue(ser, "savepoints", WRAP_SYNC_FUNCTION(getSavePoints(), SavePoints, saveLoadWithSerializer)); + _savegame->process(); // Update chapter + *entity = _entity; getProgress().chapter = entry.chapter; // Skip padding @@ -567,7 +880,7 @@ Common::InSaveFile *SaveLoad::openForLoading(GameId id) { } Common::OutSaveFile *SaveLoad::openForSaving(GameId id) { - Common::OutSaveFile *save = g_system->getSavefileManager()->openForSaving(getFilename(id)); + Common::OutSaveFile *save = g_system->getSavefileManager()->openForSaving(getFilename(id), false); // TODO Enable compression again if (!save) debugC(2, kLastExpressDebugSavegame, "Cannot open savegame for writing: %s", getFilename(id).c_str()); diff --git a/engines/lastexpress/game/savegame.h b/engines/lastexpress/game/savegame.h index 6f0408487f..8656b2ee86 100644 --- a/engines/lastexpress/game/savegame.h +++ b/engines/lastexpress/game/savegame.h @@ -80,11 +80,68 @@ namespace LastExpress { // Savegame signatures -#define SAVEGAME_SIGNATURE 0x12001200 -#define SAVEGAME_ENTRY_SIGNATURE 0xE660E660 +#define SAVEGAME_SIGNATURE 0x12001200 // 301994496 +#define SAVEGAME_ENTRY_SIGNATURE 0xE660E660 // 3865110112 + +#define WRAP_SYNC_FUNCTION(instance, className, method) \ + new Common::Functor1Mem<Common::Serializer &, void, className>(instance, &className::method) class LastExpressEngine; +class SavegameStream : public Common::MemoryWriteStreamDynamic, public Common::SeekableReadStream { +public: + SavegameStream() : MemoryWriteStreamDynamic(DisposeAfterUse::YES), _eos(false) { + _enableCompression = false; + _bufferOffset = -1; + _valueCount = 0; + _previousValue = 0; + _repeatCount = 0; + _offset = 0; + _status = kStatusReady; + + memset(_buffer, 0, 256); + } + + int32 pos() const { return MemoryWriteStreamDynamic::pos(); } + int32 size() const { return MemoryWriteStreamDynamic::size(); } + bool seek(int32 offset, int whence = SEEK_SET) { return MemoryWriteStreamDynamic::seek(offset, whence); } + bool eos() const { return _eos; } + uint32 read(void *dataPtr, uint32 dataSize); + uint32 write(const void *dataPtr, uint32 dataSize); + + uint32 process(); + +private: + enum CompressedStreamStatus { + kStatusReady, + kStatusReading, + kStatusWriting + }; + + uint32 readUncompressed(void *dataPtr, uint32 dataSize); + + // Compressed data + uint32 writeCompressed(const void *dataPtr, uint32 dataSize); + uint32 readCompressed(void *dataPtr, uint32 dataSize); + + void writeBuffer(uint8 value, bool onlyValue = true); + uint8 readBuffer(); + +private: + bool _eos; + + // Compression handling + bool _enableCompression; + int16 _bufferOffset; + byte _valueCount; + byte _previousValue; + int16 _repeatCount; + uint32 _offset; + CompressedStreamStatus _status; + + byte _buffer[256]; +}; + class SaveLoad { public: SaveLoad(LastExpressEngine *engine); @@ -116,30 +173,6 @@ public: uint32 getLastSavegameTicks() const { return _gameTicksLastSavegame; } private: - class SavegameStream : public Common::MemoryWriteStreamDynamic, public Common::SeekableReadStream { - public: - SavegameStream() : MemoryWriteStreamDynamic(DisposeAfterUse::YES), - _eos(false) {} - - int32 pos() const { return MemoryWriteStreamDynamic::pos(); } - int32 size() const { return MemoryWriteStreamDynamic::size(); } - bool seek(int32 offset, int whence = SEEK_SET) { return MemoryWriteStreamDynamic::seek(offset, whence); } - bool eos() const { return _eos; } - uint32 read(void *dataPtr, uint32 dataSize) { - if ((int32)dataSize > size() - pos()) { - dataSize = size() - pos(); - _eos = true; - } - memcpy(dataPtr, getData() + pos(), dataSize); - - seek(dataSize, SEEK_CUR); - - return dataSize; - } - private: - bool _eos; - }; - LastExpressEngine *_engine; struct SavegameMainHeader : Common::Serializable { @@ -268,6 +301,9 @@ private: void writeEntry(SavegameType type, EntityIndex entity, uint32 val); void readEntry(SavegameType *type, EntityIndex *entity, uint32 *val, bool keepIndex); + uint32 writeValue(Common::Serializer &ser, const char *name, Common::Functor1<Common::Serializer &, void> *function, uint size); + uint32 readValue(Common::Serializer &ser, const char *name, Common::Functor1<Common::Serializer &, void> *function, uint size = 0); + SavegameEntryHeader *getEntry(uint32 index); // Opening save files @@ -279,6 +315,10 @@ private: void initStream(); void loadStream(GameId id); void flushStream(GameId id); + + // Misc + EntityIndex _entity; + void syncEntity(Common::Serializer &ser); }; } // End of namespace LastExpress diff --git a/engines/lastexpress/game/savepoint.cpp b/engines/lastexpress/game/savepoint.cpp index 64ae26c2be..6b2dfc5930 100644 --- a/engines/lastexpress/game/savepoint.cpp +++ b/engines/lastexpress/game/savepoint.cpp @@ -26,7 +26,6 @@ #include "lastexpress/game/logic.h" #include "lastexpress/game/state.h" -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" @@ -95,7 +94,7 @@ void SavePoints::process() { if (!updateEntityFromData(savepoint)) { // Call requested callback - Entity::Callback *callback = getCallback(savepoint.entity1); + Callback *callback = getCallback(savepoint.entity1); if (callback && callback->isValid()) { debugC(8, kLastExpressDebugLogic, "Savepoint: entity1=%s, action=%s, entity2=%s", ENTITY_NAME(savepoint.entity1), ACTION_NAME(savepoint.action), ENTITY_NAME(savepoint.entity2)); (*callback)(savepoint); @@ -126,7 +125,7 @@ void SavePoints::addData(EntityIndex entity, ActionIndex action, uint32 param) { ////////////////////////////////////////////////////////////////////////// // Callbacks ////////////////////////////////////////////////////////////////////////// -void SavePoints::setCallback(EntityIndex index, Entity::Callback *callback) { +void SavePoints::setCallback(EntityIndex index, Callback *callback) { if (index >= 40) error("[SavePoints::setCallback] Attempting to use an invalid entity index. Valid values 0-39, was %d", index); @@ -136,7 +135,7 @@ void SavePoints::setCallback(EntityIndex index, Entity::Callback *callback) { _callbacks[index] = callback; } -Entity::Callback *SavePoints::getCallback(EntityIndex index) const { +Callback *SavePoints::getCallback(EntityIndex index) const { if (index >= 40) error("[SavePoints::getCallback] Attempting to use an invalid entity index. Valid values 0-39, was %d", index); @@ -150,7 +149,7 @@ void SavePoints::call(EntityIndex entity2, EntityIndex entity1, ActionIndex acti point.entity2 = entity2; point.param.intValue = param; - Entity::Callback *callback = getCallback(entity1); + Callback *callback = getCallback(entity1); if (callback != NULL && callback->isValid()) { debugC(8, kLastExpressDebugLogic, "Savepoint: entity1=%s, action=%s, entity2=%s, param=%d", ENTITY_NAME(entity1), ACTION_NAME(action), ENTITY_NAME(entity2), param); (*callback)(point); @@ -164,7 +163,7 @@ void SavePoints::call(EntityIndex entity2, EntityIndex entity1, ActionIndex acti point.entity2 = entity2; strcpy((char *)&point.param.charValue, param); - Entity::Callback *callback = getCallback(entity1); + Callback *callback = getCallback(entity1); if (callback != NULL && callback->isValid()) { debugC(8, kLastExpressDebugLogic, "Savepoint: entity1=%s, action=%s, entity2=%s, param=%s", ENTITY_NAME(entity1), ACTION_NAME(action), ENTITY_NAME(entity2), param); (*callback)(point); @@ -181,7 +180,7 @@ void SavePoints::callAndProcess() { bool isRunning = getFlags()->isGameRunning; while (isRunning) { - Entity::Callback *callback = getCallback(index); + Callback *callback = getCallback(index); if (callback != NULL && callback->isValid()) { (*callback)(savepoint); isRunning = getFlags()->isGameRunning; @@ -243,7 +242,15 @@ void SavePoints::saveLoadWithSerializer(Common::Serializer &s) { } // Skip uninitialized data if any - s.skip((_savePointsMaxSize - dataSize) * 16); + // (we are using a compressed stream, so we cannot seek on load) + uint32 unusedDataSize = (_savePointsMaxSize - dataSize) * 16; + if (s.isLoading()) { + byte *empty = (byte *)malloc(unusedDataSize); + s.syncBytes(empty, unusedDataSize); + free(empty); + } else { + s.skip(unusedDataSize); + } // Number of savepoints uint32 numSavepoints = _savepoints.size(); diff --git a/engines/lastexpress/game/savepoint.h b/engines/lastexpress/game/savepoint.h index a3303b4b8a..005133891a 100644 --- a/engines/lastexpress/game/savepoint.h +++ b/engines/lastexpress/game/savepoint.h @@ -23,9 +23,8 @@ #ifndef LASTEXPRESS_SAVEPOINT_H #define LASTEXPRESS_SAVEPOINT_H -#include "lastexpress/entities/entity.h" - #include "lastexpress/helpers.h" +#include "lastexpress/shared.h" #include "common/array.h" #include "common/list.h" @@ -74,10 +73,9 @@ struct SavePoint { } }; -class SavePoints : Common::Serializable { -private: - typedef Common::Functor1<const SavePoint&, void> Callback; +typedef Common::Functor1<const SavePoint&, void> Callback; +class SavePoints : Common::Serializable { public: struct SavePointData { @@ -112,7 +110,7 @@ public: void addData(EntityIndex entity, ActionIndex action, uint32 param); // Callbacks - void setCallback(EntityIndex index, Entity::Callback *callback); + void setCallback(EntityIndex index, Callback *callback); Callback *getCallback(EntityIndex entity) const; void call(EntityIndex entity2, EntityIndex entity1, ActionIndex action, uint32 param = 0) const; void call(EntityIndex entity2, EntityIndex entity1, ActionIndex action, const char *param) const; diff --git a/engines/lastexpress/game/scenes.cpp b/engines/lastexpress/game/scenes.cpp index b886951e0b..3cda900757 100644 --- a/engines/lastexpress/game/scenes.cpp +++ b/engines/lastexpress/game/scenes.cpp @@ -22,8 +22,6 @@ #include "lastexpress/game/scenes.h" -#include "lastexpress/data/scene.h" - #include "lastexpress/game/action.h" #include "lastexpress/game/beetle.h" #include "lastexpress/game/entities.h" @@ -34,10 +32,8 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" #include "lastexpress/graphics.h" -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" #include "lastexpress/resource.h" @@ -493,7 +489,7 @@ bool SceneManager::checkCurrentPosition(bool doCheckOtherCars) const { if (position == 99) return true; - switch (car){ + switch (car) { default: break; @@ -743,24 +739,31 @@ void SceneManager::resetQueue() { _queue.clear(); } -void SceneManager::setCoordinates(SequenceFrame *frame) { +void SceneManager::setCoordinates(Common::Rect rect) { + _flagCoordinates = true; - if (!frame || frame->getInfo()->subType == 3) - return; + if (_coords.right > rect.right) + _coords.right = rect.right; - _flagCoordinates = true; + if (_coords.bottom > rect.bottom) + _coords.bottom = rect.bottom; + + if (_coords.left < rect.left) + _coords.left = rect.left; - if (_coords.right > (int)frame->getInfo()->xPos1) - _coords.right = (int16)frame->getInfo()->xPos1; + if (_coords.top < rect.top) + _coords.top = rect.top; +} - if (_coords.bottom > (int)frame->getInfo()->yPos1) - _coords.bottom = (int16)frame->getInfo()->yPos1; +void SceneManager::setCoordinates(SequenceFrame *frame) { - if (_coords.left < (int)frame->getInfo()->xPos2) - _coords.left = (int16)frame->getInfo()->xPos2; + if (!frame || frame->getInfo()->subType == 3) + return; - if (_coords.top < (int)frame->getInfo()->yPos2) - _coords.top = (int16)frame->getInfo()->yPos2; + setCoordinates(Common::Rect((int16)frame->getInfo()->xPos1, + (int16)frame->getInfo()->yPos1, + (int16)frame->getInfo()->xPos2, + (int16)frame->getInfo()->yPos2)); } void SceneManager::resetCoordinates() { diff --git a/engines/lastexpress/game/scenes.h b/engines/lastexpress/game/scenes.h index 172dde2683..a866c65111 100644 --- a/engines/lastexpress/game/scenes.h +++ b/engines/lastexpress/game/scenes.h @@ -79,6 +79,7 @@ public: void removeAndRedraw(SequenceFrame **frame, bool doRedraw); void resetQueue(); void setCoordinates(SequenceFrame *frame); + void setCoordinates(Common::Rect rect); // Helpers SceneIndex getSceneIndexFromPosition(CarIndex car, Position position, int param3 = -1); diff --git a/engines/lastexpress/game/state.cpp b/engines/lastexpress/game/state.cpp index f3fd9720b1..02ede25595 100644 --- a/engines/lastexpress/game/state.cpp +++ b/engines/lastexpress/game/state.cpp @@ -49,6 +49,18 @@ State::~State() { _engine = NULL; } +void State::reset() { + SAFE_DELETE(_inventory); + SAFE_DELETE(_objects); + SAFE_DELETE(_savepoints); + SAFE_DELETE(_state); + + _inventory = new Inventory(_engine); + _objects = new Objects(_engine); + _savepoints = new SavePoints(_engine); + _state = new GameState(); +} + bool State::isNightTime() const { return (_state->progress.chapter == kChapter1 || _state->progress.chapter == kChapter4 diff --git a/engines/lastexpress/game/state.h b/engines/lastexpress/game/state.h index c937fdce9f..2c484f6976 100644 --- a/engines/lastexpress/game/state.h +++ b/engines/lastexpress/game/state.h @@ -621,6 +621,8 @@ public: State(LastExpressEngine *engine); ~State(); + void reset(); + // Accessors Inventory *getGameInventory() { return _inventory; } Objects *getGameObjects() { return _objects; } diff --git a/engines/lastexpress/helpers.h b/engines/lastexpress/helpers.h index 7f3f1e246c..02454be13d 100644 --- a/engines/lastexpress/helpers.h +++ b/engines/lastexpress/helpers.h @@ -27,6 +27,8 @@ // Misc helpers ////////////////////////////////////////////////////////////////////////// +#define LOW_BYTE(w) ((unsigned char)(((unsigned long)(w)) & 0xff)) + // Misc #define getArchive(name) _engine->getResourceManager()->getFileStream(name) #define rnd(value) _engine->getRandom().getRandomNumber(value - 1) diff --git a/engines/lastexpress/lastexpress.cpp b/engines/lastexpress/lastexpress.cpp index 250fa0f2d0..01d2634dec 100644 --- a/engines/lastexpress/lastexpress.cpp +++ b/engines/lastexpress/lastexpress.cpp @@ -54,18 +54,17 @@ const char *g_entityNames[] = { "Player", "Anna", "August", "Mertens", "Coudert" namespace LastExpress { LastExpressEngine::LastExpressEngine(OSystem *syst, const ADGameDescription *gd) : - Engine(syst), _gameDescription(gd), - _debugger(NULL), _cursor(NULL), - _font(NULL), _logic(NULL), _menu(NULL), - _frameCounter(0), _lastFrameCount(0), + Engine(syst), _gameDescription(gd), + _debugger(NULL), _random("lastexpress"), _cursor(NULL), + _font(NULL), _logic(NULL), _menu(NULL), + _frameCounter(0), _lastFrameCount(0), _graphicsMan(NULL), _resMan(NULL), _sceneMan(NULL), _soundMan(NULL), _eventMouse(NULL), _eventTick(NULL), - _eventMouseBackup(NULL), _eventTickBackup(NULL), - _random("lastexpress") + _eventMouseBackup(NULL), _eventTickBackup(NULL) { // Setup mixer - syncSoundSettings(); + Engine::syncSoundSettings(); // Adding the default directories const Common::FSNode gameDataDir(ConfMan.get("path")); diff --git a/engines/lastexpress/menu/menu.cpp b/engines/lastexpress/menu/menu.cpp index f1a8bebe94..6a453aee99 100644 --- a/engines/lastexpress/menu/menu.cpp +++ b/engines/lastexpress/menu/menu.cpp @@ -30,6 +30,7 @@ #include "lastexpress/fight/fight.h" +#include "lastexpress/game/entities.h" #include "lastexpress/game/inventory.h" #include "lastexpress/game/logic.h" #include "lastexpress/game/savegame.h" @@ -41,10 +42,8 @@ #include "lastexpress/menu/trainline.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" #include "lastexpress/graphics.h" -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" #include "lastexpress/resource.h" @@ -865,7 +864,7 @@ void Menu::init(bool doSavegame, SavegameType type, uint32 value) { doSavegame = false; } else { - // TODO rename saves? + warning("[Menu::initGame] Renaming saves not implemented"); } // Create a new savegame if needed @@ -876,7 +875,7 @@ void Menu::init(bool doSavegame, SavegameType type, uint32 value) { getSaveLoad()->saveGame(kSavegameTypeEvent2, kEntityPlayer, kEventNone); if (!getGlobalTimer()) { - // TODO: remove existing savegame temp file + warning("[Menu::initGame] Removing temporary saves not implemented"); } // Init savegame & menu values diff --git a/engines/lastexpress/resource.cpp b/engines/lastexpress/resource.cpp index ee4885e34e..1d010355ac 100644 --- a/engines/lastexpress/resource.cpp +++ b/engines/lastexpress/resource.cpp @@ -31,7 +31,6 @@ #include "common/debug.h" #include "common/file.h" -#include "common/textconsole.h" namespace LastExpress { @@ -129,13 +128,10 @@ bool ResourceManager::loadArchive(const Common::String &name) { // Get a stream to file in the archive // - same as createReadStreamForMember except it checks if the file exists and will assert / output a debug message if not -Common::SeekableReadStream *ResourceManager::getFileStream(const Common::String &name) { +Common::SeekableReadStream *ResourceManager::getFileStream(const Common::String &name) const { // Check if the file exits in the archive if (!hasFile(name)) { -//#ifdef _DEBUG -// error("[ResourceManager::getFileStream] Cannot open file: %s", name.c_str()); -//#endif debugC(2, kLastExpressDebugResource, "Error opening file: %s", name.c_str()); return NULL; } diff --git a/engines/lastexpress/resource.h b/engines/lastexpress/resource.h index f2f5d63bce..90ac9b87ad 100644 --- a/engines/lastexpress/resource.h +++ b/engines/lastexpress/resource.h @@ -42,7 +42,7 @@ public: // Loading bool loadArchive(ArchiveIndex type); static bool isArchivePresent(ArchiveIndex type); - Common::SeekableReadStream *getFileStream(const Common::String &name); + Common::SeekableReadStream *getFileStream(const Common::String &name) const; // Archive functions bool hasFile(const Common::String &name) const; diff --git a/engines/lastexpress/shared.h b/engines/lastexpress/shared.h index d60a498447..56cf730e24 100644 --- a/engines/lastexpress/shared.h +++ b/engines/lastexpress/shared.h @@ -80,7 +80,8 @@ enum SoundFlag { kFlagMusic = 0x5000010, kFlagType3 = 0x6000000, kFlagLoop = 0x6001008, - kFlagType9 = 0x7000000 + kFlagType9 = 0x7000000, + kFlagNIS = 0x7002010 }; enum SoundState { @@ -1732,62 +1733,6 @@ enum ActionIndex { kActionEnd }; -////////////////////////////////////////////////////////////////////////// -// Functors classes used by the engine -////////////////////////////////////////////////////////////////////////// - -// FIXME is this achievable with the existing Functor1Mem function -template<class Arg, class Res, class T> -class Functor1MemConst : public Common::Functor1<Arg, Res> { -public: - typedef Res (T::*FuncType)(Arg) const; - - Functor1MemConst(T *t, const FuncType &func) : _t(t), _func(func) {} - - bool isValid() const { return _func != 0 && _t != 0; } - Res operator()(Arg v1) const { - return (_t->*_func)(v1); - } -private: - mutable T *_t; - const FuncType _func; -}; - -// FIXME move this to existing func.h file -template<class Arg1, class Arg2, class Arg3, class Arg4, class Result> -struct QuaternaryFunction { - typedef Arg1 FirstArgumentType; - typedef Arg2 SecondArgumentType; - typedef Arg3 ThirdArgumentType; - typedef Arg4 FourthArgumentType; - typedef Result ResultType; -}; - -template<class Arg1, class Arg2, class Arg3, class Arg4, class Res> -struct Functor4 : public QuaternaryFunction<Arg1, Arg2, Arg3, Arg4, Res> { - virtual ~Functor4() {} - - virtual bool isValid() const = 0; - virtual Res operator()(Arg1, Arg2, Arg3, Arg4) const = 0; -}; - -template<class Arg1, class Arg2, class Arg3, class Arg4, class Res, class T> -class Functor4Mem : public Functor4<Arg1, Arg2, Arg3, Arg4, Res> { -public: - typedef Res (T::*FuncType)(Arg1, Arg2, Arg3, Arg4); - - Functor4Mem(T *t, const FuncType &func) : _t(t), _func(func) {} - - bool isValid() const { return _func != 0 && _t != 0; } - Res operator()(Arg1 v1, Arg2 v2, Arg3 v3, Arg4 v4) const { - return (_t->*_func)(v1, v2, v3, v4); - } -private: - mutable T *_t; - const FuncType _func; -}; - - } // End of namespace LastExpress #endif // LASTEXPRESS_SHARED_H diff --git a/engines/lastexpress/sound/entry.cpp b/engines/lastexpress/sound/entry.cpp index 44cc68a57b..f2a063e45f 100644 --- a/engines/lastexpress/sound/entry.cpp +++ b/engines/lastexpress/sound/entry.cpp @@ -30,11 +30,9 @@ #include "lastexpress/sound/sound.h" #include "lastexpress/graphics.h" -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" #include "lastexpress/resource.h" -#include "common/stream.h" namespace LastExpress { @@ -47,6 +45,8 @@ namespace LastExpress { SoundEntry::SoundEntry(LastExpressEngine *engine) : _engine(engine) { _type = kSoundTypeNone; + _currentDataPtr = NULL; + _blockCount = 0; _time = 0; @@ -71,7 +71,13 @@ SoundEntry::~SoundEntry() { // Entries that have been queued will have their streamed disposed automatically if (!_soundStream) SAFE_DELETE(_stream); - delete _soundStream; + + SAFE_DELETE(_soundStream); + + free(_currentDataPtr); + + _subtitle = NULL; + _stream = NULL; // Zero passed pointers _engine = NULL; @@ -111,7 +117,7 @@ void SoundEntry::close() { void SoundEntry::play() { if (!_stream) { - warning("[SoundEntry::play] stream has been disposed"); + error("[SoundEntry::play] stream has been disposed"); return; } @@ -277,7 +283,7 @@ bool SoundEntry::updateSound() { int l = strlen(sub) + 1; if (l - 1 > 4) - sub[l - 1 - 4] = 0; + sub[l - (1 + 4)] = 0; showSubtitle(sub); } } else { @@ -393,6 +399,10 @@ SubtitleEntry::SubtitleEntry(LastExpressEngine *engine) : _engine(engine) { SubtitleEntry::~SubtitleEntry() { SAFE_DELETE(_data); + + // Zero-out passed pointers + _sound = NULL; + _engine = NULL; } void SubtitleEntry::load(Common::String filename, SoundEntry *soundEntry) { @@ -423,6 +433,9 @@ void SubtitleEntry::loadData() { } void SubtitleEntry::setupAndDraw() { + if (!_sound) + error("[SubtitleEntry::setupAndDraw] Sound entry not initialized"); + if (!_data) { _data = new SubtitleManager(_engine->getFont()); _data->load(getArchive(_filename)); diff --git a/engines/lastexpress/sound/queue.cpp b/engines/lastexpress/sound/queue.cpp index 33b4c06793..d72acfd8a0 100644 --- a/engines/lastexpress/sound/queue.cpp +++ b/engines/lastexpress/sound/queue.cpp @@ -26,6 +26,7 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/entry.h" +#include "lastexpress/sound/sound.h" #include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" @@ -39,6 +40,7 @@ SoundQueue::SoundQueue(LastExpressEngine *engine) : _engine(engine) { _subtitlesFlag = 0; _currentSubtitle = NULL; + //_soundCacheData = NULL; } SoundQueue::~SoundQueue() { @@ -51,6 +53,7 @@ SoundQueue::~SoundQueue() { _subtitles.clear(); _currentSubtitle = NULL; + //SAFE_DELETE(_soundCacheData); // Zero passed pointers _engine = NULL; @@ -134,7 +137,7 @@ void SoundQueue::updateQueue() { // Original update the current entry, loading another set of samples to be decoded - getFlags()->flag_3 = 0; + getFlags()->flag_3 = false; --_flag; } @@ -340,13 +343,14 @@ void SoundQueue::updateSubtitles() { return; } + if (!subtitle) + return; + if (_subtitlesFlag & 1) subtitle->drawOnScreen(); - if (subtitle) { - subtitle->loadData(); - subtitle->setupAndDraw(); - } + subtitle->loadData(); + subtitle->setupAndDraw(); } ////////////////////////////////////////////////////////////////////////// @@ -368,7 +372,15 @@ void SoundQueue::saveLoadWithSerializer(Common::Serializer &s) { (*i)->saveLoadWithSerializer(s); } else { warning("[Sound::saveLoadWithSerializer] Loading not implemented"); - s.skip(numEntries * 64); + + uint32 unusedDataSize = numEntries * 64; + if (s.isLoading()) { + byte *empty = (byte *)malloc(unusedDataSize); + s.syncBytes(empty, unusedDataSize); + free(empty); + } else { + s.skip(unusedDataSize); + } } } diff --git a/engines/lastexpress/sound/queue.h b/engines/lastexpress/sound/queue.h index 75fe06883a..e1f9be1cf7 100644 --- a/engines/lastexpress/sound/queue.h +++ b/engines/lastexpress/sound/queue.h @@ -106,7 +106,7 @@ private: // Entries Common::List<SoundEntry *> _soundList; ///< List of all sound entries - void *_soundCacheData; + //void *_soundCacheData; // Subtitles int _subtitlesFlag; diff --git a/engines/lastexpress/sound/sound.cpp b/engines/lastexpress/sound/sound.cpp index 2f7bb4a601..319f7cd4f4 100644 --- a/engines/lastexpress/sound/sound.cpp +++ b/engines/lastexpress/sound/sound.cpp @@ -33,7 +33,6 @@ #include "lastexpress/sound/entry.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/helpers.h" #include "lastexpress/graphics.h" #include "lastexpress/lastexpress.h" #include "lastexpress/resource.h" @@ -675,7 +674,7 @@ const char *SoundManager::getDialogName(EntityIndex entity) const { ////////////////////////////////////////////////////////////////////////// // Letters & Messages ////////////////////////////////////////////////////////////////////////// -void SoundManager::readText(int id){ +void SoundManager::readText(int id) { if (!_queue->isBuffered(kEntityTables4)) return; @@ -1330,23 +1329,23 @@ void SoundManager::playLoopingSound(int param) { } } else { switch (getEntityData(kEntityPlayer)->car) { - case 1: - case 6: + case kCarBaggageRear: + case kCarBaggage: partNumber = 4; break; - case 2: - case 3: - case 4: - case 5: + case kCarKronos: + case kCarGreenSleeping: + case kCarRedSleeping: + case kCarRestaurant: partNumber = 1; break; - case 7: + case kCarCoalTender: partNumber = 5; break; - case 8: + case kCarLocomotive: partNumber = 99; break; - case 9: + case kCar9: partNumber = 3; break; default: @@ -1357,13 +1356,13 @@ void SoundManager::playLoopingSound(int param) { } if (partNumber != 99) - sprintf(tmp, "LOOP%d%c.SND", partNumber, _engine->getRandom().getRandomNumber(numLoops[partNumber] - 1) + 'A'); + sprintf(tmp, "LOOP%d%c.SND", partNumber, (char)(_engine->getRandom().getRandomNumber(numLoops[partNumber] - 1) + 'A')); } if (getFlags()->flag_3) fnameLen = 5; - if (!entry || scumm_strnicmp(entry->getName2().c_str(), tmp, fnameLen)) { + if (!entry || scumm_strnicmp(entry->getName2().c_str(), tmp, (uint)fnameLen)) { _loopingSoundDuration = _engine->getRandom().getRandomNumber(319) + 260; if (partNumber != 99) { diff --git a/engines/lure/surface.cpp b/engines/lure/surface.cpp index 4d63647af5..0f13d87fbc 100644 --- a/engines/lure/surface.cpp +++ b/engines/lure/surface.cpp @@ -1360,8 +1360,8 @@ bool CopyProtectionDialog::show() { (*tmpHotspot)->copyTo(&screen.screen()); screen.update(); - } else if ((events.event().kbd.keycode >= Common::KEYCODE_0) && - (events.event().kbd.keycode <= Common::KEYCODE_9)) { + } else if ((events.event().kbd.ascii >= '0') && + (events.event().kbd.ascii <= '9')) { HotspotsList::iterator tmpHotspot = _hotspots.begin(); for (int i = 0; i < _charIndex + 3; i++) ++tmpHotspot; diff --git a/engines/mohawk/detection.cpp b/engines/mohawk/detection.cpp index f0c657897d..5664929948 100644 --- a/engines/mohawk/detection.cpp +++ b/engines/mohawk/detection.cpp @@ -167,7 +167,7 @@ public: } virtual const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const { - return detectGameFilebased(allFiles, Mohawk::fileBased); + return detectGameFilebased(allFiles, fslist, Mohawk::fileBased); } virtual const char *getName() const { diff --git a/engines/mohawk/detection_tables.h b/engines/mohawk/detection_tables.h index 5acc1bb179..55814af1c3 100644 --- a/engines/mohawk/detection_tables.h +++ b/engines/mohawk/detection_tables.h @@ -204,24 +204,6 @@ static const MohawkGameDescription gameDescriptions[] = { }, // Myst Masterpiece Edition - // English Windows - // From clone2727 - { - { - "myst", - "Masterpiece Edition", - AD_ENTRY1("MYST.DAT", "c4cae9f143b5947262e6cb2397e1617e"), - Common::EN_ANY, - Common::kPlatformMacintosh, - ADGF_UNSTABLE, - GUIO1(GUIO_NOASPECT) - }, - GType_MYST, - GF_ME, - 0, - }, - - // Myst Masterpiece Edition // German Windows // From DrMcCoy (Included in "Myst: Die Trilogie") { diff --git a/engines/mohawk/livingbooks.cpp b/engines/mohawk/livingbooks.cpp index 708478a6d8..a0671d18d5 100644 --- a/engines/mohawk/livingbooks.cpp +++ b/engines/mohawk/livingbooks.cpp @@ -3378,11 +3378,10 @@ void LBLiveTextItem::readData(uint16 type, uint16 size, Common::MemoryReadStream LiveTextWord word; word.bounds = _vm->readRect(stream); word.soundId = stream->readUint16(); - // TODO: unknowns - uint16 unknown1 = stream->readUint16(); - uint16 unknown2 = stream->readUint16(); - debug(4, "Word: (%d, %d) to (%d, %d), sound %d, unknowns %04x, %04x", - word.bounds.left, word.bounds.top, word.bounds.right, word.bounds.bottom, word.soundId, unknown1, unknown2); + word.itemType = stream->readUint16(); + word.itemId = stream->readUint16(); + debug(4, "Word: (%d, %d) to (%d, %d), sound %d, item %d (type %d)", + word.bounds.left, word.bounds.top, word.bounds.right, word.bounds.bottom, word.soundId, word.itemId, word.itemType); _words.push_back(word); } @@ -3461,6 +3460,12 @@ void LBLiveTextItem::update() { uint16 soundId = _words[_currentWord].soundId; if (soundId && !_vm->_sound->isPlaying(soundId)) { paletteUpdate(_currentWord, false); + + // TODO: check this in RE + LBItem *item = _vm->getItemById(_words[_currentWord].itemId); + if (item) + item->togglePlaying(false, true); + _currentWord = 0xFFFF; } } diff --git a/engines/mohawk/livingbooks.h b/engines/mohawk/livingbooks.h index 91d6a8cd30..76da7d8219 100644 --- a/engines/mohawk/livingbooks.h +++ b/engines/mohawk/livingbooks.h @@ -537,6 +537,9 @@ protected: struct LiveTextWord { Common::Rect bounds; uint16 soundId; + + uint16 itemType; + uint16 itemId; }; struct LiveTextPhrase { diff --git a/engines/mohawk/myst.cpp b/engines/mohawk/myst.cpp index 0efd412bd0..9c0e642203 100644 --- a/engines/mohawk/myst.cpp +++ b/engines/mohawk/myst.cpp @@ -98,11 +98,6 @@ MohawkEngine_Myst::MohawkEngine_Myst(OSystem *syst, const MohawkGameDescription _view.soundListVolume = NULL; _view.scriptResCount = 0; _view.scriptResources = NULL; - - if ((getFeatures() & GF_ME) && getPlatform() == Common::kPlatformMacintosh) { - const Common::FSNode gameDataDir(ConfMan.get("path")); - SearchMan.addSubDirectoryMatching(gameDataDir, "CD Data"); - } } MohawkEngine_Myst::~MohawkEngine_Myst() { @@ -205,11 +200,6 @@ static const char *mystFiles[] = { // qtw/myst/libelev.mov: libup.mov is basically the same with sound Common::String MohawkEngine_Myst::wrapMovieFilename(const Common::String &movieName, uint16 stack) { - // The Macintosh release of Myst ME stores its videos in a different folder - // WORKAROUND: The gear rotation videos are not in the CD Data folder. See above comments. - if ((getFeatures() & GF_ME) && getPlatform() == Common::kPlatformMacintosh && !movieName.matchString("cl1wg?")) - return Common::String("CD Data/m/") + movieName + ".mov"; - Common::String prefix; switch (stack) { @@ -498,52 +488,32 @@ void MohawkEngine_Myst::changeToStack(uint16 stack, uint16 card, uint16 linkSrcS if (!_mhk[0]->openFile(mystFiles[_curStack])) error("Could not open %s", mystFiles[_curStack]); - if (getPlatform() == Common::kPlatformMacintosh) - _gfx->loadExternalPictureFile(_curStack); - _runExitScript = false; // Clear the resource cache and the image cache _cache.clear(); _gfx->clearCache(); - // Play Flyby Entry Movie on Masterpiece Edition. The Macintosh version is currently hooked - // up to the Cinepak versions of the video (the 'c' suffix) until the SVQ1 decoder is completed. + // Play Flyby Entry Movie on Masterpiece Edition. const char *flyby = 0; if (getFeatures() & GF_ME) { switch (_curStack) { case kSeleniticStack: - if (getPlatform() == Common::kPlatformMacintosh) - flyby = "FLY_SEc"; - else - flyby = "selenitic flyby"; + flyby = "selenitic flyby"; break; case kStoneshipStack: - if (getPlatform() == Common::kPlatformMacintosh) - flyby = "FLY_STc"; - else - flyby = "stoneship flyby"; + flyby = "stoneship flyby"; break; // Myst Flyby Movie not used in Original Masterpiece Edition Engine case kMystStack: - if (_tweaksEnabled) { - if (getPlatform() == Common::kPlatformMacintosh) - flyby = "FLY_MYc"; - else - flyby = "myst flyby"; - } + if (_tweaksEnabled) + flyby = "myst flyby"; break; case kMechanicalStack: - if (getPlatform() == Common::kPlatformMacintosh) - flyby = "FLY_MEc"; - else - flyby = "mech age flyby"; + flyby = "mech age flyby"; break; case kChannelwoodStack: - if (getPlatform() == Common::kPlatformMacintosh) - flyby = "FLY_CHc"; - else - flyby = "channelwood flyby"; + flyby = "channelwood flyby"; break; default: break; diff --git a/engines/mohawk/myst_graphics.cpp b/engines/mohawk/myst_graphics.cpp index ae80dd5538..2df0f7e6ba 100644 --- a/engines/mohawk/myst_graphics.cpp +++ b/engines/mohawk/myst_graphics.cpp @@ -49,8 +49,6 @@ MystGraphics::MystGraphics(MohawkEngine_Myst* vm) : GraphicsManager(), _vm(vm) { if (_pixelFormat.bytesPerPixel == 1) error("Myst requires greater than 256 colors to run"); - _pictureFile.entries = NULL; - // Initialize our buffer _backBuffer = new Graphics::Surface(); _backBuffer->create(_vm->_system->getWidth(), _vm->_system->getHeight(), _pixelFormat); @@ -61,122 +59,50 @@ MystGraphics::MystGraphics(MohawkEngine_Myst* vm) : GraphicsManager(), _vm(vm) { MystGraphics::~MystGraphics() { delete _bmpDecoder; - delete[] _pictureFile.entries; _backBuffer->free(); delete _backBuffer; } -static const char *s_picFileNames[] = { - "CHpics", - "", - "", - "DUpics", - "INpics", - "", - "MEpics", - "MYpics", - "SEpics", - "", - "", - "STpics" -}; - -void MystGraphics::loadExternalPictureFile(uint16 stack) { - if (_vm->getPlatform() != Common::kPlatformMacintosh) - return; - - if (_pictureFile.picFile.isOpen()) - _pictureFile.picFile.close(); - delete[] _pictureFile.entries; - - if (!scumm_stricmp(s_picFileNames[stack], "")) - return; - - if (!_pictureFile.picFile.open(s_picFileNames[stack])) - error ("Could not open external picture file \'%s\'", s_picFileNames[stack]); - - _pictureFile.pictureCount = _pictureFile.picFile.readUint32BE(); - _pictureFile.entries = new PictureFile::PictureEntry[_pictureFile.pictureCount]; - - for (uint32 i = 0; i < _pictureFile.pictureCount; i++) { - _pictureFile.entries[i].offset = _pictureFile.picFile.readUint32BE(); - _pictureFile.entries[i].size = _pictureFile.picFile.readUint32BE(); - _pictureFile.entries[i].id = _pictureFile.picFile.readUint16BE(); - _pictureFile.entries[i].type = _pictureFile.picFile.readUint16BE(); - _pictureFile.entries[i].width = _pictureFile.picFile.readUint16BE(); - _pictureFile.entries[i].height = _pictureFile.picFile.readUint16BE(); +MohawkSurface *MystGraphics::decodeImage(uint16 id) { + // We need to grab the image from the current stack archive, however, we don't know + // if it's a PICT or WDIB resource. If it's Myst ME it's most likely a PICT, and if it's + // original it's definitely a WDIB. However, Myst ME throws us another curve ball in + // that PICT resources can contain WDIB's instead of PICT's. + Common::SeekableReadStream *dataStream = NULL; + + if (_vm->getFeatures() & GF_ME && _vm->hasResource(ID_PICT, id)) { + // The PICT resource exists. However, it could still contain a MystBitmap + // instead of a PICT image... + dataStream = _vm->getResource(ID_PICT, id); + } else { + // No PICT, so the WDIB must exist. Let's go grab it. + dataStream = _vm->getResource(ID_WDIB, id); } -} -MohawkSurface *MystGraphics::decodeImage(uint16 id) { - MohawkSurface *mhkSurface = 0; + bool isPict = false; - // Myst ME uses JPEG/PICT images instead of compressed Windows Bitmaps for room images, - // though there are a few weird ones that use that format. For further nonsense with images, - // the Macintosh version stores images in external "picture files." We check them before - // going to check for a PICT resource. - if (_vm->getFeatures() & GF_ME && _vm->getPlatform() == Common::kPlatformMacintosh && _pictureFile.picFile.isOpen()) { - for (uint32 i = 0; i < _pictureFile.pictureCount; i++) - if (_pictureFile.entries[i].id == id) { - if (_pictureFile.entries[i].type == 0) { - Graphics::JPEGDecoder jpeg; - Common::SeekableSubReadStream subStream(&_pictureFile.picFile, _pictureFile.entries[i].offset, _pictureFile.entries[i].offset + _pictureFile.entries[i].size); - - if (!jpeg.loadStream(subStream)) - error("Could not decode Myst ME Mac JPEG"); - - mhkSurface = new MohawkSurface(jpeg.getSurface()->convertTo(_pixelFormat)); - } else if (_pictureFile.entries[i].type == 1) { - Graphics::PICTDecoder pict; - Common::SeekableSubReadStream subStream(&_pictureFile.picFile, _pictureFile.entries[i].offset, _pictureFile.entries[i].offset + _pictureFile.entries[i].size); - - if (!pict.loadStream(subStream)) - error("Could not decode Myst ME Mac PICT"); - - mhkSurface = new MohawkSurface(pict.getSurface()->convertTo(_pixelFormat)); - } else - error ("Unknown Picture File type %d", _pictureFile.entries[i].type); - break; - } + if (_vm->getFeatures() & GF_ME) { + // Here we detect whether it's really a PICT or a WDIB. Since a MystBitmap + // would be compressed, there's no way to detect for the BM without a hack. + // So, we search for the PICT version opcode for detection. + dataStream->seek(512 + 10); // 512 byte pict header + isPict = (dataStream->readUint32BE() == 0x001102FF); + dataStream->seek(0); } - // We're not using the external Mac files, so it's time to delve into the main Mohawk - // archives. However, we still don't know if it's a PICT or WDIB resource. If it's Myst - // ME it's most likely a PICT, and if it's original it's definitely a WDIB. However, - // Myst ME throws us another curve ball in that PICT resources can contain WDIB's instead - // of PICT's. - if (!mhkSurface) { - bool isPict = false; - Common::SeekableReadStream *dataStream = NULL; - - if (_vm->getFeatures() & GF_ME && _vm->hasResource(ID_PICT, id)) { - // The PICT resource exists. However, it could still contain a MystBitmap - // instead of a PICT image... - dataStream = _vm->getResource(ID_PICT, id); - } else // No PICT, so the WDIB must exist. Let's go grab it. - dataStream = _vm->getResource(ID_WDIB, id); - - if (_vm->getFeatures() & GF_ME) { - // Here we detect whether it's really a PICT or a WDIB. Since a MystBitmap - // would be compressed, there's no way to detect for the BM without a hack. - // So, we search for the PICT version opcode for detection. - dataStream->seek(512 + 10); // 512 byte pict header - isPict = (dataStream->readUint32BE() == 0x001102FF); - dataStream->seek(0); - } + MohawkSurface *mhkSurface = 0; - if (isPict) { - Graphics::PICTDecoder pict; + if (isPict) { + Graphics::PICTDecoder pict; - if (!pict.loadStream(*dataStream)) - error("Could not decode Myst ME PICT"); + if (!pict.loadStream(*dataStream)) + error("Could not decode Myst ME PICT"); - mhkSurface = new MohawkSurface(pict.getSurface()->convertTo(_pixelFormat)); - } else { - mhkSurface = _bmpDecoder->decodeImage(dataStream); - mhkSurface->convertToTrueColor(); - } + mhkSurface = new MohawkSurface(pict.getSurface()->convertTo(_pixelFormat)); + } else { + mhkSurface = _bmpDecoder->decodeImage(dataStream); + mhkSurface->convertToTrueColor(); } assert(mhkSurface); diff --git a/engines/mohawk/myst_graphics.h b/engines/mohawk/myst_graphics.h index 20fd46c5b9..de8fe521e6 100644 --- a/engines/mohawk/myst_graphics.h +++ b/engines/mohawk/myst_graphics.h @@ -43,7 +43,6 @@ public: MystGraphics(MohawkEngine_Myst*); ~MystGraphics(); - void loadExternalPictureFile(uint16 stack); void copyImageSectionToScreen(uint16 image, Common::Rect src, Common::Rect dest); void copyImageSectionToBackBuffer(uint16 image, Common::Rect src, Common::Rect dest); void copyImageToScreen(uint16 image, Common::Rect dest); @@ -66,20 +65,6 @@ private: MohawkEngine_Myst *_vm; MystBitmap *_bmpDecoder; - struct PictureFile { - uint32 pictureCount; - struct PictureEntry { - uint32 offset; - uint32 size; - uint16 id; - uint16 type; - uint16 width; - uint16 height; - } *entries; - - Common::File picFile; - } _pictureFile; - Graphics::Surface *_backBuffer; Graphics::PixelFormat _pixelFormat; Common::Rect _viewport; diff --git a/engines/mohawk/myst_stacks/dni.cpp b/engines/mohawk/myst_stacks/dni.cpp index cae165ccf0..d103105c2d 100644 --- a/engines/mohawk/myst_stacks/dni.cpp +++ b/engines/mohawk/myst_stacks/dni.cpp @@ -109,7 +109,7 @@ void Dni::o_handPage(uint16 op, uint16 var, uint16 argc, uint16 *argv) { _vm->setMainCursor(kDefaultMystCursor); // Play movie end (atrus leaving) - _vm->_video->setVideoBounds(atrus, Audio::Timestamp(0, 14813, 600), Audio::Timestamp(0xFFFFFFFF)); + _vm->_video->setVideoBounds(atrus, Audio::Timestamp(0, 14813, 600), _vm->_video->getDuration(atrus)); _vm->_video->setVideoLooping(atrus, false); _atrusLeft = true; diff --git a/engines/mohawk/myst_stacks/intro.cpp b/engines/mohawk/myst_stacks/intro.cpp index a5f608dbbf..545b97d956 100644 --- a/engines/mohawk/myst_stacks/intro.cpp +++ b/engines/mohawk/myst_stacks/intro.cpp @@ -100,12 +100,8 @@ void Intro::introMovies_run() { switch (_introStep) { case 0: - // Play the Mattel (or UbiSoft) logo in the Myst ME Mac version - if ((_vm->getFeatures() & GF_ME) && _vm->getPlatform() == Common::kPlatformMacintosh) { - _vm->_video->playMovie(_vm->wrapMovieFilename("mattel", kIntroStack)); - _introStep = 1; - } else - _introStep = 2; + _introStep = 1; + _vm->_video->playMovie(_vm->wrapMovieFilename("broder", kIntroStack)); break; case 1: if (!_vm->_video->isVideoPlaying()) @@ -113,10 +109,7 @@ void Intro::introMovies_run() { break; case 2: _introStep = 3; - if ((_vm->getFeatures() & GF_ME) && _vm->getPlatform() == Common::kPlatformMacintosh) - _vm->_video->playMovie(_vm->wrapMovieFilename("presto", kIntroStack)); - else - _vm->_video->playMovie(_vm->wrapMovieFilename("broder", kIntroStack)); + _vm->_video->playMovie(_vm->wrapMovieFilename("cyanlogo", kIntroStack)); break; case 3: if (!_vm->_video->isVideoPlaying()) @@ -124,21 +117,13 @@ void Intro::introMovies_run() { break; case 4: _introStep = 5; - _vm->_video->playMovie(_vm->wrapMovieFilename("cyanlogo", kIntroStack)); - break; - case 5: - if (!_vm->_video->isVideoPlaying()) - _introStep = 6; - break; - case 6: - _introStep = 7; if (!(_vm->getFeatures() & GF_DEMO)) // The demo doesn't have the intro video _vm->_video->playMovie(_vm->wrapMovieFilename("intro", kIntroStack)); break; - case 7: + case 5: if (!_vm->_video->isVideoPlaying()) - _introStep = 8; + _introStep = 6; break; default: if (_vm->getFeatures() & GF_DEMO) diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp index e54d6fefa2..32613c6185 100644 --- a/engines/mohawk/riven.cpp +++ b/engines/mohawk/riven.cpp @@ -646,7 +646,7 @@ Common::String MohawkEngine_Riven::getName(uint16 nameResource, uint16 nameID) { } delete nameStream; - delete [] stringOffsets; + delete[] stringOffsets; return name; } diff --git a/engines/mohawk/video.cpp b/engines/mohawk/video.cpp index 18d609c513..0ed4f38b53 100644 --- a/engines/mohawk/video.cpp +++ b/engines/mohawk/video.cpp @@ -29,6 +29,7 @@ #include "common/textconsole.h" #include "common/system.h" +#include "graphics/palette.h" #include "graphics/surface.h" #include "video/qt_decoder.h" @@ -43,13 +44,12 @@ void VideoEntry::clear() { loop = false; enabled = false; start = Audio::Timestamp(0, 1); - end = Audio::Timestamp(0xFFFFFFFF, 1); // Largest possible, there is an endOfVideo() check anyway filename.clear(); id = -1; } bool VideoEntry::endOfVideo() { - return !video || video->endOfVideo() || video->getTime() >= (uint)end.msecs(); + return !video || video->endOfVideo(); } VideoManager::VideoManager(MohawkEngine* vm) : _vm(vm) { @@ -207,7 +207,7 @@ bool VideoManager::updateMovies() { // Remove any videos that are over if (_videoStreams[i].endOfVideo()) { if (_videoStreams[i].loop) { - _videoStreams[i]->seekToTime(_videoStreams[i].start); + _videoStreams[i]->seek(_videoStreams[i].start); } else { // Check the video time one last time before deleting it _vm->doVideoTimer(i, true); @@ -239,7 +239,7 @@ bool VideoManager::updateMovies() { frame = convertedFrame; } else if (pixelFormat.bytesPerPixel == 1 && _videoStreams[i]->hasDirtyPalette()) { // Set the palette when running in 8bpp mode only - _videoStreams[i]->setSystemPalette(); + _vm->_system->getPaletteManager()->setPalette(_videoStreams[i]->getPalette(), 0, 256); } // Clip the width/height to make sure we stay on the screen (Myst does this a few times) @@ -394,6 +394,8 @@ VideoHandle VideoManager::createVideoHandle(uint16 id, uint16 x, uint16 y, bool entry.loop = loop; entry.enabled = true; + entry->start(); + // Search for any deleted videos so we can take a formerly used slot for (uint32 i = 0; i < _videoStreams.size(); i++) if (!_videoStreams[i].video) { @@ -430,6 +432,7 @@ VideoHandle VideoManager::createVideoHandle(const Common::String &filename, uint entry->loadStream(file); entry->setVolume(volume); + entry->start(); // Search for any deleted videos so we can take a formerly used slot for (uint32 i = 0; i < _videoStreams.size(); i++) @@ -492,7 +495,7 @@ uint32 VideoManager::getTime(VideoHandle handle) { uint32 VideoManager::getDuration(VideoHandle handle) { assert(handle != NULL_VID_HANDLE); - return _videoStreams[handle]->getDuration(); + return _videoStreams[handle]->getDuration().msecs(); } bool VideoManager::endOfVideo(VideoHandle handle) { @@ -511,14 +514,13 @@ bool VideoManager::isVideoPlaying() { void VideoManager::setVideoBounds(VideoHandle handle, Audio::Timestamp start, Audio::Timestamp end) { assert(handle != NULL_VID_HANDLE); _videoStreams[handle].start = start; - _videoStreams[handle].end = end; - _videoStreams[handle]->seekToTime(start); + _videoStreams[handle]->setEndTime(end); + _videoStreams[handle]->seek(start); } void VideoManager::drawVideoFrame(VideoHandle handle, Audio::Timestamp time) { assert(handle != NULL_VID_HANDLE); - _videoStreams[handle].end = Audio::Timestamp(0xffffffff, 1); - _videoStreams[handle]->seekToTime(time); + _videoStreams[handle]->seek(time); updateMovies(); delete _videoStreams[handle].video; _videoStreams[handle].clear(); @@ -526,7 +528,7 @@ void VideoManager::drawVideoFrame(VideoHandle handle, Audio::Timestamp time) { void VideoManager::seekToTime(VideoHandle handle, Audio::Timestamp time) { assert(handle != NULL_VID_HANDLE); - _videoStreams[handle]->seekToTime(time); + _videoStreams[handle]->seek(time); } void VideoManager::setVideoLooping(VideoHandle handle, bool loop) { diff --git a/engines/mohawk/video.h b/engines/mohawk/video.h index 98bcadfb53..9dddcde09b 100644 --- a/engines/mohawk/video.h +++ b/engines/mohawk/video.h @@ -45,19 +45,19 @@ struct MLSTRecord { struct VideoEntry { // Playback variables - Video::SeekableVideoDecoder *video; + Video::VideoDecoder *video; uint16 x; uint16 y; bool loop; bool enabled; - Audio::Timestamp start, end; + Audio::Timestamp start; // Identification Common::String filename; // External video files int id; // Internal Mohawk files // Helper functions - Video::SeekableVideoDecoder *operator->() const { assert(video); return video; } // TODO: Remove this eventually + Video::VideoDecoder *operator->() const { assert(video); return video; } // TODO: Remove this eventually void clear(); bool endOfVideo(); }; diff --git a/engines/parallaction/disk_br.cpp b/engines/parallaction/disk_br.cpp index 5e39c893db..ee981a2c7d 100644 --- a/engines/parallaction/disk_br.cpp +++ b/engines/parallaction/disk_br.cpp @@ -617,7 +617,7 @@ GfxObj* AmigaDisk_br::loadStatic(const char* name) { } } - delete []shadow; + delete[] shadow; delete stream; } diff --git a/engines/parallaction/disk_ns.cpp b/engines/parallaction/disk_ns.cpp index 839b2c6834..8d4afd6847 100644 --- a/engines/parallaction/disk_ns.cpp +++ b/engines/parallaction/disk_ns.cpp @@ -832,7 +832,7 @@ void AmigaDisk_ns::decodeCnv(byte *data, uint16 numFrames, uint16 width, uint16 assert(buf); stream->read(buf, rawsize); unpackBitmap(data, buf, numFrames, bytesPerPlane, height); - delete []buf; + delete[] buf; } #undef NUM_PLANES diff --git a/engines/parallaction/graphics.cpp b/engines/parallaction/graphics.cpp index 6868505c52..9855830478 100644 --- a/engines/parallaction/graphics.cpp +++ b/engines/parallaction/graphics.cpp @@ -766,7 +766,7 @@ Gfx::~Gfx() { freeLabels(); - delete []_unpackedBitmap; + delete[] _unpackedBitmap; return; } diff --git a/engines/parallaction/graphics.h b/engines/parallaction/graphics.h index b43dd193b5..f8cb4b3647 100644 --- a/engines/parallaction/graphics.h +++ b/engines/parallaction/graphics.h @@ -144,7 +144,7 @@ public: ~Cnv() { if (_freeData) - delete []_data; + delete[] _data; } byte* getFramePtr(uint16 index) { diff --git a/engines/parallaction/sound_ns.cpp b/engines/parallaction/sound_ns.cpp index 3cc25b36b0..dcc71e4f2f 100644 --- a/engines/parallaction/sound_ns.cpp +++ b/engines/parallaction/sound_ns.cpp @@ -237,7 +237,7 @@ AmigaSoundMan_ns::~AmigaSoundMan_ns() { stopSfx(2); stopSfx(3); - delete []beepSoundBuffer; + delete[] beepSoundBuffer; } Audio::AudioStream *AmigaSoundMan_ns::loadChannelData(const char *filename, Channel *ch, bool looping) { diff --git a/engines/pegasus/movie.cpp b/engines/pegasus/movie.cpp index 5d393de492..fc722e5043 100644 --- a/engines/pegasus/movie.cpp +++ b/engines/pegasus/movie.cpp @@ -73,8 +73,6 @@ void Movie::initFromMovieFile(const Common::String &fileName, bool transparent) error("Could not load video '%s'", fileName.c_str()); } - _video->pauseVideo(true); - Common::Rect bounds(0, 0, _video->getWidth(), _video->getHeight()); sizeElement(_video->getWidth(), _video->getHeight()); _movieBox = bounds; @@ -83,7 +81,7 @@ void Movie::initFromMovieFile(const Common::String &fileName, bool transparent) allocateSurface(bounds); setStart(0, getScale()); - setStop(_video->getDuration() * getScale() / 1000, getScale()); + setStop(_video->getDuration().convertToFramerate(getScale()).totalNumberOfFrames(), getScale()); } void Movie::redrawMovieWorld() { @@ -149,7 +147,7 @@ void Movie::setTime(const TimeValue time, const TimeScale scale) { else if (timeFrac >= Common::Rational(_stopTime, _stopScale)) return; - _video->seekToTime(Audio::Timestamp(0, timeFrac.getNumerator(), timeFrac.getDenominator())); + _video->seek(Audio::Timestamp(0, timeFrac.getNumerator(), timeFrac.getDenominator())); _time = timeFrac; _lastMillis = 0; } @@ -166,15 +164,15 @@ void Movie::setRate(const Common::Rational rate) { } void Movie::start() { - if (_video && _video->isPaused()) - _video->pauseVideo(false); + if (_video) + _video->start(); TimeBase::start(); } void Movie::stop() { - if (_video && !_video->isPaused()) - _video->pauseVideo(true); + if (_video) + _video->stop(); TimeBase::stop(); } @@ -199,7 +197,7 @@ TimeValue Movie::getDuration(const TimeScale scale) const { // but the problem is that too much code requires this function to behave this way... if (_video) - return _video->getDuration() * ((scale == 0) ? getScale() : scale) / 1000; + return _video->getDuration().convertToFramerate(((scale == 0) ? getScale() : scale)).totalNumberOfFrames(); return 0; } diff --git a/engines/pegasus/movie.h b/engines/pegasus/movie.h index 593442fa44..efe4a7c244 100644 --- a/engines/pegasus/movie.h +++ b/engines/pegasus/movie.h @@ -32,7 +32,7 @@ #include "pegasus/surface.h" namespace Video { - class SeekableVideoDecoder; +class VideoDecoder; } namespace Pegasus { @@ -65,13 +65,13 @@ public: virtual TimeValue getDuration(const TimeScale = 0) const; // *** HACK ALERT - Video::SeekableVideoDecoder *getMovie() { return _video; } + Video::VideoDecoder *getMovie() { return _video; } void setVolume(uint16); protected: void updateTime(); - Video::SeekableVideoDecoder *_video; + Video::VideoDecoder *_video; Common::Rect _movieBox; }; diff --git a/engines/pegasus/neighborhood/caldoria/caldoria.cpp b/engines/pegasus/neighborhood/caldoria/caldoria.cpp index ce62b17265..a6806d5c46 100644 --- a/engines/pegasus/neighborhood/caldoria/caldoria.cpp +++ b/engines/pegasus/neighborhood/caldoria/caldoria.cpp @@ -205,13 +205,11 @@ void Caldoria::start() { error("Could not load pullback movie"); // Draw the first frame so we can fade to it - pullbackMovie->pauseVideo(true); const Graphics::Surface *frame = pullbackMovie->decodeNextFrame(); assert(frame); assert(frame->format == g_system->getScreenFormat()); g_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, 64, 112, frame->w, frame->h); _vm->_gfx->doFadeInSync(kTwoSeconds * kFifteenTicksPerSecond, kFifteenTicksPerSecond); - pullbackMovie->pauseVideo(false); bool saveAllowed = _vm->swapSaveAllowed(false); bool openAllowed = _vm->swapLoadAllowed(false); @@ -219,6 +217,8 @@ void Caldoria::start() { bool skipped = false; Input input; + pullbackMovie->start(); + while (!_vm->shouldQuit() && !pullbackMovie->endOfVideo()) { if (pullbackMovie->needsUpdate()) { frame = pullbackMovie->decodeNextFrame(); diff --git a/engines/pegasus/neighborhood/mars/mars.cpp b/engines/pegasus/neighborhood/mars/mars.cpp index c950a85897..816a13f01d 100644 --- a/engines/pegasus/neighborhood/mars/mars.cpp +++ b/engines/pegasus/neighborhood/mars/mars.cpp @@ -2405,6 +2405,8 @@ void Mars::doCanyonChase() { if (!video->loadFile("Images/Mars/M44ESA.movie")) error("Could not load interface->shuttle transition video"); + video->start(); + while (!_vm->shouldQuit() && !video->endOfVideo()) { if (video->needsUpdate()) { const Graphics::Surface *frame = video->decodeNextFrame(); @@ -3024,6 +3026,8 @@ void Mars::transportToRobotShip() { if (!video->loadFile("Images/Mars/M98EAE.movie")) error("Could not load shuttle->interface transition video"); + video->start(); + while (!_vm->shouldQuit() && !video->endOfVideo()) { if (video->needsUpdate()) { const Graphics::Surface *frame = video->decodeNextFrame(); diff --git a/engines/pegasus/pegasus.cpp b/engines/pegasus/pegasus.cpp index 0e426a2943..bbe2e0e212 100644 --- a/engines/pegasus/pegasus.cpp +++ b/engines/pegasus/pegasus.cpp @@ -289,8 +289,10 @@ void PegasusEngine::runIntro() { bool skipped = false; - Video::SeekableVideoDecoder *video = new Video::QuickTimeDecoder(); + Video::VideoDecoder *video = new Video::QuickTimeDecoder(); if (video->loadFile(_introDirectory + "/BandaiLogo.movie")) { + video->start(); + while (!shouldQuit() && !video->endOfVideo() && !skipped) { if (video->needsUpdate()) { const Graphics::Surface *frame = video->decodeNextFrame(); @@ -320,7 +322,8 @@ void PegasusEngine::runIntro() { if (!video->loadFile(_introDirectory + "/Big Movie.movie")) error("Could not load intro movie"); - video->seekToTime(Audio::Timestamp(0, 10 * 600, 600)); + video->seek(Audio::Timestamp(0, 10 * 600, 600)); + video->start(); playMovieScaled(video, 0, 0); @@ -671,13 +674,14 @@ void PegasusEngine::introTimerExpired() { bool skipped = false; - Video::SeekableVideoDecoder *video = new Video::QuickTimeDecoder(); + Video::VideoDecoder *video = new Video::QuickTimeDecoder(); if (!video->loadFile(_introDirectory + "/LilMovie.movie")) error("Failed to load little movie"); bool saveAllowed = swapSaveAllowed(false); bool openAllowed = swapLoadAllowed(false); + video->start(); skipped = playMovieScaled(video, 0, 0); delete video; @@ -815,13 +819,14 @@ void PegasusEngine::doGameMenuCommand(const GameMenuCommand command) { _gfx->clearScreen(); _gfx->updateDisplay(); - Video::SeekableVideoDecoder *video = new Video::QuickTimeDecoder(); + Video::VideoDecoder *video = new Video::QuickTimeDecoder(); if (!video->loadFile(_introDirectory + "/Closing.movie")) error("Could not load closing movie"); uint16 x = (640 - video->getWidth() * 2) / 2; uint16 y = (480 - video->getHeight() * 2) / 2; + video->start(); playMovieScaled(video, x, y); delete video; @@ -1261,7 +1266,7 @@ void PegasusEngine::checkFlashlight() { _neighborhood->checkFlashlight(); } -bool PegasusEngine::playMovieScaled(Video::SeekableVideoDecoder *video, uint16 x, uint16 y) { +bool PegasusEngine::playMovieScaled(Video::VideoDecoder *video, uint16 x, uint16 y) { bool skipped = false; while (!shouldQuit() && !video->endOfVideo() && !skipped) { @@ -2084,10 +2089,12 @@ void PegasusEngine::playEndMessage() { void PegasusEngine::doSubChase() { static const uint32 endTime = 133200 * 1000 / 600; - Video::SeekableVideoDecoder *video = new Video::QuickTimeDecoder(); + Video::VideoDecoder *video = new Video::QuickTimeDecoder(); if (!video->loadFile("Images/Norad Alpha/Sub Chase Movie")) error("Failed to load sub chase"); + video->start(); + while (!shouldQuit() && !video->endOfVideo() && video->getTime() < endTime) { if (video->needsUpdate()) { const Graphics::Surface *frame = video->decodeNextFrame(); diff --git a/engines/pegasus/pegasus.h b/engines/pegasus/pegasus.h index a1b4cff9ab..1ee0136c30 100644 --- a/engines/pegasus/pegasus.h +++ b/engines/pegasus/pegasus.h @@ -50,7 +50,7 @@ namespace Common { } namespace Video { - class SeekableVideoDecoder; + class VideoDecoder; } namespace Pegasus { @@ -253,7 +253,7 @@ private: Hotspot _returnHotspot; InputHandler *_savedHandler; void showTempScreen(const Common::String &fileName); - bool playMovieScaled(Video::SeekableVideoDecoder *video, uint16 x, uint16 y); + bool playMovieScaled(Video::VideoDecoder *video, uint16 x, uint16 y); void throwAwayEverything(); void shellGameInput(const Input &input, const Hotspot *cursorSpot); Common::RandomSource *_rnd; diff --git a/engines/pegasus/surface.cpp b/engines/pegasus/surface.cpp index 897305f2a3..343bc415f3 100644 --- a/engines/pegasus/surface.cpp +++ b/engines/pegasus/surface.cpp @@ -108,8 +108,8 @@ void Surface::getImageFromPICTStream(Common::SeekableReadStream *stream) { _bounds = Common::Rect(0, 0, _surface->w, _surface->h); } -void Surface::getImageFromMovieFrame(Video::SeekableVideoDecoder *video, TimeValue time) { - video->seekToTime(Audio::Timestamp(0, time, 600)); +void Surface::getImageFromMovieFrame(Video::VideoDecoder *video, TimeValue time) { + video->seek(Audio::Timestamp(0, time, 600)); const Graphics::Surface *frame = video->decodeNextFrame(); if (frame) { @@ -344,7 +344,7 @@ void Frame::initFromPICTResource(Common::MacResManager *resFork, uint16 id, bool _transparent = transparent; } -void Frame::initFromMovieFrame(Video::SeekableVideoDecoder *video, TimeValue time, bool transparent) { +void Frame::initFromMovieFrame(Video::VideoDecoder *video, TimeValue time, bool transparent) { getImageFromMovieFrame(video, time); _transparent = transparent; } @@ -381,7 +381,7 @@ void Picture::initFromPICTResource(Common::MacResManager *resFork, uint16 id, bo sizeElement(surfaceBounds.width(), surfaceBounds.height()); } -void Picture::initFromMovieFrame(Video::SeekableVideoDecoder *video, TimeValue time, bool transparent) { +void Picture::initFromMovieFrame(Video::VideoDecoder *video, TimeValue time, bool transparent) { Frame::initFromMovieFrame(video, time, transparent); Common::Rect surfaceBounds; diff --git a/engines/pegasus/surface.h b/engines/pegasus/surface.h index 34a88dbd53..311fb50419 100644 --- a/engines/pegasus/surface.h +++ b/engines/pegasus/surface.h @@ -41,7 +41,7 @@ namespace Graphics { } namespace Video { - class SeekableVideoDecoder; + class VideoDecoder; } namespace Pegasus { @@ -76,7 +76,7 @@ public: virtual void getImageFromPICTFile(const Common::String &fileName); virtual void getImageFromPICTResource(Common::MacResManager *resFork, uint16 id); - virtual void getImageFromMovieFrame(Video::SeekableVideoDecoder *, TimeValue); + virtual void getImageFromMovieFrame(Video::VideoDecoder *, TimeValue); protected: bool _ownsSurface; @@ -110,7 +110,7 @@ public: virtual void initFromPICTFile(const Common::String &fileName, bool transparent = false); virtual void initFromPICTResource(Common::MacResManager *resFork, uint16 id, bool transparent = false); - virtual void initFromMovieFrame(Video::SeekableVideoDecoder *, TimeValue, bool transparent = false); + virtual void initFromMovieFrame(Video::VideoDecoder *, TimeValue, bool transparent = false); }; class SpriteFrame : public Frame { @@ -130,7 +130,7 @@ public: virtual void initFromPICTFile(const Common::String &fileName, bool transparent = false); virtual void initFromPICTResource(Common::MacResManager *resFork, uint16 id, bool transparent = false); - virtual void initFromMovieFrame(Video::SeekableVideoDecoder *, TimeValue, bool transparent = false); + virtual void initFromMovieFrame(Video::VideoDecoder *, TimeValue, bool transparent = false); virtual void draw(const Common::Rect &); }; diff --git a/engines/queen/queen.cpp b/engines/queen/queen.cpp index 3acc87b856..f3b183c84f 100644 --- a/engines/queen/queen.cpp +++ b/engines/queen/queen.cpp @@ -56,8 +56,8 @@ static const PlainGameDescriptor queenGameDescriptor = { }; static const ExtraGuiOption queenExtraGuiOption = { - _s("Floppy intro"), - _s("Use the floppy version's intro (CD version only)"), + _s("Alternative intro"), + _s("Use an alternative game intro (CD version only)"), "alt_intro", false }; diff --git a/engines/saga/detection.cpp b/engines/saga/detection.cpp index d39ec34cc8..9c178559f2 100644 --- a/engines/saga/detection.cpp +++ b/engines/saga/detection.cpp @@ -252,9 +252,6 @@ SaveStateDescriptor SagaMetaEngine::querySaveMetaInfos(const char *target, int s debug(0, "Save is for: %s", title); } - desc.setDeletableFlag(true); - desc.setWriteProtectedFlag(false); - if (version >= 6) { Graphics::Surface *const thumbnail = Graphics::loadThumbnail(*in); desc.setThumbnail(thumbnail); diff --git a/engines/saga/introproc_saga2.cpp b/engines/saga/introproc_saga2.cpp index b6470370af..260eca98e6 100644 --- a/engines/saga/introproc_saga2.cpp +++ b/engines/saga/introproc_saga2.cpp @@ -32,6 +32,7 @@ #include "common/keyboard.h" #include "common/system.h" #include "common/textconsole.h" +#include "graphics/palette.h" #include "graphics/surface.h" #include "video/smk_decoder.h" @@ -92,7 +93,7 @@ int Scene::FTA2EndProc(FTA2Endings whichEnding) { } void Scene::playMovie(const char *filename) { - Video::SmackerDecoder *smkDecoder = new Video::SmackerDecoder(_vm->_mixer); + Video::SmackerDecoder *smkDecoder = new Video::SmackerDecoder(); if (!smkDecoder->loadFile(filename)) return; @@ -101,6 +102,8 @@ void Scene::playMovie(const char *filename) { uint16 y = (g_system->getHeight() - smkDecoder->getHeight()) / 2; bool skipVideo = false; + smkDecoder->start(); + while (!_vm->shouldQuit() && !smkDecoder->endOfVideo() && !skipVideo) { if (smkDecoder->needsUpdate()) { const Graphics::Surface *frame = smkDecoder->decodeNextFrame(); @@ -108,7 +111,7 @@ void Scene::playMovie(const char *filename) { _vm->_system->copyRectToScreen(frame->pixels, frame->pitch, x, y, frame->w, frame->h); if (smkDecoder->hasDirtyPalette()) - smkDecoder->setSystemPalette(); + _vm->_system->getPaletteManager()->setPalette(smkDecoder->getPalette(), 0, 256); _vm->_system->updateScreen(); } diff --git a/engines/saga/shorten.cpp b/engines/saga/shorten.cpp index 5efc8d1f67..69c27b6a6b 100644 --- a/engines/saga/shorten.cpp +++ b/engines/saga/shorten.cpp @@ -519,9 +519,6 @@ byte *loadShortenFromStream(Common::ReadStream &stream, int &size, int &rate, by if (maxLPC > 0) free(lpc); - if (size > 0) - free(unpackedBuffer); - delete gReader; return unpackedBuffer; } diff --git a/engines/savestate.h b/engines/savestate.h index 6cbdb22edf..c233554657 100644 --- a/engines/savestate.h +++ b/engines/savestate.h @@ -40,6 +40,8 @@ struct Surface; * * Further possibilites are a thumbnail, play time, creation date, * creation time, delete protected, write protection. + * + * Saves are writable and deletable by default. */ class SaveStateDescriptor { public: diff --git a/engines/sci/console.cpp b/engines/sci/console.cpp index 40a6fd1415..1889d53480 100644 --- a/engines/sci/console.cpp +++ b/engines/sci/console.cpp @@ -250,20 +250,18 @@ void Console::postEnter() { #endif if (_videoFile.hasSuffix(".seq")) { - SeqDecoder *seqDecoder = new SeqDecoder(); - seqDecoder->setFrameDelay(_videoFrameDelay); - videoDecoder = seqDecoder; + videoDecoder = new SEQDecoder(_videoFrameDelay); #ifdef ENABLE_SCI32 } else if (_videoFile.hasSuffix(".vmd")) { - videoDecoder = new Video::VMDDecoder(g_system->getMixer()); + videoDecoder = new Video::AdvancedVMDDecoder(); } else if (_videoFile.hasSuffix(".rbt")) { - videoDecoder = new RobotDecoder(g_system->getMixer(), _engine->getPlatform() == Common::kPlatformMacintosh); + videoDecoder = new RobotDecoder(_engine->getPlatform() == Common::kPlatformMacintosh); } else if (_videoFile.hasSuffix(".duk")) { duckMode = true; - videoDecoder = new Video::AviDecoder(g_system->getMixer()); + videoDecoder = new Video::AVIDecoder(); #endif } else if (_videoFile.hasSuffix(".avi")) { - videoDecoder = new Video::AviDecoder(g_system->getMixer()); + videoDecoder = new Video::AVIDecoder(); } else { warning("Unrecognized video type"); } @@ -2987,8 +2985,9 @@ void Console::printKernelCallsFound(int kernelFuncNum, bool showFoundScripts) { // Ignore specific leftover scripts, which require other non-existing scripts if ((_engine->getGameId() == GID_HOYLE3 && itr->getNumber() == 995) || (_engine->getGameId() == GID_KQ5 && itr->getNumber() == 980) || - (_engine->getGameId() == GID_SLATER && itr->getNumber() == 947) || - (_engine->getGameId() == GID_MOTHERGOOSE256 && itr->getNumber() == 980)) { + (_engine->getGameId() == GID_KQ7 && itr->getNumber() == 111) || + (_engine->getGameId() == GID_MOTHERGOOSE256 && itr->getNumber() == 980) || + (_engine->getGameId() == GID_SLATER && itr->getNumber() == 947)) { continue; } diff --git a/engines/sci/detection.cpp b/engines/sci/detection.cpp index 78df3065b2..58ac5f1fa6 100644 --- a/engines/sci/detection.cpp +++ b/engines/sci/detection.cpp @@ -397,8 +397,8 @@ static const ADExtraGuiOptionsMap optionsList[] = { { GAMEOPTION_FB01_MIDI, { - _s("Use IMF/Yahama FB-01 for MIDI output"), - _s("Use an IBM Music Feature card or a Yahama FB-01 FM synth module for MIDI output"), + _s("Use IMF/Yamaha FB-01 for MIDI output"), + _s("Use an IBM Music Feature card or a Yamaha FB-01 FM synth module for MIDI output"), "native_fb01", false } @@ -762,9 +762,6 @@ SaveStateDescriptor SciMetaEngine::querySaveMetaInfos(const char *target, int sl Graphics::Surface *const thumbnail = Graphics::loadThumbnail(*in); desc.setThumbnail(thumbnail); - desc.setDeletableFlag(true); - desc.setWriteProtectedFlag(false); - int day = (meta.saveDate >> 24) & 0xFF; int month = (meta.saveDate >> 16) & 0xFF; int year = meta.saveDate & 0xFFFF; diff --git a/engines/sci/detection_tables.h b/engines/sci/detection_tables.h index 9872973e09..b978f40aba 100644 --- a/engines/sci/detection_tables.h +++ b/engines/sci/detection_tables.h @@ -1238,6 +1238,24 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // King's Quest 5 - English DOS Floppy + // VERSION file reports "0.000.051" + // Supplied by misterhands in bug report #3536863. + // This is the original English version, which has been externally patched to + // Polish in the Polish release below. + {"kq5", "", { + {"resource.map", 0, "70010c20138541f89013bb5e1b30f16a", 7998}, + {"resource.000", 0, "a591bd4b879fc832b8095c0b3befe9e2", 276398}, + {"resource.001", 0, "c0f48d4a7ebeaa6aa074fc98d77423e9", 1018560}, + {"resource.002", 0, "7f188a95acdb60bbe32a8379ba299393", 1307048}, + {"resource.003", 0, "0860785af59518b94d54718dddcd6907", 1348500}, + {"resource.004", 0, "c4745dd1e261c22daa6477961d08bf6c", 1239887}, + {"resource.005", 0, "6556ff8e7c4d1acf6a78aea154daa76c", 1287869}, + {"resource.006", 0, "da82e4beb744731d0a151f1d4922fafa", 1170456}, + {"resource.007", 0, "431def14ca29cdb5e6a5e84d3f38f679", 1240176}, + AD_LISTEND}, + Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // King's Quest 5 - English DOS Floppy (supplied by omer_mor in bug report #3036996) // VERSION file reports "0.000.051" {"kq5", "", { @@ -1308,6 +1326,21 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // King's Quest 5 DOS Spanish Floppy 0.000.062 VGA (5 x 3.5" disks) + // Supplied by dianiu in bug report #3555646 + {"kq5", "", { + {"resource.map", 0, "c09896a2a30c9b002c5cbbc62f5a5c3a", 8169}, + {"resource.000", 0, "1f1d03aead44da46362ff40c0074a3ec", 335871}, + {"resource.001", 0, "d1803ad904127ae091edb274ee8c047f", 1180637}, + {"resource.002", 0, "d9cd5972016f650cc31fb7c2a2b0953a", 1102207}, + {"resource.003", 0, "829c8caeff793f3cfcea2cb01aaa4150", 965586}, + {"resource.004", 0, "0bd9e570ee04b025e43d3075998fae5b", 1117965}, + {"resource.005", 0, "4aaa2e9a69089b9afbaaccbbf2c4e647", 1202936}, + {"resource.006", 0, "65b520e60c4217e6a6572d9edf77193b", 1141985}, + {"resource.007", 0, "f42b0100f0a1c30806814f8648b6bc28", 1145583}, + AD_LISTEND}, + Common::ES_ESP, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // King's Quest 5 - German DOS Floppy (supplied by markcoolio in bug report #2727101, also includes english language) // SCI interpreter version 1.000.060 {"kq5", "", { @@ -1354,8 +1387,10 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::IT_ITA, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, - // King's Quest 5 - Polish DOS Floppy (supplied by jacek909 in bug report #2725722, includes english language?!) + // King's Quest 5 - Polish DOS Floppy (supplied by jacek909 in bug report #2725722) // SCI interpreter version 1.000.060 + // VERSION file reports "0.000.051". + // This is actually an English version with external text resource patches (bug #3536863). {"kq5", "", { {"resource.map", 0, "70010c20138541f89013bb5e1b30f16a", 7998}, {"resource.000", 0, "a591bd4b879fc832b8095c0b3befe9e2", 276398}, @@ -1366,6 +1401,7 @@ static const struct ADGameDescription SciGameDescriptions[] = { {"resource.005", 0, "6556ff8e7c4d1acf6a78aea154daa76c", 1287869}, {"resource.006", 0, "da82e4beb744731d0a151f1d4922fafa", 1170456}, {"resource.007", 0, "431def14ca29cdb5e6a5e84d3f38f679", 1240176}, + {"text.000", 0, "601aa35a3ddeb558e1280e0963e955a2", 1517}, AD_LISTEND}, Common::PL_POL, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, @@ -1447,6 +1483,16 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::DE_DEU, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // King's Quest 6 - Spanish DOS Floppy (from jvprat) + // Executable scanning reports "1.cfs.158", VERSION file reports "1.000.000, July 5, 1994" + // SCI interpreter version 1.001.055 + {"kq6", "", { + {"resource.map", 0, "a73a5ab04b8f60c4b75b946a4dccea5a", 8953}, + {"resource.000", 0, "4da3ad5868a775549a7cc4f72770a58e", 8537260}, + {"resource.msg", 0, "41eed2d3893e1ca6c3695deba4e9d2e8", 267102}, + AD_LISTEND}, + Common::ES_ESP, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // King's Quest 6 - English DOS CD (from the King's Quest Collection) // Executable scanning reports "1.cfs.158", VERSION file reports "1.034 9/11/94 - KQ6 version 1.000.00G" // SCI interpreter version 1.001.054 @@ -1465,16 +1511,6 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformWindows, ADGF_CD, GUIO5(GUIO_NOASPECT, GAMEOPTION_KQ6_WINDOWS_CURSORS, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, - // King's Quest 6 - Spanish DOS CD (from jvprat) - // Executable scanning reports "1.cfs.158", VERSION file reports "1.000.000, July 5, 1994" - // SCI interpreter version 1.001.055 - {"kq6", "CD", { - {"resource.map", 0, "a73a5ab04b8f60c4b75b946a4dccea5a", 8953}, - {"resource.000", 0, "4da3ad5868a775549a7cc4f72770a58e", 8537260}, - {"resource.msg", 0, "41eed2d3893e1ca6c3695deba4e9d2e8", 267102}, - AD_LISTEND}, - Common::ES_ESP, Common::kPlatformPC, ADGF_CD, GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, - // King's Quest 6 - English Macintosh Floppy // VERSION file reports "1.0" {"kq6", "", { @@ -2660,6 +2696,13 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::DE_DEU, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Police Quest 3 - Spanish DOS v1.000 - Supplied by dianiu in bug report #3555647 + {"pq3", "", { + {"resource.map", 0, "ffa0b4631c4e36d69631256d19ba29e7", 5421}, + {"resource.000", 0, "5ee460af3d70c06a745cc482b6c783ba", 5410263}, + AD_LISTEND}, + Common::ES_ESP, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Police Quest 3 EGA // Reported by musiclyinspired in bug report #3046573 {"pq3", "", { @@ -2777,6 +2820,31 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Quest for Glory 1 / Hero's Quest - English DOS 3.5" Floppy v1.102 Int#0.000.629 (suppled by digitoxin1 in bug report #3554611) + {"qfg1", "", { + {"resource.map", 0, "b162dbd4632250d4d83bed46d0783c10", 6396}, + {"resource.000", 0, "40332d3ebfc70a4b6a6a0443c2763287", 78800}, + {"resource.001", 0, "a270012fa74445d74c044d1b65a9ff8c", 459835}, + {"resource.002", 0, "e64004e020fdf1813be52b639b08be89", 635561}, + {"resource.003", 0, "f0af87c60ec869946da442833aa5afa8", 640502}, + {"resource.004", 0, "f0af87c60ec869946da442833aa5afa8", 644575}, + AD_LISTEND}, + Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + + // Quest for Glory 1 / Hero's Quest - English DOS 5.25" Floppy v1.102 Int#0.000.629 (suppled by digitoxin1 in bug report #3554611) + {"qfg1", "", { + {"resource.map", 0, "5772a2c1bfae46f26582582c9901121e", 6858}, + {"resource.000", 0, "40332d3ebfc70a4b6a6a0443c2763287", 78800}, + {"resource.001", 0, "a270012fa74445d74c044d1b65a9ff8c", 75090}, + {"resource.002", 0, "d22695c53835dfdece056d86f26c251e", 271354}, + {"resource.003", 0, "3cd085e27078f269b3ece5838812ff41", 258084}, + {"resource.004", 0, "8927c7a04a78f1e76f342db3ccc9d879", 267835}, + {"resource.005", 0, "13d16cc9b90b51e2c8643cdf52a62957", 268807}, + {"resource.006", 0, "48b2b3c964dcbeccb68e984e6d4e97db", 278473}, + {"resource.007", 0, "f0af87c60ec869946da442833aa5afa8", 269237}, + AD_LISTEND}, + Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Quest for Glory 1 / Hero's Quest - English DOS 5.25" Floppy (supplied by markcoolio in bug report #2723843) // Executable scanning reports "0.000.566" {"qfg1", "", { @@ -2864,17 +2932,6 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, - // Quest for Glory 1 (from abevi, bug report #2612718) - {"qfg1", "", { - {"resource.map", 0, "b162dbd4632250d4d83bed46d0783c10", 6396}, - {"resource.000", 0, "40332d3ebfc70a4b6a6a0443c2763287", 78800}, - {"resource.001", 0, "a270012fa74445d74c044d1b65a9ff8c", 459835}, - {"resource.002", 0, "e64004e020fdf1813be52b639b08be89", 635561}, - {"resource.003", 0, "f0af87c60ec869946da442833aa5afa8", 640502}, - {"resource.004", 0, "f0af87c60ec869946da442833aa5afa8", 644575}, - AD_LISTEND}, - Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, - // Quest for Glory 1 - English DOS // SCI interpreter version 0.000.629 {"qfg1", "", { @@ -2981,6 +3038,21 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Quest for Glory 2 - English DOS (supplied by digitoxin1 in bug report #3554614) + // v1.102 9x3.5" (label: Int#11.20.90) + {"qfg2", "", { + {"resource.map", 0, "367023314ea33e3156297402f6c1da49", 8166}, + {"resource.000", 0, "a17e374c4d33b81208c862bc0ffc1a38", 212119}, + {"resource.001", 0, "e08d7887e30b12008c40f9570447711a", 331995}, + {"resource.002", 0, "df137dc7869cab07e1149ba2333c815c", 467461}, + {"resource.003", 0, "df137dc7869cab07e1149ba2333c815c", 502560}, + {"resource.004", 0, "df137dc7869cab07e1149ba2333c815c", 488532}, + {"resource.005", 0, "df137dc7869cab07e1149ba2333c815c", 478574}, + {"resource.006", 0, "b1944bd664ddbd2859cdaa0c4a0d6281", 507489}, + {"resource.007", 0, "cd2de58e27665d5853530de93fae7cd6", 490794}, + AD_LISTEND}, + Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Quest for Glory 2 - English DOS Non-Interactive Demo // Executable scanning reports "1.000.046" {"qfg2", "Demo", { @@ -3536,7 +3608,7 @@ static const struct ADGameDescription SciGameDescriptions[] = { {"resource.map", 0, "ed90a8e3ccc53af6633ff6ab58392bae", 7054}, {"resource.000", 0, "63247e3901ab8963d4eece73747832e0", 5157378}, AD_LISTEND}, - Common::EN_ANY, Common::kPlatformPC, ADGF_CD, GUIO1(GAMEOPTION_SQ4_SILVER_CURSORS) }, + Common::EN_ANY, Common::kPlatformPC, ADGF_CD, GUIO5(GUIO_MIDIGM, GAMEOPTION_SQ4_SILVER_CURSORS, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, // Space Quest 4 - English Windows CD (from the Space Quest Collection) // Executable scanning reports "1.001.064", VERSION file reports "1.0" diff --git a/engines/sci/engine/file.cpp b/engines/sci/engine/file.cpp index 0d575f97dd..a0f7ebf4a2 100644 --- a/engines/sci/engine/file.cpp +++ b/engines/sci/engine/file.cpp @@ -57,11 +57,24 @@ namespace Sci { reg_t file_open(EngineState *s, const Common::String &filename, int mode, bool unwrapFilename) { Common::String englishName = g_sci->getSciLanguageString(filename, K_LANG_ENGLISH); + englishName.toLowercase(); + Common::String wrappedName = unwrapFilename ? g_sci->wrapFilename(englishName) : englishName; Common::SeekableReadStream *inFile = 0; Common::WriteStream *outFile = 0; Common::SaveFileManager *saveFileMan = g_sci->getSaveFileManager(); + bool isCompressed = true; + const SciGameId gameId = g_sci->getGameId(); + if ((gameId == GID_QFG1 || gameId == GID_QFG1VGA || gameId == GID_QFG2 || gameId == GID_QFG3) + && englishName.hasSuffix(".sav")) { + // QFG Characters are saved via the CharSave object. + // We leave them uncompressed so that they can be imported in later QFG + // games. + // Rooms/Scripts: QFG1: 601, QFG2: 840, QFG3/4: 52 + isCompressed = false; + } + if (mode == _K_FILE_MODE_OPEN_OR_FAIL) { // Try to open file, abort if not possible inFile = saveFileMan->openForLoading(wrappedName); @@ -74,12 +87,12 @@ reg_t file_open(EngineState *s, const Common::String &filename, int mode, bool u debugC(kDebugLevelFile, " -> file_open(_K_FILE_MODE_OPEN_OR_FAIL): failed to open file '%s'", englishName.c_str()); } else if (mode == _K_FILE_MODE_CREATE) { // Create the file, destroying any content it might have had - outFile = saveFileMan->openForSaving(wrappedName); + outFile = saveFileMan->openForSaving(wrappedName, isCompressed); if (!outFile) debugC(kDebugLevelFile, " -> file_open(_K_FILE_MODE_CREATE): failed to create file '%s'", englishName.c_str()); } else if (mode == _K_FILE_MODE_OPEN_OR_CREATE) { // Try to open file, create it if it doesn't exist - outFile = saveFileMan->openForSaving(wrappedName); + outFile = saveFileMan->openForSaving(wrappedName, isCompressed); if (!outFile) debugC(kDebugLevelFile, " -> file_open(_K_FILE_MODE_CREATE): failed to create file '%s'", englishName.c_str()); diff --git a/engines/sci/engine/kernel.cpp b/engines/sci/engine/kernel.cpp index c8fe47d9fc..46051ef145 100644 --- a/engines/sci/engine/kernel.cpp +++ b/engines/sci/engine/kernel.cpp @@ -757,13 +757,26 @@ bool Kernel::debugSetFunction(const char *kernelName, int logging, int breakpoin return true; } -void Kernel::setDefaultKernelNames(GameFeatures *features) { - _kernelNames = Common::StringArray(s_defaultKernelNames, ARRAYSIZE(s_defaultKernelNames)); +#ifdef ENABLE_SCI32 +enum { + kKernelEntriesSci2 = 0x8b, + kKernelEntriesGk2Demo = 0xa0, + kKernelEntriesSci21 = 0x9d, + kKernelEntriesSci3 = 0xa1 +}; +#endif + +void Kernel::loadKernelNames(GameFeatures *features) { + _kernelNames.clear(); - // Some (later) SCI versions replaced CanBeHere by CantBeHere - // If vocab.999 exists, the kernel function is still named CanBeHere - if (_selectorCache.cantBeHere != -1) - _kernelNames[0x4d] = "CantBeHere"; + if (getSciVersion() <= SCI_VERSION_1_1) { + _kernelNames = Common::StringArray(s_defaultKernelNames, ARRAYSIZE(s_defaultKernelNames)); + + // Some (later) SCI versions replaced CanBeHere by CantBeHere + // If vocab.999 exists, the kernel function is still named CanBeHere + if (_selectorCache.cantBeHere != -1) + _kernelNames[0x4d] = "CantBeHere"; + } switch (getSciVersion()) { case SCI_VERSION_0_EARLY: @@ -817,66 +830,60 @@ void Kernel::setDefaultKernelNames(GameFeatures *features) { _kernelNames[0x7c] = "Message"; break; - default: - // Use default table for the other versions - break; - } -} - #ifdef ENABLE_SCI32 + case SCI_VERSION_2: + _kernelNames = Common::StringArray(sci2_default_knames, kKernelEntriesSci2); + break; -enum { - kKernelEntriesSci2 = 0x8b, - kKernelEntriesGk2Demo = 0xa0, - kKernelEntriesSci21 = 0x9d, - kKernelEntriesSci3 = 0xa1 -}; - -void Kernel::setKernelNamesSci2() { - _kernelNames = Common::StringArray(sci2_default_knames, kKernelEntriesSci2); -} + case SCI_VERSION_2_1: + if (features->detectSci21KernelType() == SCI_VERSION_2) { + // Some early SCI2.1 games use a modified SCI2 kernel table instead of + // the SCI2.1 kernel table. We detect which version to use based on + // how kDoSound is called from Sound::play(). + // Known games that use this: + // GK2 demo + // KQ7 1.4 + // PQ4 SWAT demo + // LSL6 + // PQ4CD + // QFG4CD + + // This is interesting because they all have the same interpreter + // version (2.100.002), yet they would not be compatible with other + // games of the same interpreter. + + _kernelNames = Common::StringArray(sci2_default_knames, kKernelEntriesGk2Demo); + // OnMe is IsOnMe here, but they should be compatible + _kernelNames[0x23] = "Robot"; // Graph in SCI2 + _kernelNames[0x2e] = "Priority"; // DisposeTextBitmap in SCI2 + } else { + // Normal SCI2.1 kernel table + _kernelNames = Common::StringArray(sci21_default_knames, kKernelEntriesSci21); + } + break; -void Kernel::setKernelNamesSci21(GameFeatures *features) { - // Some SCI games use a modified SCI2 kernel table instead of the - // SCI2.1 kernel table. We detect which version to use based on - // how kDoSound is called from Sound::play(). - // Known games that use this: - // GK2 demo - // KQ7 1.4 - // PQ4 SWAT demo - // LSL6 - // PQ4CD - // QFG4CD - - // This is interesting because they all have the same interpreter - // version (2.100.002), yet they would not be compatible with other - // games of the same interpreter. - - if (getSciVersion() != SCI_VERSION_3 && features->detectSci21KernelType() == SCI_VERSION_2) { - _kernelNames = Common::StringArray(sci2_default_knames, kKernelEntriesGk2Demo); - // OnMe is IsOnMe here, but they should be compatible - _kernelNames[0x23] = "Robot"; // Graph in SCI2 - _kernelNames[0x2e] = "Priority"; // DisposeTextBitmap in SCI2 - } else if (getSciVersion() != SCI_VERSION_3) { - _kernelNames = Common::StringArray(sci21_default_knames, kKernelEntriesSci21); - } else if (getSciVersion() == SCI_VERSION_3) { + case SCI_VERSION_3: _kernelNames = Common::StringArray(sci21_default_knames, kKernelEntriesSci3); - } -} -#endif - -void Kernel::loadKernelNames(GameFeatures *features) { - _kernelNames.clear(); + // In SCI3, some kernel functions have been removed, and others have been added + _kernelNames[0x18] = "Dummy"; // AddMagnify in SCI2.1 + _kernelNames[0x19] = "Dummy"; // DeleteMagnify in SCI2.1 + _kernelNames[0x30] = "Dummy"; // SetScroll in SCI2.1 + _kernelNames[0x39] = "Dummy"; // ShowMovie in SCI2.1 + _kernelNames[0x4c] = "Dummy"; // ScrollWindow in SCI2.1 + _kernelNames[0x56] = "Dummy"; // VibrateMouse in SCI2.1 (only used in QFG4 floppy) + _kernelNames[0x64] = "Dummy"; // AvoidPath in SCI2.1 + _kernelNames[0x66] = "Dummy"; // MergePoly in SCI2.1 + _kernelNames[0x8d] = "MessageBox"; // Dummy in SCI2.1 + _kernelNames[0x9b] = "Minimize"; // Dummy in SCI2.1 -#ifdef ENABLE_SCI32 - if (getSciVersion() >= SCI_VERSION_2_1) - setKernelNamesSci21(features); - else if (getSciVersion() == SCI_VERSION_2) - setKernelNamesSci2(); - else + break; #endif - setDefaultKernelNames(features); + + default: + // Use default table for the other versions + break; + } mapFunctions(); } diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h index 677b790f93..f985a69ebc 100644 --- a/engines/sci/engine/kernel.h +++ b/engines/sci/engine/kernel.h @@ -224,23 +224,6 @@ public: private: /** - * Sets the default kernel function names, based on the SCI version used. - */ - void setDefaultKernelNames(GameFeatures *features); - -#ifdef ENABLE_SCI32 - /** - * Sets the default kernel function names to the SCI2 kernel functions. - */ - void setKernelNamesSci2(); - - /** - * Sets the default kernel function names to the SCI2.1 kernel functions. - */ - void setKernelNamesSci21(GameFeatures *features); -#endif - - /** * Loads the kernel selector names. */ void loadSelectorNames(); @@ -429,6 +412,7 @@ reg_t kListAt(EngineState *s, int argc, reg_t *argv); reg_t kString(EngineState *s, int argc, reg_t *argv); reg_t kMulDiv(EngineState *s, int argc, reg_t *argv); reg_t kCantBeHere32(EngineState *s, int argc, reg_t *argv); +reg_t kRemapColors32(EngineState *s, int argc, reg_t *argv); // "Screen items" in SCI32 are views reg_t kAddScreenItem(EngineState *s, int argc, reg_t *argv); reg_t kUpdateScreenItem(EngineState *s, int argc, reg_t *argv); @@ -453,6 +437,8 @@ reg_t kObjectIntersect(EngineState *s, int argc, reg_t *argv); reg_t kEditText(EngineState *s, int argc, reg_t *argv); reg_t kMakeSaveCatName(EngineState *s, int argc, reg_t *argv); reg_t kMakeSaveFileName(EngineState *s, int argc, reg_t *argv); +reg_t kSetScroll(EngineState *s, int argc, reg_t *argv); +reg_t kPalCycle(EngineState *s, int argc, reg_t *argv); // SCI2.1 Kernel Functions reg_t kText(EngineState *s, int argc, reg_t *argv); @@ -556,6 +542,7 @@ reg_t kFileIOWriteByte(EngineState *s, int argc, reg_t *argv); reg_t kFileIOReadWord(EngineState *s, int argc, reg_t *argv); reg_t kFileIOWriteWord(EngineState *s, int argc, reg_t *argv); reg_t kFileIOCreateSaveSlot(EngineState *s, int argc, reg_t *argv); +reg_t kFileIOIsValidDirectory(EngineState *s, int argc, reg_t *argv); #endif //@} diff --git a/engines/sci/engine/kernel_tables.h b/engines/sci/engine/kernel_tables.h index 6965a5da45..f5f46285be 100644 --- a/engines/sci/engine/kernel_tables.h +++ b/engines/sci/engine/kernel_tables.h @@ -240,7 +240,7 @@ static const SciKernelMapSubEntry kFileIO_subops[] = { { SIG_SCI32, 16, MAP_CALL(FileIOWriteWord), "ii", NULL }, { SIG_SCI32, 17, MAP_CALL(FileIOCreateSaveSlot), "ir", NULL }, { SIG_SCI32, 18, MAP_EMPTY(FileIOChangeDirectory), "r", NULL }, // for SQ6, when changing the savegame directory in the save/load dialog - { SIG_SCI32, 19, MAP_CALL(Stub), "r", NULL }, // for Torin / Torin demo + { SIG_SCI32, 19, MAP_CALL(FileIOIsValidDirectory), "r", NULL }, // for Torin / Torin demo #endif SCI_SUBOPENTRY_TERMINATOR }; @@ -248,7 +248,7 @@ static const SciKernelMapSubEntry kFileIO_subops[] = { #ifdef ENABLE_SCI32 static const SciKernelMapSubEntry kSave_subops[] = { - { SIG_SCI32, 0, MAP_CALL(SaveGame), "[r0]i[r0](r)", NULL }, + { SIG_SCI32, 0, MAP_CALL(SaveGame), "[r0]i[r0](r0)", NULL }, { SIG_SCI32, 1, MAP_CALL(RestoreGame), "[r0]i[r0]", NULL }, { SIG_SCI32, 2, MAP_CALL(GetSaveDir), "(r*)", NULL }, { SIG_SCI32, 3, MAP_CALL(CheckSaveGame), ".*", NULL }, @@ -419,7 +419,10 @@ static SciKernelMapEntry s_kernelMap[] = { { MAP_CALL(PriCoord), SIG_EVERYWHERE, "i", NULL, NULL }, { MAP_CALL(Random), SIG_EVERYWHERE, "i(i)(i)", NULL, NULL }, { MAP_CALL(ReadNumber), SIG_EVERYWHERE, "r", NULL, NULL }, - { MAP_CALL(RemapColors), SIG_EVERYWHERE, "i(i)(i)(i)(i)(i)", NULL, NULL }, + { MAP_CALL(RemapColors), SIG_SCI11, SIGFOR_ALL, "i(i)(i)(i)(i)", NULL, NULL }, +#ifdef ENABLE_SCI32 + { "RemapColors", kRemapColors32, SIG_SCI32, SIGFOR_ALL, "i(i)(i)(i)(i)(i)", NULL, NULL }, +#endif { MAP_CALL(ResCheck), SIG_EVERYWHERE, "ii(iiii)", NULL, NULL }, { MAP_CALL(RespondsTo), SIG_EVERYWHERE, ".i", NULL, NULL }, { MAP_CALL(RestartGame), SIG_EVERYWHERE, "", NULL, NULL }, @@ -517,11 +520,8 @@ static SciKernelMapEntry s_kernelMap[] = { { MAP_CALL(EditText), SIG_EVERYWHERE, "o", NULL, NULL }, { MAP_CALL(MakeSaveCatName), SIG_EVERYWHERE, "rr", NULL, NULL }, { MAP_CALL(MakeSaveFileName), SIG_EVERYWHERE, "rri", NULL, NULL }, - - // SCI2 unmapped functions - TODO! - - // SetScroll - called by script 64909, Styler::doit() - // PalCycle - called by Game::newRoom. Related to RemapColors. + { MAP_CALL(SetScroll), SIG_EVERYWHERE, "oiiiii(i)", NULL, NULL }, + { MAP_CALL(PalCycle), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // SCI2 Empty functions @@ -561,6 +561,11 @@ static SciKernelMapEntry s_kernelMap[] = { { MAP_DUMMY(InputText), SIG_EVERYWHERE, "(.*)", NULL, NULL }, { MAP_DUMMY(TextWidth), SIG_EVERYWHERE, "(.*)", NULL, NULL }, { MAP_DUMMY(PointSize), SIG_EVERYWHERE, "(.*)", NULL, NULL }, + // SetScroll is called by script 64909, Styler::doit(), but it doesn't seem to + // be used at all (plus, it was then changed to a dummy function in SCI3). + // Since this is most likely unused, and we got no test case, error out when + // it is called in order to find an actual call to it. + { MAP_DUMMY(SetScroll), SIG_EVERYWHERE, "(.*)", NULL, NULL }, // SCI2.1 Kernel Functions { MAP_CALL(CD), SIG_EVERYWHERE, "(.*)", NULL, NULL }, @@ -583,8 +588,8 @@ static SciKernelMapEntry s_kernelMap[] = { { MAP_CALL(Font), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, { MAP_CALL(Bitmap), SIG_EVERYWHERE, "(.*)", NULL, NULL }, { MAP_CALL(AddLine), SIG_EVERYWHERE, "oiiiiiiiii", NULL, NULL }, - { MAP_CALL(UpdateLine), SIG_EVERYWHERE, "roiiiiiiiii", NULL, NULL }, - { MAP_CALL(DeleteLine), SIG_EVERYWHERE, "ro", NULL, NULL }, + { MAP_CALL(UpdateLine), SIG_EVERYWHERE, "[r0]oiiiiiiiii", NULL, NULL }, + { MAP_CALL(DeleteLine), SIG_EVERYWHERE, "[r0]o", NULL, NULL }, // SCI2.1 Empty Functions @@ -629,11 +634,14 @@ static SciKernelMapEntry s_kernelMap[] = { // SCI2.1 unmapped functions - TODO! + // SetHotRectangles - used by Phantasmagoria 1, script 64981 (used in the chase scene) + // <lskovlun> The idea, if I understand correctly, is that the engine generates events + // of a special HotRect type continuously when the mouse is on that rectangle + // MovePlaneItems - used by SQ6 to scroll through the inventory via the up/down buttons // SetPalStyleRange - 2 integer parameters, start and end. All styles from start-end // (inclusive) are set to 0 // MorphOn - used by SQ6, script 900, the datacorder reprogramming puzzle (from room 270) - // SetHotRectangles - used by Phantasmagoria 1 // SCI3 Kernel Functions { MAP_CALL(PlayDuck), SIG_EVERYWHERE, "(.*)", NULL, NULL }, @@ -828,7 +836,7 @@ static const char *const sci2_default_knames[] = { /*0x20*/ "AddMagnify", /*0x21*/ "DeleteMagnify", /*0x22*/ "IsHiRes", - /*0x23*/ "Graph", + /*0x23*/ "Graph", // Robot in early SCI2.1 games with a SCI2 kernel table /*0x24*/ "InvertRect", // only in SCI2, not used in any SCI2 game /*0x25*/ "TextSize", /*0x26*/ "Message", @@ -839,7 +847,7 @@ static const char *const sci2_default_knames[] = { /*0x2b*/ "EditText", /*0x2c*/ "InputText", // unused function /*0x2d*/ "CreateTextBitmap", - /*0x2e*/ "DisposeTextBitmap", + /*0x2e*/ "DisposeTextBitmap", // Priority in early SCI2.1 games with a SCI2 kernel table /*0x2f*/ "GetEvent", /*0x30*/ "GlobalToLocal", /*0x31*/ "LocalToGlobal", @@ -1099,7 +1107,7 @@ static const char *const sci21_default_knames[] = { /*0x8a*/ "LoadChunk", /*0x8b*/ "SetPalStyleRange", /*0x8c*/ "AddPicAt", - /*0x8d*/ "MessageBox", // SCI3, was Dummy in SCI2.1 + /*0x8d*/ "Dummy", // MessageBox in SCI3 /*0x8e*/ "NewRoom", // debug function /*0x8f*/ "Dummy", /*0x90*/ "Priority", @@ -1113,7 +1121,7 @@ static const char *const sci21_default_knames[] = { /*0x98*/ "GetWindowsOption", // Windows only /*0x99*/ "WinDLL", // Windows only /*0x9a*/ "Dummy", - /*0x9b*/ "Minimize", // SCI3, was Dummy in SCI2.1 + /*0x9b*/ "Dummy", // Minimize in SCI3 /*0x9c*/ "DeletePic", // == SCI3 only =============== /*0x9d*/ "Dummy", @@ -1127,57 +1135,73 @@ static const char *const sci21_default_knames[] = { // Base set of opcode formats. They're copied and adjusted slightly in // script_adjust_opcode_format depending on SCI version. static const opcode_format g_base_opcode_formats[128][4] = { - /*00*/ + // 00 - 03 / bnot, add, sub, mul {Script_None}, {Script_None}, {Script_None}, {Script_None}, - /*04*/ + // 04 - 07 / div, mod, shr, shl {Script_None}, {Script_None}, {Script_None}, {Script_None}, - /*08*/ + // 08 - 0B / xor, and, or, neg {Script_None}, {Script_None}, {Script_None}, {Script_None}, - /*0C*/ + // 0C - 0F / not, eq, ne, gt {Script_None}, {Script_None}, {Script_None}, {Script_None}, - /*10*/ + // 10 - 13 / ge, lt, le, ugt {Script_None}, {Script_None}, {Script_None}, {Script_None}, - /*14*/ + // 14 - 17 / uge, ult, ule, bt {Script_None}, {Script_None}, {Script_None}, {Script_SRelative}, - /*18*/ + // 18 - 1B / bnt, jmp, ldi, push {Script_SRelative}, {Script_SRelative}, {Script_SVariable}, {Script_None}, - /*1C*/ + // 1C - 1F / pushi, toss, dup, link {Script_SVariable}, {Script_None}, {Script_None}, {Script_Variable}, - /*20*/ + // 20 - 23 / call, callk, callb, calle {Script_SRelative, Script_Byte}, {Script_Variable, Script_Byte}, {Script_Variable, Script_Byte}, {Script_Variable, Script_SVariable, Script_Byte}, - /*24 (24=ret)*/ + // 24 - 27 / ret, send, dummy, dummy {Script_End}, {Script_Byte}, {Script_Invalid}, {Script_Invalid}, - /*28*/ + // 28 - 2B / class, dummy, self, super {Script_Variable}, {Script_Invalid}, {Script_Byte}, {Script_Variable, Script_Byte}, - /*2C*/ + // 2C - 2F / rest, lea, selfID, dummy {Script_SVariable}, {Script_SVariable, Script_Variable}, {Script_None}, {Script_Invalid}, - /*30*/ + // 30 - 33 / pprev, pToa, aTop, pTos {Script_None}, {Script_Property}, {Script_Property}, {Script_Property}, - /*34*/ + // 34 - 37 / sTop, ipToa, dpToa, ipTos {Script_Property}, {Script_Property}, {Script_Property}, {Script_Property}, - /*38*/ + // 38 - 3B / dpTos, lofsa, lofss, push0 {Script_Property}, {Script_SRelative}, {Script_SRelative}, {Script_None}, - /*3C*/ + // 3C - 3F / push1, push2, pushSelf, line {Script_None}, {Script_None}, {Script_None}, {Script_Word}, - /*40-4F*/ + // ------------------------------------------------------------------------ + // 40 - 43 / lag, lal, lat, lap {Script_Global}, {Script_Local}, {Script_Temp}, {Script_Param}, + // 44 - 47 / lsg, lsl, lst, lsp {Script_Global}, {Script_Local}, {Script_Temp}, {Script_Param}, + // 48 - 4B / lagi, lali, lati, lapi {Script_Global}, {Script_Local}, {Script_Temp}, {Script_Param}, + // 4C - 4F / lsgi, lsli, lsti, lspi {Script_Global}, {Script_Local}, {Script_Temp}, {Script_Param}, - /*50-5F*/ + // ------------------------------------------------------------------------ + // 50 - 53 / sag, sal, sat, sap {Script_Global}, {Script_Local}, {Script_Temp}, {Script_Param}, + // 54 - 57 / ssg, ssl, sst, ssp {Script_Global}, {Script_Local}, {Script_Temp}, {Script_Param}, + // 58 - 5B / sagi, sali, sati, sapi {Script_Global}, {Script_Local}, {Script_Temp}, {Script_Param}, + // 5C - 5F / ssgi, ssli, ssti, sspi {Script_Global}, {Script_Local}, {Script_Temp}, {Script_Param}, - /*60-6F*/ + // ------------------------------------------------------------------------ + // 60 - 63 / plusag, plusal, plusat, plusap {Script_Global}, {Script_Local}, {Script_Temp}, {Script_Param}, + // 64 - 67 / plussg, plussl, plusst, plussp {Script_Global}, {Script_Local}, {Script_Temp}, {Script_Param}, + // 68 - 6B / plusagi, plusali, plusati, plusapi {Script_Global}, {Script_Local}, {Script_Temp}, {Script_Param}, + // 6C - 6F / plussgi, plussli, plussti, plusspi {Script_Global}, {Script_Local}, {Script_Temp}, {Script_Param}, - /*70-7F*/ + // ------------------------------------------------------------------------ + // 70 - 73 / minusag, minusal, minusat, minusap {Script_Global}, {Script_Local}, {Script_Temp}, {Script_Param}, + // 74 - 77 / minussg, minussl, minusst, minussp {Script_Global}, {Script_Local}, {Script_Temp}, {Script_Param}, + // 78 - 7B / minusagi, minusali, minusati, minusapi {Script_Global}, {Script_Local}, {Script_Temp}, {Script_Param}, + // 7C - 7F / minussgi, minussli, minussti, minusspi {Script_Global}, {Script_Local}, {Script_Temp}, {Script_Param} }; #undef END diff --git a/engines/sci/engine/kfile.cpp b/engines/sci/engine/kfile.cpp index a21e19802d..f7cc4f44b5 100644 --- a/engines/sci/engine/kfile.cpp +++ b/engines/sci/engine/kfile.cpp @@ -197,8 +197,15 @@ reg_t kCD(EngineState *s, int argc, reg_t *argv) { // TODO: Stub switch (argv[0].toUint16()) { case 0: - // Return whether the contents of disc argv[1] is available. - return TRUE_REG; + if (argc == 1) { + // Check if a disc is in the drive + return TRUE_REG; + } else { + // Check if the specified disc is in the drive + // and return the current disc number. We just + // return the requested disc number. + return argv[1]; + } case 1: // Return the current CD number return make_reg(0, 1); @@ -688,6 +695,13 @@ reg_t kFileIOCreateSaveSlot(EngineState *s, int argc, reg_t *argv) { return TRUE_REG; // slot creation was successful } +reg_t kFileIOIsValidDirectory(EngineState *s, int argc, reg_t *argv) { + // Used in Torin's Passage and LSL7 to determine if the directory passed as + // a parameter (usually the save directory) is valid. We always return true + // here. + return TRUE_REG; +} + #endif // ---- Save operations ------------------------------------------------------- @@ -1002,7 +1016,7 @@ reg_t kAutoSave(EngineState *s, int argc, reg_t *argv) { // the elapsed time from the timer object) // This function has to return something other than 0 to proceed - return s->r_acc; + return TRUE_REG; } #endif diff --git a/engines/sci/engine/kgraphics.cpp b/engines/sci/engine/kgraphics.cpp index 0ef268f108..da377319c0 100644 --- a/engines/sci/engine/kgraphics.cpp +++ b/engines/sci/engine/kgraphics.cpp @@ -645,6 +645,20 @@ reg_t kPaletteAnimate(EngineState *s, int argc, reg_t *argv) { if (paletteChanged) g_sci->_gfxPalette->kernelAnimateSet(); + // WORKAROUND: The game scripts in SQ4 floppy count the number of elapsed + // cycles in the intro from the number of successive kAnimate calls during + // the palette cycling effect, while showing the SQ4 logo. This worked in + // older computers because each animate call took awhile to complete. + // Normally, such scripts are handled automatically by our speed throttler, + // however in this case there are no calls to kGameIsRestarting (where the + // speed throttler gets called) between the different palette animation calls. + // Thus, we add a small delay between each animate call to make the whole + // palette animation effect slower and visible, and not have the logo screen + // get skipped because the scripts don't wait between animation steps. Fixes + // bug #3537232. + if (g_sci->getGameId() == GID_SQ4 && !g_sci->isCD() && s->currentRoomNumber() == 1) + g_sci->sleep(10); + return s->r_acc; } @@ -936,8 +950,9 @@ reg_t kDrawControl(EngineState *s, int argc, reg_t *argv) { } } if (objName == "savedHeros") { - // Import of QfG character files dialog is shown - // display additional popup information before letting user use it + // Import of QfG character files dialog is shown. + // Display additional popup information before letting user use it. + // For the SCI32 version of this, check kernelAddPlane(). reg_t changeDirButton = s->_segMan->findObjectByName("changeDirItem"); if (!changeDirButton.isNull()) { // check if checkDirButton is still enabled, in that case we are called the first time during that room @@ -950,6 +965,8 @@ reg_t kDrawControl(EngineState *s, int argc, reg_t *argv) { "for Quest for Glory 2. Example: 'qfg2-thief.sav'."); } } + + // For the SCI32 version of this, check kListAt(). s->_chosenQfGImportItem = readSelectorValue(s->_segMan, controlObject, SELECTOR(mark)); } @@ -1204,73 +1221,27 @@ reg_t kShow(EngineState *s, int argc, reg_t *argv) { return s->r_acc; } +// Early variant of the SCI32 kRemapColors kernel function, used in the demo of QFG4 reg_t kRemapColors(EngineState *s, int argc, reg_t *argv) { uint16 operation = argv[0].toUint16(); switch (operation) { - case 0: { // Set remapping to base. 0 turns remapping off. - int16 base = (argc >= 2) ? argv[1].toSint16() : 0; - if (base != 0) // 0 is the default behavior when changing rooms in GK1, thus silencing the warning - warning("kRemapColors: Set remapping to base %d", base); + case 0: { // remap by percent + uint16 percent = argv[1].toUint16(); + g_sci->_gfxPalette->resetRemapping(); + g_sci->_gfxPalette->setRemappingPercent(254, percent); } break; - case 1: { // unknown - // The demo of QFG4 calls this with 1+3 parameters, thus there are differences here - //int16 unk1 = argv[1].toSint16(); - //int16 unk2 = argv[2].toSint16(); - //int16 unk3 = argv[3].toSint16(); - //uint16 unk4 = argv[4].toUint16(); - //uint16 unk5 = (argc >= 6) ? argv[5].toUint16() : 0; - kStub(s, argc, argv); + case 1: { // remap by range + uint16 from = argv[1].toUint16(); + uint16 to = argv[2].toUint16(); + uint16 base = argv[3].toUint16(); + g_sci->_gfxPalette->resetRemapping(); + g_sci->_gfxPalette->setRemappingRange(254, from, to, base); } break; - case 2: { // remap by percent - // This adjusts the alpha value of a specific color, and it operates on - // an RGBA palette. Since we're operating on an RGB palette, we just - // modify the color intensity instead - // TODO: From what I understand, palette remapping should be placed - // separately, so that it can be reset by case 0 above. Thus, we - // should adjust the functionality of the Palette class accordingly. - int16 color = argv[1].toSint16(); - if (color >= 10) - color -= 10; - uint16 percent = argv[2].toUint16(); // 0 - 100 - if (argc >= 4) - warning("RemapByPercent called with 4 parameters, unknown parameter is %d", argv[3].toUint16()); - warning("kRemapColors: RemapByPercent color %d by %d percent", color, percent); - // TODO: It's not correct to set intensity here - //g_sci->_gfxPalette->kernelSetIntensity(color, 255, percent, false); - } - break; - case 3: { // remap to gray - // NOTE: This adjusts the alpha value of a specific color, and it operates on - // an RGBA palette - int16 color = argv[1].toSint16(); // this is subtracted from a maximum color value, and can be offset by 10 - int16 percent = argv[2].toSint16(); // 0 - 100 - uint16 unk3 = (argc >= 4) ? argv[3].toUint16() : 0; - warning("kRemapColors: RemapToGray color %d by %d percent (unk3 = %d)", color, percent, unk3); - } - break; - case 4: { // unknown - //int16 unk1 = argv[1].toSint16(); - //uint16 unk2 = argv[2].toUint16(); - //uint16 unk3 = argv[3].toUint16(); - //uint16 unk4 = (argc >= 5) ? argv[4].toUint16() : 0; - kStub(s, argc, argv); - } - break; - case 5: { // set color intensity - // TODO: This isn't right, it should be setting a mapping table instead. - // For PQ4, we can emulate this with kernelSetIntensity(). In QFG4, this - // won't do. - //int16 mapping = argv[1].toSint16(); - uint16 intensity = argv[2].toUint16(); - // HACK for PQ4 - if (g_sci->getGameId() == GID_PQ4) - g_sci->_gfxPalette->kernelSetIntensity(0, 255, intensity, true); - - kStub(s, argc, argv); - } + case 2: // turn remapping off (unused) + error("Unused subop kRemapColors(2) has been called"); break; default: break; diff --git a/engines/sci/engine/kgraphics32.cpp b/engines/sci/engine/kgraphics32.cpp index 413ad1ecb1..8b3afeef99 100644 --- a/engines/sci/engine/kgraphics32.cpp +++ b/engines/sci/engine/kgraphics32.cpp @@ -39,6 +39,7 @@ #include "sci/graphics/cache.h" #include "sci/graphics/compare.h" #include "sci/graphics/controls16.h" +#include "sci/graphics/coordadjuster.h" #include "sci/graphics/cursor.h" #include "sci/graphics/palette.h" #include "sci/graphics/paint16.h" @@ -171,8 +172,13 @@ reg_t kCreateTextBitmap(EngineState *s, int argc, reg_t *argv) { debugC(kDebugLevelStrings, "kCreateTextBitmap case 0 (%04x:%04x, %04x:%04x, %04x:%04x)", PRINT_REG(argv[1]), PRINT_REG(argv[2]), PRINT_REG(argv[3])); debugC(kDebugLevelStrings, "%s", text.c_str()); - uint16 maxWidth = argv[1].toUint16(); // nsRight - nsLeft + 1 - uint16 maxHeight = argv[2].toUint16(); // nsBottom - nsTop + 1 + int16 maxWidth = argv[1].toUint16(); + int16 maxHeight = argv[2].toUint16(); + g_sci->_gfxCoordAdjuster->fromScriptToDisplay(maxHeight, maxWidth); + // These values can be larger than the screen in the SQ6 demo, room 100 + // TODO: Find out why. For now, don't show any text in that room. + if (g_sci->getGameId() == GID_SQ6 && g_sci->isDemo() && s->currentRoomNumber() == 100) + return NULL_REG; return g_sci->_gfxText32->createTextBitmap(object, maxWidth, maxHeight); } case 1: { @@ -197,18 +203,6 @@ reg_t kDisposeTextBitmap(EngineState *s, int argc, reg_t *argv) { return s->r_acc; } -reg_t kGetWindowsOption(EngineState *s, int argc, reg_t *argv) { - uint16 windowsOption = argv[0].toUint16(); - switch (windowsOption) { - case 0: - // Title bar on/off in Phantasmagoria, we return 0 (off) - return NULL_REG; - default: - warning("GetWindowsOption: Unknown option %d", windowsOption); - return NULL_REG; - } -} - reg_t kWinHelp(EngineState *s, int argc, reg_t *argv) { switch (argv[0].toUint16()) { case 1: @@ -236,12 +230,12 @@ reg_t kSetShowStyle(EngineState *s, int argc, reg_t *argv) { // tables inside graphics/transitions.cpp uint16 showStyle = argv[0].toUint16(); // 0 - 15 reg_t planeObj = argv[1]; // the affected plane - //uint16 seconds = argv[2].toUint16(); // seconds that the transition lasts - //uint16 backColor = argv[3].toUint16(); // target back color(?). When fading out, it's 0x0000. When fading in, it's 0xffff - //int16 priority = argv[4].toSint16(); // always 0xc8 (200) when fading in/out - //uint16 animate = argv[5].toUint16(); // boolean, animate or not while the transition lasts - //uint16 refFrame = argv[6].toUint16(); // refFrame, always 0 when fading in/out -#if 0 + Common::String planeObjName = s->_segMan->getObjectName(planeObj); + uint16 seconds = argv[2].toUint16(); // seconds that the transition lasts + uint16 backColor = argv[3].toUint16(); // target back color(?). When fading out, it's 0x0000. When fading in, it's 0xffff + int16 priority = argv[4].toSint16(); // always 0xc8 (200) when fading in/out + uint16 animate = argv[5].toUint16(); // boolean, animate or not while the transition lasts + uint16 refFrame = argv[6].toUint16(); // refFrame, always 0 when fading in/out int16 divisions; // If the game has the pFadeArray selector, another parameter is used here, @@ -253,7 +247,7 @@ reg_t kSetShowStyle(EngineState *s, int argc, reg_t *argv) { } else { divisions = (argc >= 8) ? argv[7].toSint16() : -1; // divisions (transition steps?) } -#endif + if (showStyle > 15) { warning("kSetShowStyle: Illegal style %d for plane %04x:%04x", showStyle, PRINT_REG(planeObj)); return s->r_acc; @@ -269,18 +263,29 @@ reg_t kSetShowStyle(EngineState *s, int argc, reg_t *argv) { // TODO: Check if the plane is in the list of planes to draw + Common::String effectName = "unknown"; + switch (showStyle) { - //case 0: // no transition, perhaps? (like in the previous SCI versions) + case 0: // no transition / show + effectName = "show"; + break; case 13: // fade out + effectName = "fade out"; // TODO + break; case 14: // fade in + effectName = "fade in"; // TODO + break; default: - // TODO: This is all a stub/skeleton, thus we're invoking kStub() for now - kStub(s, argc, argv); + // TODO break; } + warning("kSetShowStyle: effect %d (%s) - plane: %04x:%04x (%s), sec: %d, " + "back: %d, prio: %d, animate: %d, ref frame: %d, divisions: %d", + showStyle, effectName.c_str(), PRINT_REG(planeObj), planeObjName.c_str(), + seconds, backColor, priority, animate, refFrame, divisions); return s->r_acc; } @@ -364,7 +369,8 @@ reg_t kScrollWindow(EngineState *s, int argc, reg_t *argv) { case 10: // Where, called by ScrollableWindow::where // TODO // argv[2] is an unknown integer - kStub(s, argc, argv); + // Silenced the warnings because of the high amount of console spam + //kStub(s, argc, argv); break; case 11: // Go, called by ScrollableWindow::scrollTo // 2 extra parameters here @@ -638,6 +644,169 @@ reg_t kDeleteLine(EngineState *s, int argc, reg_t *argv) { return s->r_acc; } +reg_t kSetScroll(EngineState *s, int argc, reg_t *argv) { + // Called in the intro of LSL6 hires (room 110) + // The end effect of this is the same as the old screen scroll transition + + // 7 parameters + reg_t planeObject = argv[0]; + //int16 x = argv[1].toSint16(); + //int16 y = argv[2].toSint16(); + uint16 pictureId = argv[3].toUint16(); + // param 4: int (0 in LSL6, probably scroll direction? The picture in LSL6 scrolls down) + // param 5: int (first call is 1, then the subsequent one is 0 in LSL6) + // param 6: optional int (0 in LSL6) + + // Set the new picture directly for now + //writeSelectorValue(s->_segMan, planeObject, SELECTOR(left), x); + //writeSelectorValue(s->_segMan, planeObject, SELECTOR(top), y); + writeSelectorValue(s->_segMan, planeObject, SELECTOR(picture), pictureId); + // and update our draw list + g_sci->_gfxFrameout->kernelUpdatePlane(planeObject); + + // TODO + return kStub(s, argc, argv); +} + +reg_t kPalCycle(EngineState *s, int argc, reg_t *argv) { + // Examples: GK1 room 480 (Bayou ritual), LSL6 room 100 (title screen) + + switch (argv[0].toUint16()) { + case 0: { // Palette animation initialization + // 3 or 4 extra params + // Case 1 sends fromColor and speed again, so we don't need them here. + // Only toColor is stored + //uint16 fromColor = argv[1].toUint16(); + s->_palCycleToColor = argv[2].toUint16(); + //uint16 speed = argv[3].toUint16(); + + // Invalidate the picture, so that the palette steps calls (case 1 + // below) can update its palette without it being overwritten by the + // view/picture palettes. + g_sci->_gfxScreen->_picNotValid = 1; + + // TODO: The fourth optional parameter is an unknown integer, and is 0 by default + if (argc == 5) { + // When this variant is used, picNotValid doesn't seem to be set + // (e.g. GK1 room 480). In this case, the animation step calls are + // not made, so perhaps this signifies the palette cycling steps + // to make. + // GK1 sets this to 6 (6 palette steps?) + g_sci->_gfxScreen->_picNotValid = 0; + } + kStub(s, argc, argv); + } + break; + case 1: { // Palette animation step + // This is the same as the old kPaletteAnimate call, with 1 set of colors. + // The end color is set up during initialization in case 0 above. + + // 1 or 2 extra params + uint16 fromColor = argv[1].toUint16(); + uint16 speed = (argc == 2) ? 1 : argv[2].toUint16(); + // TODO: For some reason, this doesn't set the color correctly + // (e.g. LSL6 intro, room 100, Sierra logo) + if (g_sci->_gfxPalette->kernelAnimate(fromColor, s->_palCycleToColor, speed)) + g_sci->_gfxPalette->kernelAnimateSet(); + } + // No kStub() call here, as this gets called loads of times, like kPaletteAnimate + break; + // case 2 hasn't been encountered + // case 3 hasn't been encountered + case 4: // reset any palette cycling and make the picture valid again + // Gets called when changing rooms and after palette cycling animations finish + // 0 or 1 extra params + if (argc == 1) { + g_sci->_gfxScreen->_picNotValid = 0; + // TODO: This also seems to perform more steps + } else { + // The variant with the 1 extra param resets remapping to base + // TODO + } + kStub(s, argc, argv); + break; + default: + // TODO + kStub(s, argc, argv); + break; + } + + return s->r_acc; +} + +reg_t kRemapColors32(EngineState *s, int argc, reg_t *argv) { + uint16 operation = argv[0].toUint16(); + + switch (operation) { + case 0: { // turn remapping off + // WORKAROUND: Game scripts in QFG4 erroneously turn remapping off in room + // 140 (the character point allocation screen) and never turn it back on, + // even if it's clearly used in that screen. + if (g_sci->getGameId() == GID_QFG4 && s->currentRoomNumber() == 140) + return s->r_acc; + + int16 base = (argc >= 2) ? argv[1].toSint16() : 0; + if (base > 0) + warning("kRemapColors(0) called with base %d", base); + g_sci->_gfxPalette->resetRemapping(); + } + break; + case 1: { // remap by range + uint16 color = argv[1].toUint16(); + uint16 from = argv[2].toUint16(); + uint16 to = argv[3].toUint16(); + uint16 base = argv[4].toUint16(); + uint16 unk5 = (argc >= 6) ? argv[5].toUint16() : 0; + if (unk5 > 0) + warning("kRemapColors(1) called with 6 parameters, unknown parameter is %d", unk5); + g_sci->_gfxPalette->setRemappingRange(color, from, to, base); + } + break; + case 2: { // remap by percent + uint16 color = argv[1].toUint16(); + uint16 percent = argv[2].toUint16(); // 0 - 100 + if (argc >= 4) + warning("RemapByPercent called with 4 parameters, unknown parameter is %d", argv[3].toUint16()); + g_sci->_gfxPalette->setRemappingPercent(color, percent); + } + break; + case 3: { // remap to gray + // Example call: QFG4 room 490 (Baba Yaga's hut) - params are color 253, 75% and 0. + // In this room, it's used for the cloud before Baba Yaga appears. + int16 color = argv[1].toSint16(); + int16 percent = argv[2].toSint16(); // 0 - 100 + if (argc >= 4) + warning("RemapToGray called with 4 parameters, unknown parameter is %d", argv[3].toUint16()); + g_sci->_gfxPalette->setRemappingPercentGray(color, percent); + } + break; + case 4: { // remap to percent gray + // Example call: QFG4 rooms 530/535 (swamp) - params are 253, 100%, 200 + int16 color = argv[1].toSint16(); + int16 percent = argv[2].toSint16(); // 0 - 100 + // argv[3] is unknown (a number, e.g. 200) - start color, perhaps? + if (argc >= 5) + warning("RemapToGrayPercent called with 5 parameters, unknown parameter is %d", argv[4].toUint16()); + g_sci->_gfxPalette->setRemappingPercentGray(color, percent); + } + break; + case 5: { // don't map to range + //int16 mapping = argv[1].toSint16(); + uint16 intensity = argv[2].toUint16(); + // HACK for PQ4 + if (g_sci->getGameId() == GID_PQ4) + g_sci->_gfxPalette->kernelSetIntensity(0, 255, intensity, true); + + kStub(s, argc, argv); + } + break; + default: + break; + } + + return s->r_acc; +} + #endif } // End of namespace Sci diff --git a/engines/sci/engine/klists.cpp b/engines/sci/engine/klists.cpp index 15d18eb4bb..342fa95eda 100644 --- a/engines/sci/engine/klists.cpp +++ b/engines/sci/engine/klists.cpp @@ -506,6 +506,11 @@ reg_t kListAt(EngineState *s, int argc, reg_t *argv) { curIndex++; } + // Update the virtual file selected in the character import screen of QFG4. + // For the SCI0-SCI1.1 version of this, check kDrawControl(). + if (g_sci->inQfGImportRoom() && !strcmp(s->_segMan->getObjectName(curObject), "SelectorDText")) + s->_chosenQfGImportItem = listIndex; + return curObject; } diff --git a/engines/sci/engine/kmath.cpp b/engines/sci/engine/kmath.cpp index 7570856dff..4b8fadbb84 100644 --- a/engines/sci/engine/kmath.cpp +++ b/engines/sci/engine/kmath.cpp @@ -77,7 +77,18 @@ reg_t kSqrt(EngineState *s, int argc, reg_t *argv) { return make_reg(0, (int16) sqrt((float) ABS(argv[0].toSint16()))); } +/** + * Returns the angle (in degrees) between the two points determined by (x1, y1) + * and (x2, y2). The angle ranges from 0 to 359 degrees. + * What this function does is pretty simple but apparently the original is not + * accurate. + */ uint16 kGetAngleWorker(int16 x1, int16 y1, int16 x2, int16 y2) { + // SCI1 games (QFG2 and newer) use a simple atan implementation. SCI0 games + // use a somewhat less accurate calculation (below). + if (getSciVersion() >= SCI_VERSION_1_EGA_ONLY) + return (int16)(360 - atan2((double)(x1 - x2), (double)(y1 - y2)) * 57.2958) % 360; + int16 xRel = x2 - x1; int16 yRel = y1 - y2; // y-axis is mirrored. int16 angle; diff --git a/engines/sci/engine/kmisc.cpp b/engines/sci/engine/kmisc.cpp index 12c830f622..8b7fc4ffec 100644 --- a/engines/sci/engine/kmisc.cpp +++ b/engines/sci/engine/kmisc.cpp @@ -419,6 +419,18 @@ reg_t kGetSierraProfileInt(EngineState *s, int argc, reg_t *argv) { return argv[2]; } +reg_t kGetWindowsOption(EngineState *s, int argc, reg_t *argv) { + uint16 windowsOption = argv[0].toUint16(); + switch (windowsOption) { + case 0: + // Title bar on/off in Phantasmagoria, we return 0 (off) + return NULL_REG; + default: + warning("GetWindowsOption: Unknown option %d", windowsOption); + return NULL_REG; + } +} + #endif // kIconBar is really a subop of kMacPlatform for SCI1.1 Mac diff --git a/engines/sci/engine/ksound.cpp b/engines/sci/engine/ksound.cpp index b378b4d58b..0633267db4 100644 --- a/engines/sci/engine/ksound.cpp +++ b/engines/sci/engine/ksound.cpp @@ -140,12 +140,14 @@ reg_t kDoAudio(EngineState *s, int argc, reg_t *argv) { ((argv[3].toUint16() & 0xff) << 16) | ((argv[4].toUint16() & 0xff) << 8) | (argv[5].toUint16() & 0xff); - if (argc == 8) { + // Removed warning because of the high amount of console spam + /*if (argc == 8) { + // TODO: Handle the extra 2 SCI21 params // argv[6] is always 1 // argv[7] is the contents of global 229 (0xE5) warning("kDoAudio: Play called with SCI2.1 extra parameters: %04x:%04x and %04x:%04x", PRINT_REG(argv[6]), PRINT_REG(argv[7])); - } + }*/ } else { warning("kDoAudio: Play called with an unknown number of parameters (%d)", argc); return NULL_REG; @@ -244,6 +246,11 @@ reg_t kDoAudio(EngineState *s, int argc, reg_t *argv) { // Used in Pharkas whenever a speech sample starts (takes no params) //warning("kDoAudio: Unhandled case 13, %d extra arguments passed", argc - 1); break; + case 17: + // Seems to be some sort of audio sync, used in SQ6. Silenced the + // warning due to the high level of spam it produces. (takes no params) + //warning("kDoAudio: Unhandled case 17, %d extra arguments passed", argc - 1); + break; default: warning("kDoAudio: Unhandled case %d, %d extra arguments passed", argv[0].toUint16(), argc - 1); } diff --git a/engines/sci/engine/kvideo.cpp b/engines/sci/engine/kvideo.cpp index 2456ba1100..6bf9aff2fe 100644 --- a/engines/sci/engine/kvideo.cpp +++ b/engines/sci/engine/kvideo.cpp @@ -50,6 +50,8 @@ void playVideo(Video::VideoDecoder *videoDecoder, VideoState videoState) { if (!videoDecoder) return; + videoDecoder->start(); + byte *scaleBuffer = 0; byte bytesPerPixel = videoDecoder->getPixelFormat().bytesPerPixel; uint16 width = videoDecoder->getWidth(); @@ -90,7 +92,7 @@ void playVideo(Video::VideoDecoder *videoDecoder, VideoState videoState) { EngineState *s = g_sci->getEngineState(); if (videoDecoder->hasDirtyPalette()) { - byte *palette = (byte *)videoDecoder->getPalette() + s->_vmdPalStart * 3; + const byte *palette = videoDecoder->getPalette() + s->_vmdPalStart * 3; g_system->getPaletteManager()->setPalette(palette, s->_vmdPalStart, s->_vmdPalEnd - s->_vmdPalStart); } @@ -108,7 +110,7 @@ void playVideo(Video::VideoDecoder *videoDecoder, VideoState videoState) { } if (videoDecoder->hasDirtyPalette()) { - byte *palette = (byte *)videoDecoder->getPalette() + s->_vmdPalStart * 3; + const byte *palette = videoDecoder->getPalette() + s->_vmdPalStart * 3; g_system->getPaletteManager()->setPalette(palette, s->_vmdPalStart, s->_vmdPalEnd - s->_vmdPalStart); } @@ -162,9 +164,8 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) { } else { // DOS SEQ // SEQ's are called with no subops, just the string and delay - SeqDecoder *seqDecoder = new SeqDecoder(); - seqDecoder->setFrameDelay(argv[1].toUint16()); // Time between frames in ticks - videoDecoder = seqDecoder; + // Time is specified as ticks + videoDecoder = new SEQDecoder(argv[1].toUint16()); if (!videoDecoder->loadFile(filename)) { warning("Failed to open movie file %s", filename.c_str()); @@ -190,7 +191,7 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) { switch (argv[0].toUint16()) { case 0: { Common::String filename = s->_segMan->getString(argv[1]); - videoDecoder = new Video::AviDecoder(g_system->getMixer()); + videoDecoder = new Video::AVIDecoder(); if (filename.equalsIgnoreCase("gk2a.avi")) { // HACK: Switch to 16bpp graphics for Indeo3. @@ -209,6 +210,8 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) { warning("Failed to open movie file %s", filename.c_str()); delete videoDecoder; videoDecoder = 0; + } else { + s->_videoState.fileName = filename; } break; } @@ -250,6 +253,7 @@ reg_t kRobot(EngineState *s, int argc, reg_t *argv) { int16 y = argv[5].toUint16(); warning("kRobot(init), id %d, obj %04x:%04x, flag %d, x=%d, y=%d", id, PRINT_REG(obj), flag, x, y); g_sci->_robotDecoder->load(id); + g_sci->_robotDecoder->start(); g_sci->_robotDecoder->setPos(x, y); } break; @@ -265,13 +269,13 @@ reg_t kRobot(EngineState *s, int argc, reg_t *argv) { warning("kRobot(%d)", subop); break; case 8: // sync - //if (false) { // debug: automatically skip all robot videos - if ((uint32)g_sci->_robotDecoder->getCurFrame() != g_sci->_robotDecoder->getFrameCount() - 1) { - writeSelector(s->_segMan, argv[1], SELECTOR(signal), NULL_REG); - } else { + //if (true) { // debug: automatically skip all robot videos + if (g_sci->_robotDecoder->endOfVideo()) { g_sci->_robotDecoder->close(); // Signal the engine scripts that the video is done writeSelector(s->_segMan, argv[1], SELECTOR(signal), SIGNAL_REG); + } else { + writeSelector(s->_segMan, argv[1], SELECTOR(signal), NULL_REG); } break; default: @@ -346,7 +350,7 @@ reg_t kPlayVMD(EngineState *s, int argc, reg_t *argv) { break; } case 6: // Play - videoDecoder = new Video::VMDDecoder(g_system->getMixer()); + videoDecoder = new Video::AdvancedVMDDecoder(); if (s->_videoState.fileName.empty()) { // Happens in Lighthouse @@ -404,7 +408,7 @@ reg_t kPlayDuck(EngineState *s, int argc, reg_t *argv) { s->_videoState.reset(); s->_videoState.fileName = Common::String::format("%d.duk", argv[1].toUint16()); - videoDecoder = new Video::AviDecoder(g_system->getMixer()); + videoDecoder = new Video::AVIDecoder(); if (!videoDecoder->loadFile(s->_videoState.fileName)) { warning("Could not open Duck %s", s->_videoState.fileName.c_str()); diff --git a/engines/sci/engine/message.cpp b/engines/sci/engine/message.cpp index cddd01e10c..a92d572d35 100644 --- a/engines/sci/engine/message.cpp +++ b/engines/sci/engine/message.cpp @@ -400,11 +400,21 @@ Common::String MessageState::processString(const char *s) { void MessageState::outputString(reg_t buf, const Common::String &str) { #ifdef ENABLE_SCI32 if (getSciVersion() >= SCI_VERSION_2) { - SciString *sciString = _segMan->lookupString(buf); - sciString->setSize(str.size() + 1); - for (uint32 i = 0; i < str.size(); i++) - sciString->setValue(i, str.c_str()[i]); - sciString->setValue(str.size(), 0); + if (_segMan->getSegmentType(buf.getSegment()) == SEG_TYPE_STRING) { + SciString *sciString = _segMan->lookupString(buf); + sciString->setSize(str.size() + 1); + for (uint32 i = 0; i < str.size(); i++) + sciString->setValue(i, str.c_str()[i]); + sciString->setValue(str.size(), 0); + } else if (_segMan->getSegmentType(buf.getSegment()) == SEG_TYPE_ARRAY) { + // Happens in the intro of LSL6, we are asked to write the string + // into an array + SciArray<reg_t> *sciString = _segMan->lookupArray(buf); + sciString->setSize(str.size() + 1); + for (uint32 i = 0; i < str.size(); i++) + sciString->setValue(i, make_reg(0, str.c_str()[i])); + sciString->setValue(str.size(), NULL_REG); + } } else { #endif SegmentRef buffer_r = _segMan->dereference(buf); diff --git a/engines/sci/engine/script.cpp b/engines/sci/engine/script.cpp index 57334b89aa..037f4ab700 100644 --- a/engines/sci/engine/script.cpp +++ b/engines/sci/engine/script.cpp @@ -385,7 +385,7 @@ void Script::setLockers(int lockers) { _lockers = lockers; } -uint32 Script::validateExportFunc(int pubfunct, bool relocateSci3) { +uint32 Script::validateExportFunc(int pubfunct, bool relocSci3) { bool exportsAreWide = (g_sci->_features->detectLofsType() == SCI_VERSION_1_MIDDLE); if (_numExports <= pubfunct) { @@ -401,7 +401,7 @@ uint32 Script::validateExportFunc(int pubfunct, bool relocateSci3) { if (getSciVersion() != SCI_VERSION_3) { offset = READ_SCI11ENDIAN_UINT16(_exportTable + pubfunct); } else { - if (!relocateSci3) + if (!relocSci3) offset = READ_SCI11ENDIAN_UINT16(_exportTable + pubfunct) + getCodeBlockOffsetSci3(); else offset = relocateOffsetSci3(pubfunct * 2 + 22); @@ -422,16 +422,9 @@ uint32 Script::validateExportFunc(int pubfunct, bool relocateSci3) { } } - if (!offset) { -#ifdef ENABLE_SCI32 - // WORKAROUNDS for invalid (empty) exports - if (g_sci->getGameId() == GID_TORIN && _nr == 64036) { - } else if (g_sci->getGameId() == GID_RAMA && _nr == 64908) { - } else -#endif - error("Request for invalid exported function 0x%x of script %d", pubfunct, _nr); - return NULL; - } + // Note that it's perfectly normal to return a zero offset, especially in + // SCI1.1 and newer games. Examples include script 64036 in Torin's Passage, + // script 64908 in the demo of RAMA and script 1013 in KQ6 floppy. if (offset >= _bufSize) error("Invalid export function pointer"); diff --git a/engines/sci/engine/script.h b/engines/sci/engine/script.h index 1fc8caf313..0b499203c2 100644 --- a/engines/sci/engine/script.h +++ b/engines/sci/engine/script.h @@ -197,11 +197,11 @@ public: * Validate whether the specified public function is exported by * the script in the specified segment. * @param pubfunct Index of the function to validate - * @param relocateSci3 Decide whether to relocate this SCI3 public function or not + * @param relocSci3 Decide whether to relocate this SCI3 public function or not * @return NULL if the public function is invalid, its * offset into the script's segment otherwise */ - uint32 validateExportFunc(int pubfunct, bool relocateSci3); + uint32 validateExportFunc(int pubfunct, bool relocSci3); /** * Marks the script as deleted. diff --git a/engines/sci/engine/script_patches.cpp b/engines/sci/engine/script_patches.cpp index 69eb377684..659c13b13e 100644 --- a/engines/sci/engine/script_patches.cpp +++ b/engines/sci/engine/script_patches.cpp @@ -978,7 +978,27 @@ const uint16 sq4CdPatchTextOptionsButton[] = { PATCH_END }; -// Patch 2: Add the ability to toggle among the three available options, +// Patch 2: Adjust a check in babbleIcon::init, which handles the babble icon +// (e.g. the two guys from Andromeda) shown when dying/quitting. +// Fixes bug #3538418. +const byte sq4CdSignatureBabbleIcon[] = { + 7, + 0x89, 0x5a, // lsg 5a + 0x35, 0x02, // ldi 02 + 0x1a, // eq? + 0x31, 0x26, // bnt 26 [02a7] + 0 +}; + +const uint16 sq4CdPatchBabbleIcon[] = { + 0x89, 0x5a, // lsg 5a + 0x35, 0x01, // ldi 01 + 0x1a, // eq? + 0x2f, 0x26, // bt 26 [02a7] + PATCH_END +}; + +// Patch 3: Add the ability to toggle among the three available options, // when the text options button is clicked: "Speech", "Text" and "Both". // Refer to the patch above for additional details. // iconTextSwitch::doit (called when the text options button is clicked) @@ -1030,6 +1050,7 @@ const SciScriptSignature sq4Signatures[] = { { 298, "Floppy: endless flight", 1, PATCH_MAGICDWORD(0x67, 0x08, 0x63, 0x44), -3, sq4FloppySignatureEndlessFlight, sq4FloppyPatchEndlessFlight }, { 298, "Floppy (German): endless flight", 1, PATCH_MAGICDWORD(0x67, 0x08, 0x63, 0x4c), -3, sq4FloppySignatureEndlessFlightGerman, sq4FloppyPatchEndlessFlight }, { 818, "CD: Speech and subtitles option", 1, PATCH_MAGICDWORD(0x89, 0x5a, 0x3c, 0x35), 0, sq4CdSignatureTextOptions, sq4CdPatchTextOptions }, + { 0, "CD: Babble icon speech and subtitles fix", 1, PATCH_MAGICDWORD(0x89, 0x5a, 0x35, 0x02), 0, sq4CdSignatureBabbleIcon, sq4CdPatchBabbleIcon }, { 818, "CD: Speech and subtitles option button", 1, PATCH_MAGICDWORD(0x35, 0x01, 0xa1, 0x53), 0, sq4CdSignatureTextOptionsButton, sq4CdPatchTextOptionsButton }, SCI_SIGNATUREENTRY_TERMINATOR }; diff --git a/engines/sci/engine/seg_manager.cpp b/engines/sci/engine/seg_manager.cpp index a6c145979f..951fc7c363 100644 --- a/engines/sci/engine/seg_manager.cpp +++ b/engines/sci/engine/seg_manager.cpp @@ -143,16 +143,27 @@ Script *SegManager::allocateScript(int script_nr, SegmentId *segid) { } void SegManager::deallocate(SegmentId seg) { - if (!check(seg)) - error("SegManager::deallocate(): invalid segment ID"); + if (seg < 1 || (uint)seg >= _heap.size()) + error("Attempt to deallocate an invalid segment ID"); SegmentObj *mobj = _heap[seg]; + if (!mobj) + error("Attempt to deallocate an already freed segment"); if (mobj->getType() == SEG_TYPE_SCRIPT) { Script *scr = (Script *)mobj; _scriptSegMap.erase(scr->getScriptNumber()); - if (scr->getLocalsSegment()) - deallocate(scr->getLocalsSegment()); + if (scr->getLocalsSegment()) { + // Check if the locals segment has already been deallocated. + // If the locals block has been stored in a segment with an ID + // smaller than the segment ID of the script itself, it will be + // already freed at this point. This can happen when scripts are + // uninstantiated and instantiated again: they retain their own + // segment ID, but are allocated a new locals segment, which can + // have an ID smaller than the segment of the script itself. + if (_heap[scr->getLocalsSegment()]) + deallocate(scr->getLocalsSegment()); + } } delete mobj; @@ -307,21 +318,6 @@ reg_t SegManager::findObjectByName(const Common::String &name, int index) { return result[index]; } -// validate the seg -// return: -// false - invalid seg -// true - valid seg -bool SegManager::check(SegmentId seg) { - if (seg < 1 || (uint)seg >= _heap.size()) { - return false; - } - if (!_heap[seg]) { - warning("SegManager: seg %x is removed from memory, but not removed from hash_map", seg); - return false; - } - return true; -} - // return the seg if script_id is valid and in the map, else 0 SegmentId SegManager::getScriptSegment(int script_id) const { return _scriptSegMap.getVal(script_id, 0); diff --git a/engines/sci/engine/seg_manager.h b/engines/sci/engine/seg_manager.h index 356a1b04a7..074d3f6b0a 100644 --- a/engines/sci/engine/seg_manager.h +++ b/engines/sci/engine/seg_manager.h @@ -471,14 +471,6 @@ private: void createClassTable(); SegmentId findFreeSegment() const; - - /** - * Check segment validity - * @param[in] seg The segment to validate - * @return false if 'seg' is an invalid segment, true if - * 'seg' is a valid segment - */ - bool check(SegmentId seg); }; } // End of namespace Sci diff --git a/engines/sci/engine/state.cpp b/engines/sci/engine/state.cpp index 94a3fe3ae5..0f0c8dcd66 100644 --- a/engines/sci/engine/state.cpp +++ b/engines/sci/engine/state.cpp @@ -122,8 +122,11 @@ void EngineState::reset(bool isRestoring) { _videoState.reset(); _syncedAudioOptions = false; + _vmdPalStart = 0; _vmdPalEnd = 256; + + _palCycleToColor = 255; } void EngineState::speedThrottler(uint32 neededSleep) { diff --git a/engines/sci/engine/state.h b/engines/sci/engine/state.h index 9ae6299d83..81090876c7 100644 --- a/engines/sci/engine/state.h +++ b/engines/sci/engine/state.h @@ -199,6 +199,8 @@ public: uint16 _vmdPalStart, _vmdPalEnd; bool _syncedAudioOptions; + uint16 _palCycleToColor; + /** * Resets the engine state. */ diff --git a/engines/sci/engine/vm.cpp b/engines/sci/engine/vm.cpp index 5a2a39def2..3f43966976 100644 --- a/engines/sci/engine/vm.cpp +++ b/engines/sci/engine/vm.cpp @@ -1173,6 +1173,7 @@ void run_vm(EngineState *s) { case op_line: // 0x3f (63) // Debug opcode (line number) + //debug("Script %d, line %d", scr->getScriptNumber(), opparams[0]); break; case op_lag: // 0x40 (64) diff --git a/engines/sci/engine/vm.h b/engines/sci/engine/vm.h index a0fd6689df..8b38faa013 100644 --- a/engines/sci/engine/vm.h +++ b/engines/sci/engine/vm.h @@ -202,6 +202,7 @@ enum SciOpcodes { op_push2 = 0x3d, // 061 op_pushSelf = 0x3e, // 062 op_line = 0x3f, // 063 + // op_lag = 0x40, // 064 op_lal = 0x41, // 065 op_lat = 0x42, // 066 @@ -218,6 +219,7 @@ enum SciOpcodes { op_lsli = 0x4d, // 077 op_lsti = 0x4e, // 078 op_lspi = 0x4f, // 079 + // op_sag = 0x50, // 080 op_sal = 0x51, // 081 op_sat = 0x52, // 082 @@ -234,6 +236,7 @@ enum SciOpcodes { op_ssli = 0x5d, // 093 op_ssti = 0x5e, // 094 op_sspi = 0x5f, // 095 + // op_plusag = 0x60, // 096 op_plusal = 0x61, // 097 op_plusat = 0x62, // 098 @@ -250,6 +253,7 @@ enum SciOpcodes { op_plussli = 0x6d, // 109 op_plussti = 0x6e, // 110 op_plusspi = 0x6f, // 111 + // op_minusag = 0x70, // 112 op_minusal = 0x71, // 113 op_minusat = 0x72, // 114 diff --git a/engines/sci/engine/workarounds.cpp b/engines/sci/engine/workarounds.cpp index ecb1e4c2d5..9fa0368784 100644 --- a/engines/sci/engine/workarounds.cpp +++ b/engines/sci/engine/workarounds.cpp @@ -36,13 +36,15 @@ const SciWorkaroundEntry arithmeticWorkarounds[] = { { GID_ECOQUEST2, 100, 0, 0, "Rain", "points", 0xcc6, 0, { WORKAROUND_FAKE, 0 } }, // op_or: when giving the papers to the customs officer, gets called against a pointer instead of a number - bug #3034464 { GID_ECOQUEST2, 100, 0, 0, "Rain", "points", 0xce0, 0, { WORKAROUND_FAKE, 0 } }, // Same as above, for the Spanish version - bug #3313962 { GID_FANMADE, 516, 983, 0, "Wander", "setTarget", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_mul: The Legend of the Lost Jewel Demo (fan made): called with object as second parameter when attacked by insects - bug #3038913 + { GID_GK1, 800,64992, 0, "Fwd", "doit", -1, 0, { WORKAROUND_FAKE, 1 } }, // op_gt: when Mosely finds Gabriel and Grace near the end of the game, compares the Grooper object with 7 { GID_ICEMAN, 199, 977, 0, "Grooper", "doit", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_add: While dancing with the girl { GID_MOTHERGOOSE256, -1, 999, 0, "Event", "new", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_and: constantly during the game (SCI1 version) { GID_MOTHERGOOSE256, -1, 4, 0, "rm004", "doit", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_or: when going north and reaching the castle (rooms 4 and 37) - bug #3038228 { GID_MOTHERGOOSEHIRES,90, 90, 0, "newGameButton", "select", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_ge: MUMG Deluxe, when selecting "New Game" in the main menu. It tries to compare an integer with a list. Needs to return false for the game to continue. + { GID_PHANTASMAGORIA, 902, 0, 0, "", "export 7", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_shr: when starting a chapter in Phantasmagoria { GID_QFG1VGA, 301, 928, 0, "Blink", "init", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_div: when entering the inn, gets called with 1 parameter, but 2nd parameter is used for div which happens to be an object { GID_QFG2, 200, 200, 0, "astro", "messages", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_lsi: when getting asked for your name by the astrologer bug #3039879 - { GID_GK1, 800,64992, 0, "Fwd", "doit", -1, 0, { WORKAROUND_FAKE, 1 } }, // op_gt: when Mosely finds Gabriel and Grace near the end of the game, compares the Grooper object with 7 + { GID_QFG4, 710,64941, 0, "RandCycle", "doit", -1, 0, { WORKAROUND_FAKE, 1 } }, // op_gt: when the tentacle appears in the third room of the caves SCI_WORKAROUNDENTRY_TERMINATOR }; @@ -162,10 +164,10 @@ const SciWorkaroundEntry uninitializedReadWorkarounds[] = { { GID_SQ1, -1, 703, 0, "", "export 1", -1, 0, { WORKAROUND_FAKE, 0 } }, // sub that's called from several objects while on sarien battle cruiser { GID_SQ1, -1, 703, 0, "firePulsar", "changeState", 0x18a, 0, { WORKAROUND_FAKE, 0 } }, // export 1, but called locally (when shooting at aliens) { GID_SQ4, -1, 398, 0, "showBox", "changeState", -1, 0, { WORKAROUND_FAKE, 0 } }, // CD: called when rummaging in Software Excess bargain bin - { GID_SQ4, -1, 928, 0, "Narrator", "startText", -1, 1000, { WORKAROUND_FAKE, 1 } }, // CD: method returns this to the caller + { GID_SQ4, -1, 928, -1, "Narrator", "startText", -1, 1000, { WORKAROUND_FAKE, 1 } }, // CD: happens in the options dialog and in-game when speech and subtitles are used simultaneously { GID_SQ5, 201, 201, 0, "buttonPanel", "doVerb", -1, 0, { WORKAROUND_FAKE, 1 } }, // when looking at the orange or red button - bug #3038563 { GID_SQ6, -1, 0, 0, "SQ6", "init", -1, 2, { WORKAROUND_FAKE, 0 } }, // Demo and full version: called when the game starts (demo: room 0, full: room 100) - { GID_SQ6, 100, 64950, 0, "View", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // called when pressing "Start game" in the main menu + { GID_SQ6, -1, 64950, -1, "Feature", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // called when pressing "Start game" in the main menu, when entering the Orion's Belt bar (room 300), and perhaps other places { GID_SQ6, -1, 64964, 0, "DPath", "init", -1, 1, { WORKAROUND_FAKE, 0 } }, // during the game { GID_TORIN, -1, 64017, 0, "oFlags", "clear", -1, 0, { WORKAROUND_FAKE, 0 } }, // entering Torin's home in the French version SCI_WORKAROUNDENTRY_TERMINATOR @@ -397,6 +399,7 @@ const SciWorkaroundEntry kUnLoad_workarounds[] = { { GID_LSL6, 740, 740, 0, "showCartoon", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // during ending, 4 additional parameters are passed by accident { GID_LSL6HIRES, 130, 130, 0, "recruitLarryScr", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // during intro, a 3rd parameter is passed by accident { GID_SQ1, 43, 303, 0, "slotGuy", "dispose", -1, 0, { WORKAROUND_IGNORE, 0 } }, // when leaving ulence flats bar, parameter 1 is not passed - script error + { GID_QFG4, -1, 110, 0, "dreamer", "dispose", -1, 0, { WORKAROUND_IGNORE, 0 } }, // during the dream sequence, a 3rd parameter is passed by accident SCI_WORKAROUNDENTRY_TERMINATOR }; diff --git a/engines/sci/graphics/font.cpp b/engines/sci/graphics/font.cpp index fcdd057509..30184cc091 100644 --- a/engines/sci/graphics/font.cpp +++ b/engines/sci/graphics/font.cpp @@ -54,7 +54,7 @@ GfxFontFromResource::GfxFontFromResource(ResourceManager *resMan, GfxScreen *scr } GfxFontFromResource::~GfxFontFromResource() { - delete []_chars; + delete[] _chars; _resMan->unlockResource(_resource); } diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp index 265a175e66..968014c032 100644 --- a/engines/sci/graphics/frameout.cpp +++ b/engines/sci/graphics/frameout.cpp @@ -28,6 +28,7 @@ #include "common/system.h" #include "common/textconsole.h" #include "engines/engine.h" +#include "graphics/palette.h" #include "graphics/surface.h" #include "sci/sci.h" @@ -117,27 +118,43 @@ void GfxFrameout::showCurrentScrollText() { } } +extern void showScummVMDialog(const Common::String &message); + void GfxFrameout::kernelAddPlane(reg_t object) { PlaneEntry newPlane; if (_planes.empty()) { // There has to be another way for sierra sci to do this or maybe script resolution is compiled into // interpreter (TODO) - uint16 tmpRunningWidth = readSelectorValue(_segMan, object, SELECTOR(resX)); - uint16 tmpRunningHeight = readSelectorValue(_segMan, object, SELECTOR(resY)); + uint16 scriptWidth = readSelectorValue(_segMan, object, SELECTOR(resX)); + uint16 scriptHeight = readSelectorValue(_segMan, object, SELECTOR(resY)); - // The above can be 0 in SCI3 (e.g. Phantasmagoria 2) - if (tmpRunningWidth == 0 && tmpRunningHeight == 0) { - tmpRunningWidth = 320; - tmpRunningHeight = 200; + // Phantasmagoria 2 doesn't specify a script width/height + if (g_sci->getGameId() == GID_PHANTASMAGORIA2) { + scriptWidth = 640; + scriptHeight = 480; } - _coordAdjuster->setScriptsResolution(tmpRunningWidth, tmpRunningHeight); + assert(scriptWidth > 0 && scriptHeight > 0); + _coordAdjuster->setScriptsResolution(scriptWidth, scriptHeight); + } + + // Import of QfG character files dialog is shown in QFG4. + // Display additional popup information before letting user use it. + // For the SCI0-SCI1.1 version of this, check kDrawControl(). + if (g_sci->inQfGImportRoom() && !strcmp(_segMan->getObjectName(object), "DSPlane")) { + showScummVMDialog("Characters saved inside ScummVM are shown " + "automatically. Character files saved in the original " + "interpreter need to be put inside ScummVM's saved games " + "directory and a prefix needs to be added depending on which " + "game it was saved in: 'qfg1-' for Quest for Glory 1, 'qfg2-' " + "for Quest for Glory 2, 'qfg3-' for Quest for Glory 3. " + "Example: 'qfg2-thief.sav'."); } newPlane.object = object; newPlane.priority = readSelectorValue(_segMan, object, SELECTOR(priority)); - newPlane.lastPriority = 0xFFFF; // hidden + newPlane.lastPriority = -1; // hidden newPlane.planeOffsetX = 0; newPlane.planeOffsetY = 0; newPlane.pictureId = kPlanePlainColored; @@ -294,6 +311,10 @@ reg_t GfxFrameout::addPlaneLine(reg_t object, Common::Point startPoint, Common:: } void GfxFrameout::updatePlaneLine(reg_t object, reg_t hunkId, Common::Point startPoint, Common::Point endPoint, byte color, byte priority, byte control) { + // Check if we're asked to update a line that was never added + if (hunkId.isNull()) + return; + for (PlaneList::iterator it = _planes.begin(); it != _planes.end(); ++it) { if (it->object == object) { for (PlaneLineList::iterator it2 = it->lines.begin(); it2 != it->lines.end(); ++it2) { @@ -311,6 +332,10 @@ void GfxFrameout::updatePlaneLine(reg_t object, reg_t hunkId, Common::Point star } void GfxFrameout::deletePlaneLine(reg_t object, reg_t hunkId) { + // Check if we're asked to delete a line that was never added (happens during the intro of LSL6) + if (hunkId.isNull()) + return; + for (PlaneList::iterator it = _planes.begin(); it != _planes.end(); ++it) { if (it->object == object) { for (PlaneLineList::iterator it2 = it->lines.begin(); it2 != it->lines.end(); ++it2) { @@ -326,8 +351,10 @@ void GfxFrameout::deletePlaneLine(reg_t object, reg_t hunkId) { void GfxFrameout::kernelAddScreenItem(reg_t object) { // Ignore invalid items - if (!_segMan->isObject(object)) + if (!_segMan->isObject(object)) { + warning("kernelAddScreenItem: Attempt to add an invalid object (%04x:%04x)", PRINT_REG(object)); return; + } FrameoutEntry *itemEntry = new FrameoutEntry(); memset(itemEntry, 0, sizeof(FrameoutEntry)); @@ -341,8 +368,10 @@ void GfxFrameout::kernelAddScreenItem(reg_t object) { void GfxFrameout::kernelUpdateScreenItem(reg_t object) { // Ignore invalid items - if (!_segMan->isObject(object)) + if (!_segMan->isObject(object)) { + warning("kernelUpdateScreenItem: Attempt to update an invalid object (%04x:%04x)", PRINT_REG(object)); return; + } FrameoutEntry *itemEntry = findScreenItem(object); if (!itemEntry) { @@ -372,10 +401,9 @@ void GfxFrameout::kernelUpdateScreenItem(reg_t object) { void GfxFrameout::kernelDeleteScreenItem(reg_t object) { FrameoutEntry *itemEntry = findScreenItem(object); - if (!itemEntry) { - warning("kernelDeleteScreenItem: invalid object %04x:%04x", PRINT_REG(object)); + // If the item could not be found, it may already have been deleted + if (!itemEntry) return; - } _screenItems.remove(itemEntry); delete itemEntry; @@ -432,15 +460,10 @@ bool sortHelper(const FrameoutEntry* entry1, const FrameoutEntry* entry2) { } bool planeSortHelper(const PlaneEntry &entry1, const PlaneEntry &entry2) { -// SegManager *segMan = g_sci->getEngineState()->_segMan; - -// uint16 plane1Priority = readSelectorValue(segMan, entry1, SELECTOR(priority)); -// uint16 plane2Priority = readSelectorValue(segMan, entry2, SELECTOR(priority)); - - if (entry1.priority == 0xffff) + if (entry1.priority < 0) return true; - if (entry2.priority == 0xffff) + if (entry2.priority < 0) return false; return entry1.priority < entry2.priority; @@ -466,7 +489,7 @@ void GfxFrameout::showVideo() { uint16 y = videoDecoder->getPos().y; if (videoDecoder->hasDirtyPalette()) - videoDecoder->setSystemPalette(); + g_system->getPaletteManager()->setPalette(videoDecoder->getPalette(), 0, 256); while (!g_engine->shouldQuit() && !videoDecoder->endOfVideo() && !skipVideo) { if (videoDecoder->needsUpdate()) { @@ -475,7 +498,7 @@ void GfxFrameout::showVideo() { g_system->copyRectToScreen(frame->pixels, frame->pitch, x, y, frame->w, frame->h); if (videoDecoder->hasDirtyPalette()) - videoDecoder->setSystemPalette(); + g_system->getPaletteManager()->setPalette(videoDecoder->getPalette(), 0, 256); g_system->updateScreen(); } @@ -606,13 +629,13 @@ void GfxFrameout::kernelFrameout() { _screen->drawLine(startPoint, endPoint, it2->color, it2->priority, it2->control); } - uint16 planeLastPriority = it->lastPriority; + int16 planeLastPriority = it->lastPriority; // Update priority here, sq6 sets it w/o UpdatePlane - uint16 planePriority = it->priority = readSelectorValue(_segMan, planeObject, SELECTOR(priority)); + int16 planePriority = it->priority = readSelectorValue(_segMan, planeObject, SELECTOR(priority)); it->lastPriority = planePriority; - if (planePriority == 0xffff) { // Plane currently not meant to be shown + if (planePriority < 0) { // Plane currently not meant to be shown // If plane was shown before, delete plane rect if (planePriority != planeLastPriority) _paint32->fillRect(it->planeRect, 0); @@ -653,7 +676,7 @@ void GfxFrameout::kernelFrameout() { if (view && view->isSci2Hires()) { view->adjustToUpscaledCoordinates(itemEntry->y, itemEntry->x); view->adjustToUpscaledCoordinates(itemEntry->z, dummyX); - } else if (getSciVersion() == SCI_VERSION_2_1) { + } else if (getSciVersion() >= SCI_VERSION_2_1) { _coordAdjuster->fromScriptToDisplay(itemEntry->y, itemEntry->x); _coordAdjuster->fromScriptToDisplay(itemEntry->z, dummyX); } @@ -676,13 +699,13 @@ void GfxFrameout::kernelFrameout() { // TODO: maybe we should clip the cels rect with this, i'm not sure // the only currently known usage is game menu of gk1 } else if (view) { - if ((itemEntry->scaleX == 128) && (itemEntry->scaleY == 128)) - view->getCelRect(itemEntry->loopNo, itemEntry->celNo, - itemEntry->x, itemEntry->y, itemEntry->z, itemEntry->celRect); - else - view->getCelScaledRect(itemEntry->loopNo, itemEntry->celNo, - itemEntry->x, itemEntry->y, itemEntry->z, itemEntry->scaleX, - itemEntry->scaleY, itemEntry->celRect); + if ((itemEntry->scaleX == 128) && (itemEntry->scaleY == 128)) + view->getCelRect(itemEntry->loopNo, itemEntry->celNo, + itemEntry->x, itemEntry->y, itemEntry->z, itemEntry->celRect); + else + view->getCelScaledRect(itemEntry->loopNo, itemEntry->celNo, + itemEntry->x, itemEntry->y, itemEntry->z, itemEntry->scaleX, + itemEntry->scaleY, itemEntry->celRect); Common::Rect nsRect = itemEntry->celRect; // Translate back to actual coordinate within scrollable plane @@ -691,7 +714,7 @@ void GfxFrameout::kernelFrameout() { if (view && view->isSci2Hires()) { view->adjustBackUpscaledCoordinates(nsRect.top, nsRect.left); view->adjustBackUpscaledCoordinates(nsRect.bottom, nsRect.right); - } else if (getSciVersion() == SCI_VERSION_2_1) { + } else if (getSciVersion() >= SCI_VERSION_2_1) { _coordAdjuster->fromDisplayToScript(nsRect.top, nsRect.left); _coordAdjuster->fromDisplayToScript(nsRect.bottom, nsRect.right); } @@ -706,7 +729,9 @@ void GfxFrameout::kernelFrameout() { g_sci->_gfxCompare->setNSRect(itemEntry->object, nsRect); } - // FIXME: When does this happen, and why? + // Don't attempt to draw sprites that are outside the visible + // screen area. An example is the random people walking in + // Jackson Square in GK1. if (itemEntry->celRect.bottom < 0 || itemEntry->celRect.top >= _screen->getDisplayHeight() || itemEntry->celRect.right < 0 || itemEntry->celRect.left >= _screen->getDisplayWidth()) continue; @@ -719,6 +744,9 @@ void GfxFrameout::kernelFrameout() { translatedClipRect = clipRect; translatedClipRect.translate(it->upscaledPlaneRect.left, it->upscaledPlaneRect.top); } else { + // QFG4 passes invalid rectangles when a battle is starting + if (!clipRect.isValidRect()) + continue; clipRect.clip(it->planeClipRect); translatedClipRect = clipRect; translatedClipRect.translate(it->planeRect.left, it->planeRect.top); diff --git a/engines/sci/graphics/frameout.h b/engines/sci/graphics/frameout.h index ecaf450d89..5fd2824224 100644 --- a/engines/sci/graphics/frameout.h +++ b/engines/sci/graphics/frameout.h @@ -40,8 +40,8 @@ typedef Common::List<PlaneLineEntry> PlaneLineList; struct PlaneEntry { reg_t object; - uint16 priority; - uint16 lastPriority; + int16 priority; + int16 lastPriority; int16 planeOffsetX; int16 planeOffsetY; GuiResourceId pictureId; diff --git a/engines/sci/graphics/palette.cpp b/engines/sci/graphics/palette.cpp index ea154c5037..53d69cdcca 100644 --- a/engines/sci/graphics/palette.cpp +++ b/engines/sci/graphics/palette.cpp @@ -100,6 +100,9 @@ GfxPalette::GfxPalette(ResourceManager *resMan, GfxScreen *screen) default: error("GfxPalette: Unknown view type"); } + + _remapOn = false; + resetRemapping(); } GfxPalette::~GfxPalette() { @@ -140,8 +143,9 @@ void GfxPalette::createFromData(byte *data, int bytesLeft, Palette *paletteOut) memset(paletteOut, 0, sizeof(Palette)); // Setup 1:1 mapping - for (colorNo = 0; colorNo < 256; colorNo++) + for (colorNo = 0; colorNo < 256; colorNo++) { paletteOut->mapping[colorNo] = colorNo; + } if (bytesLeft < 37) { // This happens when loading palette of picture 0 in sq5 - the resource is broken and doesn't contain a full @@ -329,6 +333,79 @@ void GfxPalette::set(Palette *newPalette, bool force, bool forceRealMerge) { } } +byte GfxPalette::remapColor(byte remappedColor, byte screenColor) { + assert(_remapOn); + if (_remappingType[remappedColor] == kRemappingByRange) + return _remappingByRange[screenColor]; + else if (_remappingType[remappedColor] == kRemappingByPercent) + return _remappingByPercent[screenColor]; + else + error("remapColor(): Color %d isn't remapped", remappedColor); + + return 0; // should never reach here +} + +void GfxPalette::resetRemapping() { + _remapOn = false; + _remappingPercentToSet = 0; + + for (int i = 0; i < 256; i++) { + _remappingType[i] = kRemappingNone; + _remappingByPercent[i] = i; + _remappingByRange[i] = i; + } +} + +void GfxPalette::setRemappingPercent(byte color, byte percent) { + _remapOn = true; + + // We need to defer the setup of the remapping table every time the screen + // palette is changed, so that kernelFindColor() can find the correct + // colors. Set it once here, in case the palette stays the same and update + // it on each palette change by copySysPaletteToScreen(). + _remappingPercentToSet = percent; + + for (int i = 0; i < 256; i++) { + byte r = _sysPalette.colors[i].r * _remappingPercentToSet / 100; + byte g = _sysPalette.colors[i].g * _remappingPercentToSet / 100; + byte b = _sysPalette.colors[i].b * _remappingPercentToSet / 100; + _remappingByPercent[i] = kernelFindColor(r, g, b); + } + + _remappingType[color] = kRemappingByPercent; +} + +void GfxPalette::setRemappingPercentGray(byte color, byte percent) { + _remapOn = true; + + // We need to defer the setup of the remapping table every time the screen + // palette is changed, so that kernelFindColor() can find the correct + // colors. Set it once here, in case the palette stays the same and update + // it on each palette change by copySysPaletteToScreen(). + _remappingPercentToSet = percent; + + // Note: This is not what the original does, but the results are the same visually + for (int i = 0; i < 256; i++) { + byte rComponent = _sysPalette.colors[i].r * _remappingPercentToSet * 0.30 / 100; + byte gComponent = _sysPalette.colors[i].g * _remappingPercentToSet * 0.59 / 100; + byte bComponent = _sysPalette.colors[i].b * _remappingPercentToSet * 0.11 / 100; + byte luminosity = rComponent + gComponent + bComponent; + _remappingByPercent[i] = kernelFindColor(luminosity, luminosity, luminosity); + } + + _remappingType[color] = kRemappingByPercent; +} + +void GfxPalette::setRemappingRange(byte color, byte from, byte to, byte base) { + _remapOn = true; + + for (int i = from; i <= to; i++) { + _remappingByRange[i] = i + base; + } + + _remappingType[color] = kRemappingByRange; +} + bool GfxPalette::insert(Palette *newPalette, Palette *destPalette) { bool paletteChanged = false; @@ -491,6 +568,16 @@ void GfxPalette::copySysPaletteToScreen() { } } + // Check if we need to reset remapping by percent with the new colors. + if (_remappingPercentToSet) { + for (int i = 0; i < 256; i++) { + byte r = _sysPalette.colors[i].r * _remappingPercentToSet / 100; + byte g = _sysPalette.colors[i].g * _remappingPercentToSet / 100; + byte b = _sysPalette.colors[i].b * _remappingPercentToSet / 100; + _remappingByPercent[i] = kernelFindColor(r, g, b); + } + } + g_system->getPaletteManager()->setPalette(bpal, 0, 256); } @@ -999,8 +1086,9 @@ bool GfxPalette::loadClut(uint16 clutId) { memset(&pal, 0, sizeof(Palette)); // Setup 1:1 mapping - for (int i = 0; i < 256; i++) + for (int i = 0; i < 256; i++) { pal.mapping[i] = i; + } // Now load in the palette for (int i = 1; i <= 236; i++) { diff --git a/engines/sci/graphics/palette.h b/engines/sci/graphics/palette.h index a9ea1c32de..e974781d49 100644 --- a/engines/sci/graphics/palette.h +++ b/engines/sci/graphics/palette.h @@ -31,6 +31,12 @@ namespace Sci { class ResourceManager; class GfxScreen; +enum ColorRemappingType { + kRemappingNone = 0, + kRemappingByRange = 1, + kRemappingByPercent = 2 +}; + /** * Palette class, handles palette operations like changing intensity, setting up the palette, merging different palettes */ @@ -53,6 +59,15 @@ public: void getSys(Palette *pal); uint16 getTotalColorCount() const { return _totalScreenColors; } + void resetRemapping(); + void setRemappingPercent(byte color, byte percent); + void setRemappingPercentGray(byte color, byte percent); + void setRemappingRange(byte color, byte from, byte to, byte base); + bool isRemapped(byte color) const { + return _remapOn && (_remappingType[color] != kRemappingNone); + } + byte remapColor(byte remappedColor, byte screenColor); + void setOnScreen(); void copySysPaletteToScreen(); @@ -123,6 +138,12 @@ private: int _palVarySignal; uint16 _totalScreenColors; + bool _remapOn; + ColorRemappingType _remappingType[256]; + byte _remappingByPercent[256]; + byte _remappingByRange[256]; + uint16 _remappingPercentToSet; + void loadMacIconBarPalette(); byte *_macClut; diff --git a/engines/sci/graphics/screen.cpp b/engines/sci/graphics/screen.cpp index 4020518b72..246b6bfff9 100644 --- a/engines/sci/graphics/screen.cpp +++ b/engines/sci/graphics/screen.cpp @@ -53,23 +53,35 @@ GfxScreen::GfxScreen(ResourceManager *resMan) : _resMan(resMan) { #ifdef ENABLE_SCI32 // GK1 Mac uses a 640x480 resolution too - if (g_sci->getGameId() == GID_GK1 && g_sci->getPlatform() == Common::kPlatformMacintosh) - _upscaledHires = GFX_SCREEN_UPSCALED_640x480; + if (g_sci->getPlatform() == Common::kPlatformMacintosh) { + if (g_sci->getGameId() == GID_GK1) + _upscaledHires = GFX_SCREEN_UPSCALED_640x480; + } #endif if (_resMan->detectHires()) { _width = 640; + _pitch = 640; _height = 480; } else { _width = 320; + _pitch = 320; _height = getLowResScreenHeight(); } +#ifdef ENABLE_SCI32 + // Phantasmagoria 1 sets a window area of 630x450 + if (g_sci->getGameId() == GID_PHANTASMAGORIA) { + _width = 630; + _height = 450; + } +#endif + // Japanese versions of games use hi-res font on upscaled version of the game. if ((g_sci->getLanguage() == Common::JA_JPN) && (getSciVersion() <= SCI_VERSION_1_1)) _upscaledHires = GFX_SCREEN_UPSCALED_640x400; - _pixels = _width * _height; + _pixels = _pitch * _height; switch (_upscaledHires) { case GFX_SCREEN_UPSCALED_640x400: @@ -91,7 +103,7 @@ GfxScreen::GfxScreen(ResourceManager *resMan) : _resMan(resMan) { _upscaledMapping[i] = (i * 12) / 5; break; default: - _displayWidth = _width; + _displayWidth = _pitch; _displayHeight = _height; memset(&_upscaledMapping, 0, sizeof(_upscaledMapping) ); break; @@ -207,7 +219,7 @@ byte GfxScreen::getDrawingMask(byte color, byte prio, byte control) { } void GfxScreen::putPixel(int x, int y, byte drawMask, byte color, byte priority, byte control) { - int offset = y * _width + x; + int offset = y * _pitch + x; if (drawMask & GFX_SCREEN_MASK_VISUAL) { _visualScreen[offset] = color; @@ -240,7 +252,7 @@ void GfxScreen::putFontPixel(int startingY, int x, int y, byte color) { // Do not scale ourselves, but put it on the display directly putPixelOnDisplay(x, y + startingY, color); } else { - int offset = (startingY + y) * _width + x; + int offset = (startingY + y) * _pitch + x; _visualScreen[offset] = color; if (!_upscaledHires) { @@ -342,19 +354,19 @@ void GfxScreen::putKanjiChar(Graphics::FontSJIS *commonFont, int16 x, int16 y, u } byte GfxScreen::getVisual(int x, int y) { - return _visualScreen[y * _width + x]; + return _visualScreen[y * _pitch + x]; } byte GfxScreen::getPriority(int x, int y) { - return _priorityScreen[y * _width + x]; + return _priorityScreen[y * _pitch + x]; } byte GfxScreen::getControl(int x, int y) { - return _controlScreen[y * _width + x]; + return _controlScreen[y * _pitch + x]; } byte GfxScreen::isFillMatch(int16 x, int16 y, byte screenMask, byte t_color, byte t_pri, byte t_con, bool isEGA) { - int offset = y * _width + x; + int offset = y * _pitch + x; byte match = 0; if (screenMask & GFX_SCREEN_MASK_VISUAL) { @@ -415,14 +427,14 @@ void GfxScreen::bitsSave(Common::Rect rect, byte mask, byte *memoryPtr) { memcpy(memoryPtr, (void *)&mask, sizeof(mask)); memoryPtr += sizeof(mask); if (mask & GFX_SCREEN_MASK_VISUAL) { - bitsSaveScreen(rect, _visualScreen, _width, memoryPtr); + bitsSaveScreen(rect, _visualScreen, _pitch, memoryPtr); bitsSaveDisplayScreen(rect, memoryPtr); } if (mask & GFX_SCREEN_MASK_PRIORITY) { - bitsSaveScreen(rect, _priorityScreen, _width, memoryPtr); + bitsSaveScreen(rect, _priorityScreen, _pitch, memoryPtr); } if (mask & GFX_SCREEN_MASK_CONTROL) { - bitsSaveScreen(rect, _controlScreen, _width, memoryPtr); + bitsSaveScreen(rect, _controlScreen, _pitch, memoryPtr); } if (mask & GFX_SCREEN_MASK_DISPLAY) { if (!_upscaledHires) @@ -475,14 +487,14 @@ void GfxScreen::bitsRestore(byte *memoryPtr) { memcpy((void *)&mask, memoryPtr, sizeof(mask)); memoryPtr += sizeof(mask); if (mask & GFX_SCREEN_MASK_VISUAL) { - bitsRestoreScreen(rect, memoryPtr, _visualScreen, _width); + bitsRestoreScreen(rect, memoryPtr, _visualScreen, _pitch); bitsRestoreDisplayScreen(rect, memoryPtr); } if (mask & GFX_SCREEN_MASK_PRIORITY) { - bitsRestoreScreen(rect, memoryPtr, _priorityScreen, _width); + bitsRestoreScreen(rect, memoryPtr, _priorityScreen, _pitch); } if (mask & GFX_SCREEN_MASK_CONTROL) { - bitsRestoreScreen(rect, memoryPtr, _controlScreen, _width); + bitsRestoreScreen(rect, memoryPtr, _controlScreen, _pitch); } if (mask & GFX_SCREEN_MASK_DISPLAY) { if (!_upscaledHires) @@ -560,7 +572,7 @@ void GfxScreen::dither(bool addToFlag) { if (!_unditheringEnabled) { // Do dithering on visual and display-screen for (y = 0; y < _height; y++) { - for (x = 0; x < _width; x++) { + for (x = 0; x < _pitch; x++) { color = *visualPtr; if (color & 0xF0) { color ^= color << 4; @@ -585,7 +597,7 @@ void GfxScreen::dither(bool addToFlag) { memset(&_ditheredPicColors, 0, sizeof(_ditheredPicColors)); // Do dithering on visual screen and put decoded but undithered byte onto display-screen for (y = 0; y < _height; y++) { - for (x = 0; x < _width; x++) { + for (x = 0; x < _pitch; x++) { color = *visualPtr; if (color & 0xF0) { color ^= color << 4; diff --git a/engines/sci/graphics/screen.h b/engines/sci/graphics/screen.h index 73ea596ba1..01fb899edb 100644 --- a/engines/sci/graphics/screen.h +++ b/engines/sci/graphics/screen.h @@ -132,6 +132,7 @@ public: private: uint16 _width; + uint16 _pitch; uint16 _height; uint _pixels; uint16 _displayWidth; diff --git a/engines/sci/graphics/view.cpp b/engines/sci/graphics/view.cpp index 4e5c4da8b2..36aaae9232 100644 --- a/engines/sci/graphics/view.cpp +++ b/engines/sci/graphics/view.cpp @@ -741,8 +741,14 @@ void GfxView::draw(const Common::Rect &rect, const Common::Rect &clipRect, const const int x2 = clipRectTranslated.left + x; const int y2 = clipRectTranslated.top + y; if (!upscaledHires) { - if (priority >= _screen->getPriority(x2, y2)) - _screen->putPixel(x2, y2, drawMask, palette->mapping[color], priority, 0); + if (priority >= _screen->getPriority(x2, y2)) { + if (!_palette->isRemapped(palette->mapping[color])) { + _screen->putPixel(x2, y2, drawMask, palette->mapping[color], priority, 0); + } else { + byte remappedColor = _palette->remapColor(palette->mapping[color], _screen->getVisual(x2, y2)); + _screen->putPixel(x2, y2, drawMask, remappedColor, priority, 0); + } + } } else { // UpscaledHires means view is hires and is supposed to // get drawn onto lowres screen. @@ -851,7 +857,12 @@ void GfxView::drawScaled(const Common::Rect &rect, const Common::Rect &clipRect, const int x2 = clipRectTranslated.left + x; const int y2 = clipRectTranslated.top + y; if (color != clearKey && priority >= _screen->getPriority(x2, y2)) { - _screen->putPixel(x2, y2, drawMask, palette->mapping[color], priority, 0); + if (!_palette->isRemapped(palette->mapping[color])) { + _screen->putPixel(x2, y2, drawMask, palette->mapping[color], priority, 0); + } else { + byte remappedColor = _palette->remapColor(palette->mapping[color], _screen->getVisual(x2, y2)); + _screen->putPixel(x2, y2, drawMask, remappedColor, priority, 0); + } } } } diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp index d43a9d06fc..42ae00b525 100644 --- a/engines/sci/sci.cpp +++ b/engines/sci/sci.cpp @@ -632,7 +632,7 @@ void SciEngine::initGraphics() { _gfxPaint = _gfxPaint32; _gfxText32 = new GfxText32(_gamestate->_segMan, _gfxCache, _gfxScreen); _gfxControls32 = new GfxControls32(_gamestate->_segMan, _gfxCache, _gfxScreen, _gfxText32); - _robotDecoder = new RobotDecoder(g_system->getMixer(), getPlatform() == Common::kPlatformMacintosh); + _robotDecoder = new RobotDecoder(getPlatform() == Common::kPlatformMacintosh); _gfxFrameout = new GfxFrameout(_gamestate->_segMan, _resMan, _gfxCoordAdjuster, _gfxCache, _gfxScreen, _gfxPalette, _gfxPaint32); } else { #endif diff --git a/engines/sci/sound/audio.cpp b/engines/sci/sound/audio.cpp index 123dd21894..528bb51393 100644 --- a/engines/sci/sound/audio.cpp +++ b/engines/sci/sound/audio.cpp @@ -67,7 +67,8 @@ int AudioPlayer::startAudio(uint16 module, uint32 number) { if (audioStream) { _wPlayFlag = false; - _mixer->playStream(Audio::Mixer::kSpeechSoundType, &_audioHandle, audioStream); + Audio::Mixer::SoundType soundType = (module == 65535) ? Audio::Mixer::kSFXSoundType : Audio::Mixer::kSpeechSoundType; + _mixer->playStream(soundType, &_audioHandle, audioStream); return sampleLen; } else { // Don't throw a warning in this case. getAudioStream() already has. Some games diff --git a/engines/sci/sound/soundcmd.cpp b/engines/sci/sound/soundcmd.cpp index 989df7c8a1..5d32f40f18 100644 --- a/engines/sci/sound/soundcmd.cpp +++ b/engines/sci/sound/soundcmd.cpp @@ -96,7 +96,7 @@ void SoundCommandParser::initSoundResource(MusicEntry *newSound) { if (_useDigitalSFX || !newSound->soundRes) { int sampleLen; newSound->pStreamAud = _audio->getAudioStream(newSound->resourceId, 65535, &sampleLen); - newSound->soundType = Audio::Mixer::kSpeechSoundType; + newSound->soundType = Audio::Mixer::kSFXSoundType; } } @@ -367,24 +367,36 @@ reg_t SoundCommandParser::kDoSoundFade(int argc, reg_t *argv, reg_t acc) { case 4: // SCI01+ case 5: // SCI1+ (SCI1 late sound scheme), with fade and continue - musicSlot->fadeTo = CLIP<uint16>(argv[1].toUint16(), 0, MUSIC_VOLUME_MAX); - // sometimes we get objects in that position, fix it up (ffs. workarounds) - if (!argv[1].getSegment()) - musicSlot->fadeStep = volume > musicSlot->fadeTo ? -argv[3].toUint16() : argv[3].toUint16(); - else - musicSlot->fadeStep = volume > musicSlot->fadeTo ? -5 : 5; - musicSlot->fadeTickerStep = argv[2].toUint16() * 16667 / _music->soundGetTempo(); - musicSlot->fadeTicker = 0; - if (argc == 5) { // TODO: We currently treat this argument as a boolean, but may // have to handle different non-zero values differently. (e.g., - // some KQ6 scripts pass 3 here) - musicSlot->stopAfterFading = (argv[4].toUint16() != 0); + // some KQ6 scripts pass 3 here). + // There is a script bug in KQ6, room 460 (the room with the flying + // books). An object is passed here, which should not be treated as + // a true flag. Fixes bugs #3555404 and #3291115. + musicSlot->stopAfterFading = (argv[4].isNumber() && argv[4].toUint16() != 0); } else { musicSlot->stopAfterFading = false; } + musicSlot->fadeTo = CLIP<uint16>(argv[1].toUint16(), 0, MUSIC_VOLUME_MAX); + // Check if the song is already at the requested volume. If it is, don't + // perform any fading. Happens for example during the intro of Longbow. + if (musicSlot->fadeTo != musicSlot->volume) { + // sometimes we get objects in that position, fix it up (ffs. workarounds) + if (!argv[1].getSegment()) + musicSlot->fadeStep = volume > musicSlot->fadeTo ? -argv[3].toUint16() : argv[3].toUint16(); + else + musicSlot->fadeStep = volume > musicSlot->fadeTo ? -5 : 5; + musicSlot->fadeTickerStep = argv[2].toUint16() * 16667 / _music->soundGetTempo(); + } else { + // Stop the music, if requested. Fixes bug #3555404. + if (musicSlot->stopAfterFading) + processStopSound(obj, false); + } + + musicSlot->fadeTicker = 0; + // WORKAROUND/HACK: In the labyrinth in KQ6, when falling in the pit and // lighting the lantern, the game scripts perform a fade in of the game // music, but set it to stop after fading. Remove that flag here. This is @@ -497,12 +509,7 @@ void SoundCommandParser::processUpdateCues(reg_t obj) { // fireworks). // It is also needed in other games, e.g. LSL6 when talking to the // receptionist (bug #3192166). - if (g_sci->getGameId() == GID_LONGBOW && g_sci->getEngineState()->currentRoomNumber() == 95) { - // HACK: Don't set a signal here in the intro of Longbow, as that makes some dialog - // boxes disappear too soon (bug #3044844). - } else { - writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); - } + writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); if (_soundVersion <= SCI_VERSION_0_LATE) { processStopSound(obj, false); } else { diff --git a/engines/sci/video/robot_decoder.cpp b/engines/sci/video/robot_decoder.cpp index ebcfac6054..608c77136f 100644 --- a/engines/sci/video/robot_decoder.cpp +++ b/engines/sci/video/robot_decoder.cpp @@ -22,11 +22,13 @@ #include "common/archive.h" #include "common/stream.h" +#include "common/substream.h" #include "common/system.h" #include "common/textconsole.h" #include "common/util.h" #include "graphics/surface.h" +#include "audio/audiostream.h" #include "audio/decoders/raw.h" #include "sci/resource.h" @@ -63,57 +65,26 @@ namespace Sci { // our graphics engine, it looks just like a part of the room. A RBT can move // around the screen and go behind other objects. (...) -#ifdef ENABLE_SCI32 - -enum robotPalTypes { +enum RobotPalTypes { kRobotPalVariable = 0, kRobotPalConstant = 1 }; -RobotDecoder::RobotDecoder(Audio::Mixer *mixer, bool isBigEndian) { - _surface = 0; - _width = 0; - _height = 0; +RobotDecoder::RobotDecoder(bool isBigEndian) { _fileStream = 0; - _audioStream = 0; - _dirtyPalette = false; _pos = Common::Point(0, 0); - _mixer = mixer; _isBigEndian = isBigEndian; + _frameTotalSize = 0; } RobotDecoder::~RobotDecoder() { close(); } -bool RobotDecoder::load(GuiResourceId id) { - // TODO: RAMA's robot 1003 cannot be played (shown at the menu screen) - - // its drawn at odd coordinates. SV can't play it either (along with some - // others), so it must be some new functionality added in RAMA's robot - // videos. Skip it for now. - if (g_sci->getGameId() == GID_RAMA && id == 1003) - return false; - - // TODO: The robot video in the Lighthouse demo gets stuck - if (g_sci->getGameId() == GID_LIGHTHOUSE && id == 16) - return false; - - Common::String fileName = Common::String::format("%d.rbt", id); - Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(fileName); - - if (!stream) { - warning("Unable to open robot file %s", fileName.c_str()); - return false; - } - - return loadStream(stream); -} - bool RobotDecoder::loadStream(Common::SeekableReadStream *stream) { close(); _fileStream = new Common::SeekableSubReadStreamEndian(stream, 0, stream->size(), _isBigEndian, DisposeAfterUse::YES); - _surface = new Graphics::Surface(); readHeaderChunk(); @@ -125,131 +96,60 @@ bool RobotDecoder::loadStream(Common::SeekableReadStream *stream) { if (_header.version < 4 || _header.version > 6) error("Unknown robot version: %d", _header.version); - if (_header.hasSound) { - _audioStream = Audio::makeQueuingAudioStream(11025, false); - _mixer->playStream(Audio::Mixer::kMusicSoundType, &_audioHandle, _audioStream, -1, getVolume(), getBalance()); - } + RobotVideoTrack *videoTrack = new RobotVideoTrack(_header.frameCount); + addTrack(videoTrack); - readPaletteChunk(_header.paletteDataSize); - readFrameSizesChunk(); - calculateVideoDimensions(); - _surface->create(_width, _height, Graphics::PixelFormat::createFormatCLUT8()); + if (_header.hasSound) + addTrack(new RobotAudioTrack()); + videoTrack->readPaletteChunk(_fileStream, _header.paletteDataSize); + readFrameSizesChunk(); + videoTrack->calculateVideoDimensions(_fileStream, _frameTotalSize); return true; } -void RobotDecoder::readHeaderChunk() { - // Header (60 bytes) - _fileStream->skip(6); - _header.version = _fileStream->readUint16(); - _header.audioChunkSize = _fileStream->readUint16(); - _header.audioSilenceSize = _fileStream->readUint16(); - _fileStream->skip(2); - _header.frameCount = _fileStream->readUint16(); - _header.paletteDataSize = _fileStream->readUint16(); - _header.unkChunkDataSize = _fileStream->readUint16(); - _fileStream->skip(5); - _header.hasSound = _fileStream->readByte(); - _fileStream->skip(34); - - // Some videos (e.g. robot 1305 in Phantasmagoria and - // robot 184 in Lighthouse) have an unknown chunk before - // the palette chunk (probably used for sound preloading). - // Skip it here. - if (_header.unkChunkDataSize) - _fileStream->skip(_header.unkChunkDataSize); -} - -void RobotDecoder::readPaletteChunk(uint16 chunkSize) { - byte *paletteData = new byte[chunkSize]; - _fileStream->read(paletteData, chunkSize); - - // SCI1.1 palette - byte palFormat = paletteData[32]; - uint16 palColorStart = paletteData[25]; - uint16 palColorCount = READ_SCI11ENDIAN_UINT16(paletteData + 29); +bool RobotDecoder::load(GuiResourceId id) { + // TODO: RAMA's robot 1003 cannot be played (shown at the menu screen) - + // its drawn at odd coordinates. SV can't play it either (along with some + // others), so it must be some new functionality added in RAMA's robot + // videos. Skip it for now. + if (g_sci->getGameId() == GID_RAMA && id == 1003) + return false; + + // TODO: The robot video in the Lighthouse demo gets stuck + if (g_sci->getGameId() == GID_LIGHTHOUSE && id == 16) + return false; - int palOffset = 37; - memset(_palette, 0, 256 * 3); + Common::String fileName = Common::String::format("%d.rbt", id); + Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(fileName); - for (uint16 colorNo = palColorStart; colorNo < palColorStart + palColorCount; colorNo++) { - if (palFormat == kRobotPalVariable) - palOffset++; - _palette[colorNo * 3 + 0] = paletteData[palOffset++]; - _palette[colorNo * 3 + 1] = paletteData[palOffset++]; - _palette[colorNo * 3 + 2] = paletteData[palOffset++]; + if (!stream) { + warning("Unable to open robot file %s", fileName.c_str()); + return false; } - _dirtyPalette = true; - delete[] paletteData; + return loadStream(stream); } +void RobotDecoder::close() { + VideoDecoder::close(); -void RobotDecoder::readFrameSizesChunk() { - // The robot video file contains 2 tables, with one entry for each frame: - // - A table containing the size of the image in each video frame - // - A table containing the total size of each video frame. - // In v5 robots, the tables contain 16-bit integers, whereas in v6 robots, - // they contain 32-bit integers. - - _frameTotalSize = new uint32[_header.frameCount]; - - // TODO: The table reading code can probably be removed once the - // audio chunk size is figured out (check the TODO inside processNextFrame()) -#if 0 - // We don't need any of the two tables to play the video, so we ignore - // both of them. - uint16 wordSize = _header.version == 6 ? 4 : 2; - _fileStream->skip(_header.frameCount * wordSize * 2); -#else - switch (_header.version) { - case 4: - case 5: // sizes are 16-bit integers - // Skip table with frame image sizes, as we don't need it - _fileStream->skip(_header.frameCount * 2); - for (int i = 0; i < _header.frameCount; ++i) - _frameTotalSize[i] = _fileStream->readUint16(); - break; - case 6: // sizes are 32-bit integers - // Skip table with frame image sizes, as we don't need it - _fileStream->skip(_header.frameCount * 4); - for (int i = 0; i < _header.frameCount; ++i) - _frameTotalSize[i] = _fileStream->readUint32(); - break; - default: - error("Can't yet handle index table for robot version %d", _header.version); - } -#endif - - // 2 more unknown tables - _fileStream->skip(1024 + 512); + delete _fileStream; + _fileStream = 0; - // Pad to nearest 2 kilobytes - uint32 curPos = _fileStream->pos(); - if (curPos & 0x7ff) - _fileStream->seek((curPos & ~0x7ff) + 2048); + delete[] _frameTotalSize; + _frameTotalSize = 0; } -void RobotDecoder::calculateVideoDimensions() { - // This is an O(n) operation, as each frame has a different size. - // We need to know the actual frame size to have a constant video size. - uint32 pos = _fileStream->pos(); - - for (uint32 curFrame = 0; curFrame < _header.frameCount; curFrame++) { - _fileStream->skip(4); - uint16 frameWidth = _fileStream->readUint16(); - uint16 frameHeight = _fileStream->readUint16(); - if (frameWidth > _width) - _width = frameWidth; - if (frameHeight > _height) - _height = frameHeight; - _fileStream->skip(_frameTotalSize[curFrame] - 8); - } +void RobotDecoder::readNextPacket() { + // Get our track + RobotVideoTrack *videoTrack = (RobotVideoTrack *)getTrack(0); + videoTrack->increaseCurFrame(); + Graphics::Surface *surface = videoTrack->getSurface(); - _fileStream->seek(pos); -} + if (videoTrack->endOfTrack()) + return; -const Graphics::Surface *RobotDecoder::decodeNextFrame() { // Read frame image header (24 bytes) _fileStream->skip(3); byte frameScale = _fileStream->readByte(); @@ -258,23 +158,28 @@ const Graphics::Surface *RobotDecoder::decodeNextFrame() { _fileStream->skip(4); // unknown, almost always 0 uint16 frameX = _fileStream->readUint16(); uint16 frameY = _fileStream->readUint16(); + // TODO: In v4 robot files, frameX and frameY have a different meaning. // Set them both to 0 for v4 for now, so that robots in PQ:SWAT show up // correctly. if (_header.version == 4) frameX = frameY = 0; + uint16 compressedSize = _fileStream->readUint16(); uint16 frameFragments = _fileStream->readUint16(); _fileStream->skip(4); // unknown uint32 decompressedSize = frameWidth * frameHeight * frameScale / 100; + // FIXME: A frame's height + position can go off limits... why? With the // following, we cut the contents to fit the frame - uint16 scaledHeight = CLIP<uint16>(decompressedSize / frameWidth, 0, _height - frameY); + uint16 scaledHeight = CLIP<uint16>(decompressedSize / frameWidth, 0, surface->h - frameY); + // FIXME: Same goes for the frame's width + position. In this case, we // modify the position to fit the contents on screen. - if (frameWidth + frameX > _width) - frameX = _width - frameWidth; - assert (frameWidth + frameX <= _width && scaledHeight + frameY <= _height); + if (frameWidth + frameX > surface->w) + frameX = surface->w - frameWidth; + + assert(frameWidth + frameX <= surface->w && scaledHeight + frameY <= surface->h); DecompressorLZS lzs; byte *decompressedFrame = new byte[decompressedSize]; @@ -305,24 +210,23 @@ const Graphics::Surface *RobotDecoder::decodeNextFrame() { // Copy over the decompressed frame byte *inFrame = decompressedFrame; - byte *outFrame = (byte *)_surface->pixels; + byte *outFrame = (byte *)surface->pixels; // Black out the surface - memset(outFrame, 0, _width * _height); + memset(outFrame, 0, surface->w * surface->h); // Move to the correct y coordinate - outFrame += _width * frameY; + outFrame += surface->w * frameY; for (uint16 y = 0; y < scaledHeight; y++) { memcpy(outFrame + frameX, inFrame, frameWidth); inFrame += frameWidth; - outFrame += _width; + outFrame += surface->w; } delete[] decompressedFrame; - // +1 because we start with frame number -1 - uint32 audioChunkSize = _frameTotalSize[_curFrame + 1] - (24 + compressedSize); + uint32 audioChunkSize = _frameTotalSize[videoTrack->getCurFrame()] - (24 + compressedSize); // TODO: The audio chunk size below is usually correct, but there are some // exceptions (e.g. robot 4902 in Phantasmagoria, towards its end) @@ -337,51 +241,166 @@ const Graphics::Surface *RobotDecoder::decodeNextFrame() { // Queue the next audio frame // FIXME: For some reason, there are audio hiccups/gaps if (_header.hasSound) { - _fileStream->skip(8); // header - _audioStream->queueBuffer(g_sci->_audio->getDecodedRobotAudioFrame(_fileStream, audioChunkSize - 8), - (audioChunkSize - 8) * 2, DisposeAfterUse::NO, - Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN); + RobotAudioTrack *audioTrack = (RobotAudioTrack *)getTrack(1); + _fileStream->skip(8); // header + audioChunkSize -= 8; + audioTrack->queueBuffer(g_sci->_audio->getDecodedRobotAudioFrame(_fileStream, audioChunkSize), audioChunkSize * 2); } else { _fileStream->skip(audioChunkSize); - } - - if (_curFrame == -1) - _startTime = g_system->getMillis(); + } +} - _curFrame++; +void RobotDecoder::readHeaderChunk() { + // Header (60 bytes) + _fileStream->skip(6); + _header.version = _fileStream->readUint16(); + _header.audioChunkSize = _fileStream->readUint16(); + _header.audioSilenceSize = _fileStream->readUint16(); + _fileStream->skip(2); + _header.frameCount = _fileStream->readUint16(); + _header.paletteDataSize = _fileStream->readUint16(); + _header.unkChunkDataSize = _fileStream->readUint16(); + _fileStream->skip(5); + _header.hasSound = _fileStream->readByte(); + _fileStream->skip(34); - return _surface; + // Some videos (e.g. robot 1305 in Phantasmagoria and + // robot 184 in Lighthouse) have an unknown chunk before + // the palette chunk (probably used for sound preloading). + // Skip it here. + if (_header.unkChunkDataSize) + _fileStream->skip(_header.unkChunkDataSize); } -void RobotDecoder::close() { - if (!_fileStream) - return; +void RobotDecoder::readFrameSizesChunk() { + // The robot video file contains 2 tables, with one entry for each frame: + // - A table containing the size of the image in each video frame + // - A table containing the total size of each video frame. + // In v5 robots, the tables contain 16-bit integers, whereas in v6 robots, + // they contain 32-bit integers. - delete _fileStream; - _fileStream = 0; + _frameTotalSize = new uint32[_header.frameCount]; + // TODO: The table reading code can probably be removed once the + // audio chunk size is figured out (check the TODO inside processNextFrame()) +#if 0 + // We don't need any of the two tables to play the video, so we ignore + // both of them. + uint16 wordSize = _header.version == 6 ? 4 : 2; + _fileStream->skip(_header.frameCount * wordSize * 2); +#else + switch (_header.version) { + case 4: + case 5: // sizes are 16-bit integers + // Skip table with frame image sizes, as we don't need it + _fileStream->skip(_header.frameCount * 2); + for (int i = 0; i < _header.frameCount; ++i) + _frameTotalSize[i] = _fileStream->readUint16(); + break; + case 6: // sizes are 32-bit integers + // Skip table with frame image sizes, as we don't need it + _fileStream->skip(_header.frameCount * 4); + for (int i = 0; i < _header.frameCount; ++i) + _frameTotalSize[i] = _fileStream->readUint32(); + break; + default: + error("Can't yet handle index table for robot version %d", _header.version); + } +#endif + + // 2 more unknown tables + _fileStream->skip(1024 + 512); + + // Pad to nearest 2 kilobytes + uint32 curPos = _fileStream->pos(); + if (curPos & 0x7ff) + _fileStream->seek((curPos & ~0x7ff) + 2048); +} + +RobotDecoder::RobotVideoTrack::RobotVideoTrack(int frameCount) : _frameCount(frameCount) { + _surface = new Graphics::Surface(); + _curFrame = -1; + _dirtyPalette = false; +} + +RobotDecoder::RobotVideoTrack::~RobotVideoTrack() { _surface->free(); delete _surface; - _surface = 0; +} - if (_header.hasSound) { - _mixer->stopHandle(_audioHandle); - //delete _audioStream; _audioStream = 0; +uint16 RobotDecoder::RobotVideoTrack::getWidth() const { + return _surface->w; +} + +uint16 RobotDecoder::RobotVideoTrack::getHeight() const { + return _surface->h; +} + +Graphics::PixelFormat RobotDecoder::RobotVideoTrack::getPixelFormat() const { + return _surface->format; +} + +void RobotDecoder::RobotVideoTrack::readPaletteChunk(Common::SeekableSubReadStreamEndian *stream, uint16 chunkSize) { + byte *paletteData = new byte[chunkSize]; + stream->read(paletteData, chunkSize); + + // SCI1.1 palette + byte palFormat = paletteData[32]; + uint16 palColorStart = paletteData[25]; + uint16 palColorCount = READ_SCI11ENDIAN_UINT16(paletteData + 29); + + int palOffset = 37; + memset(_palette, 0, 256 * 3); + + for (uint16 colorNo = palColorStart; colorNo < palColorStart + palColorCount; colorNo++) { + if (palFormat == kRobotPalVariable) + palOffset++; + _palette[colorNo * 3 + 0] = paletteData[palOffset++]; + _palette[colorNo * 3 + 1] = paletteData[palOffset++]; + _palette[colorNo * 3 + 2] = paletteData[palOffset++]; } - reset(); + _dirtyPalette = true; + delete[] paletteData; } -void RobotDecoder::updateVolume() { - if (g_system->getMixer()->isSoundHandleActive(_audioHandle)) - g_system->getMixer()->setChannelVolume(_audioHandle, getVolume()); +void RobotDecoder::RobotVideoTrack::calculateVideoDimensions(Common::SeekableSubReadStreamEndian *stream, uint32 *frameSizes) { + // This is an O(n) operation, as each frame has a different size. + // We need to know the actual frame size to have a constant video size. + uint32 pos = stream->pos(); + + uint16 width = 0, height = 0; + + for (int curFrame = 0; curFrame < _frameCount; curFrame++) { + stream->skip(4); + uint16 frameWidth = stream->readUint16(); + uint16 frameHeight = stream->readUint16(); + if (frameWidth > width) + width = frameWidth; + if (frameHeight > height) + height = frameHeight; + stream->skip(frameSizes[curFrame] - 8); + } + + stream->seek(pos); + + _surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8()); } -void RobotDecoder::updateBalance() { - if (g_system->getMixer()->isSoundHandleActive(_audioHandle)) - g_system->getMixer()->setChannelBalance(_audioHandle, getBalance()); +RobotDecoder::RobotAudioTrack::RobotAudioTrack() { + _audioStream = Audio::makeQueuingAudioStream(11025, false); } -#endif +RobotDecoder::RobotAudioTrack::~RobotAudioTrack() { + delete _audioStream; +} + +void RobotDecoder::RobotAudioTrack::queueBuffer(byte *buffer, int size) { + _audioStream->queueBuffer(buffer, size, DisposeAfterUse::YES, Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN); +} + +Audio::AudioStream *RobotDecoder::RobotAudioTrack::getAudioStream() const { + return _audioStream; +} } // End of namespace Sci diff --git a/engines/sci/video/robot_decoder.h b/engines/sci/video/robot_decoder.h index e9cefe7d91..ebc3262939 100644 --- a/engines/sci/video/robot_decoder.h +++ b/engines/sci/video/robot_decoder.h @@ -25,84 +25,103 @@ #include "common/rational.h" #include "common/rect.h" -#include "common/stream.h" -#include "common/substream.h" -#include "audio/audiostream.h" -#include "audio/mixer.h" -#include "graphics/pixelformat.h" #include "video/video_decoder.h" -namespace Sci { +namespace Audio { +class QueuingAudioStream; +} -#ifdef ENABLE_SCI32 - -struct RobotHeader { - // 6 bytes, identifier bytes - uint16 version; - uint16 audioChunkSize; - uint16 audioSilenceSize; - // 2 bytes, unknown - uint16 frameCount; - uint16 paletteDataSize; - uint16 unkChunkDataSize; - // 5 bytes, unknown - byte hasSound; - // 34 bytes, unknown -}; +namespace Common { +class SeekableSubReadStreamEndian; +} + +namespace Sci { -class RobotDecoder : public Video::FixedRateVideoDecoder { +class RobotDecoder : public Video::VideoDecoder { public: - RobotDecoder(Audio::Mixer *mixer, bool isBigEndian); + RobotDecoder(bool isBigEndian); virtual ~RobotDecoder(); bool loadStream(Common::SeekableReadStream *stream); bool load(GuiResourceId id); void close(); - - bool isVideoLoaded() const { return _fileStream != 0; } - uint16 getWidth() const { return _width; } - uint16 getHeight() const { return _height; } - uint32 getFrameCount() const { return _header.frameCount; } - const Graphics::Surface *decodeNextFrame(); - Graphics::PixelFormat getPixelFormat() const { return Graphics::PixelFormat::createFormatCLUT8(); } - const byte *getPalette() { _dirtyPalette = false; return _palette; } - bool hasDirtyPalette() const { return _dirtyPalette; } + void setPos(uint16 x, uint16 y) { _pos = Common::Point(x, y); } Common::Point getPos() const { return _pos; } protected: - // VideoDecoder API - void updateVolume(); - void updateBalance(); - - // FixedRateVideoDecoder API - Common::Rational getFrameRate() const { return Common::Rational(60, 10); } - + void readNextPacket(); + private: + class RobotVideoTrack : public FixedRateVideoTrack { + public: + RobotVideoTrack(int frameCount); + ~RobotVideoTrack(); + + uint16 getWidth() const; + uint16 getHeight() const; + Graphics::PixelFormat getPixelFormat() const; + int getCurFrame() const { return _curFrame; } + int getFrameCount() const { return _frameCount; } + const Graphics::Surface *decodeNextFrame() { return _surface; } + const byte *getPalette() const { _dirtyPalette = false; return _palette; } + bool hasDirtyPalette() const { return _dirtyPalette; } + + void readPaletteChunk(Common::SeekableSubReadStreamEndian *stream, uint16 chunkSize); + void calculateVideoDimensions(Common::SeekableSubReadStreamEndian *stream, uint32 *frameSizes); + Graphics::Surface *getSurface() { return _surface; } + void increaseCurFrame() { _curFrame++; } + + protected: + Common::Rational getFrameRate() const { return Common::Rational(60, 10); } + + private: + int _frameCount; + int _curFrame; + byte _palette[256 * 3]; + mutable bool _dirtyPalette; + Graphics::Surface *_surface; + }; + + class RobotAudioTrack : public AudioTrack { + public: + RobotAudioTrack(); + ~RobotAudioTrack(); + + Audio::Mixer::SoundType getSoundType() const { return Audio::Mixer::kMusicSoundType; } + + void queueBuffer(byte *buffer, int size); + + protected: + Audio::AudioStream *getAudioStream() const; + + private: + Audio::QueuingAudioStream *_audioStream; + }; + + struct RobotHeader { + // 6 bytes, identifier bytes + uint16 version; + uint16 audioChunkSize; + uint16 audioSilenceSize; + // 2 bytes, unknown + uint16 frameCount; + uint16 paletteDataSize; + uint16 unkChunkDataSize; + // 5 bytes, unknown + byte hasSound; + // 34 bytes, unknown + } _header; + void readHeaderChunk(); - void readPaletteChunk(uint16 chunkSize); void readFrameSizesChunk(); - void calculateVideoDimensions(); - - void freeData(); - RobotHeader _header; Common::Point _pos; bool _isBigEndian; + uint32 *_frameTotalSize; Common::SeekableSubReadStreamEndian *_fileStream; - - uint16 _width; - uint16 _height; - uint32 *_frameTotalSize; - byte _palette[256 * 3]; - bool _dirtyPalette; - Graphics::Surface *_surface; - Audio::QueuingAudioStream *_audioStream; - Audio::SoundHandle _audioHandle; - Audio::Mixer *_mixer; }; -#endif } // End of namespace Sci diff --git a/engines/sci/video/seq_decoder.cpp b/engines/sci/video/seq_decoder.cpp index abd64911a7..a7b6346eca 100644 --- a/engines/sci/video/seq_decoder.cpp +++ b/engines/sci/video/seq_decoder.cpp @@ -41,33 +41,44 @@ enum seqFrameTypes { kSeqFrameDiff = 1 }; -SeqDecoder::SeqDecoder() { - _fileStream = 0; - _surface = 0; - _dirtyPalette = false; +SEQDecoder::SEQDecoder(uint frameDelay) : _frameDelay(frameDelay) { } -SeqDecoder::~SeqDecoder() { +SEQDecoder::~SEQDecoder() { close(); } -bool SeqDecoder::loadStream(Common::SeekableReadStream *stream) { +bool SEQDecoder::loadStream(Common::SeekableReadStream *stream) { close(); + addTrack(new SEQVideoTrack(stream, _frameDelay)); + + return true; +} + +SEQDecoder::SEQVideoTrack::SEQVideoTrack(Common::SeekableReadStream *stream, uint frameDelay) { + assert(stream); + assert(frameDelay != 0); _fileStream = stream; + _frameDelay = frameDelay; + _curFrame = -1; + _surface = new Graphics::Surface(); _surface->create(SEQ_SCREEN_WIDTH, SEQ_SCREEN_HEIGHT, Graphics::PixelFormat::createFormatCLUT8()); _frameCount = _fileStream->readUint16LE(); - // Set palette - int paletteChunkSize = _fileStream->readUint32LE(); - readPaletteChunk(paletteChunkSize); + // Set initial palette + readPaletteChunk(_fileStream->readUint32LE()); +} - return true; +SEQDecoder::SEQVideoTrack::~SEQVideoTrack() { + delete _fileStream; + _surface->free(); + delete _surface; } -void SeqDecoder::readPaletteChunk(uint16 chunkSize) { +void SEQDecoder::SEQVideoTrack::readPaletteChunk(uint16 chunkSize) { byte *paletteData = new byte[chunkSize]; _fileStream->read(paletteData, chunkSize); @@ -91,23 +102,7 @@ void SeqDecoder::readPaletteChunk(uint16 chunkSize) { delete[] paletteData; } -void SeqDecoder::close() { - if (!_fileStream) - return; - - _frameDelay = 0; - - delete _fileStream; - _fileStream = 0; - - _surface->free(); - delete _surface; - _surface = 0; - - reset(); -} - -const Graphics::Surface *SeqDecoder::decodeNextFrame() { +const Graphics::Surface *SEQDecoder::SEQVideoTrack::decodeNextFrame() { int16 frameWidth = _fileStream->readUint16LE(); int16 frameHeight = _fileStream->readUint16LE(); int16 frameLeft = _fileStream->readUint16LE(); @@ -142,9 +137,6 @@ const Graphics::Surface *SeqDecoder::decodeNextFrame() { delete[] buf; } - if (_curFrame == -1) - _startTime = g_system->getMillis(); - _curFrame++; return _surface; } @@ -159,7 +151,7 @@ const Graphics::Surface *SeqDecoder::decodeNextFrame() { } \ memcpy(dest + writeRow * SEQ_SCREEN_WIDTH + writeCol, litData + litPos, n); -bool SeqDecoder::decodeFrame(byte *rleData, int rleSize, byte *litData, int litSize, byte *dest, int left, int width, int height, int colorKey) { +bool SEQDecoder::SEQVideoTrack::decodeFrame(byte *rleData, int rleSize, byte *litData, int litSize, byte *dest, int left, int width, int height, int colorKey) { int writeRow = 0; int writeCol = left; int litPos = 0; @@ -237,4 +229,9 @@ bool SeqDecoder::decodeFrame(byte *rleData, int rleSize, byte *litData, int litS return true; } +const byte *SEQDecoder::SEQVideoTrack::getPalette() const { + _dirtyPalette = false; + return _palette; +} + } // End of namespace Sci diff --git a/engines/sci/video/seq_decoder.h b/engines/sci/video/seq_decoder.h index 800a3c9024..890f349feb 100644 --- a/engines/sci/video/seq_decoder.h +++ b/engines/sci/video/seq_decoder.h @@ -40,44 +40,49 @@ namespace Sci { /** * Implementation of the Sierra SEQ decoder, used in KQ6 DOS floppy/CD and GK1 DOS */ -class SeqDecoder : public Video::FixedRateVideoDecoder { +class SEQDecoder : public Video::VideoDecoder { public: - SeqDecoder(); - virtual ~SeqDecoder(); + SEQDecoder(uint frameDelay); + virtual ~SEQDecoder(); bool loadStream(Common::SeekableReadStream *stream); - void close(); - - void setFrameDelay(int frameDelay) { _frameDelay = frameDelay; } - - bool isVideoLoaded() const { return _fileStream != 0; } - uint16 getWidth() const { return SEQ_SCREEN_WIDTH; } - uint16 getHeight() const { return SEQ_SCREEN_HEIGHT; } - uint32 getFrameCount() const { return _frameCount; } - const Graphics::Surface *decodeNextFrame(); - Graphics::PixelFormat getPixelFormat() const { return Graphics::PixelFormat::createFormatCLUT8(); } - const byte *getPalette() { _dirtyPalette = false; return _palette; } - bool hasDirtyPalette() const { return _dirtyPalette; } - -protected: - Common::Rational getFrameRate() const { assert(_frameDelay); return Common::Rational(60, _frameDelay); } private: - enum { - SEQ_SCREEN_WIDTH = 320, - SEQ_SCREEN_HEIGHT = 200 + class SEQVideoTrack : public FixedRateVideoTrack { + public: + SEQVideoTrack(Common::SeekableReadStream *stream, uint frameDelay); + ~SEQVideoTrack(); + + uint16 getWidth() const { return SEQ_SCREEN_WIDTH; } + uint16 getHeight() const { return SEQ_SCREEN_HEIGHT; } + Graphics::PixelFormat getPixelFormat() const { return Graphics::PixelFormat::createFormatCLUT8(); } + int getCurFrame() const { return _curFrame; } + int getFrameCount() const { return _frameCount; } + const Graphics::Surface *decodeNextFrame(); + const byte *getPalette() const; + bool hasDirtyPalette() const { return _dirtyPalette; } + + protected: + Common::Rational getFrameRate() const { return Common::Rational(60, _frameDelay); } + + private: + enum { + SEQ_SCREEN_WIDTH = 320, + SEQ_SCREEN_HEIGHT = 200 + }; + + void readPaletteChunk(uint16 chunkSize); + bool decodeFrame(byte *rleData, int rleSize, byte *litData, int litSize, byte *dest, int left, int width, int height, int colorKey); + + Common::SeekableReadStream *_fileStream; + int _curFrame, _frameCount; + byte _palette[256 * 3]; + mutable bool _dirtyPalette; + Graphics::Surface *_surface; + uint _frameDelay; }; - void readPaletteChunk(uint16 chunkSize); - bool decodeFrame(byte *rleData, int rleSize, byte *litData, int litSize, byte *dest, int left, int width, int height, int colorKey); - - uint16 _width, _height; - uint16 _frameDelay; - Common::SeekableReadStream *_fileStream; - byte _palette[256 * 3]; - bool _dirtyPalette; - uint32 _frameCount; - Graphics::Surface *_surface; + uint _frameDelay; }; } // End of namespace Sci diff --git a/engines/scumm/detection.cpp b/engines/scumm/detection.cpp index ebf1a2675c..5404c7f8b1 100644 --- a/engines/scumm/detection.cpp +++ b/engines/scumm/detection.cpp @@ -20,9 +20,6 @@ * */ -// FIXME: Avoid using printf -#define FORBIDDEN_SYMBOL_EXCEPTION_printf - #include "base/plugins.h" #include "common/archive.h" @@ -1066,15 +1063,19 @@ Common::Error ScummMetaEngine::createInstance(OSystem *syst, Engine **engine) co // unknown MD5, or with a medium debug level in case of a known MD5 (for // debugging purposes). if (!findInMD5Table(res.md5.c_str())) { - printf("Your game version appears to be unknown. If this is *NOT* a fan-modified\n"); - printf("version (in particular, not a fan-made translation), please, report the\n"); - printf("following data to the ScummVM team along with name of the game you tried\n"); - printf("to add and its version/language/etc.:\n"); + Common::String md5Warning; + + md5Warning = "Your game version appears to be unknown. If this is *NOT* a fan-modified\n"; + md5Warning += "version (in particular, not a fan-made translation), please, report the\n"; + md5Warning += "following data to the ScummVM team along with name of the game you tried\n"; + md5Warning += "to add and its version/language/etc.:\n"; - printf(" SCUMM gameid '%s', file '%s', MD5 '%s'\n\n", + md5Warning += Common::String::format(" SCUMM gameid '%s', file '%s', MD5 '%s'\n\n", res.game.gameid, generateFilenameForDetection(res.fp.pattern, res.fp.genMethod).c_str(), res.md5.c_str()); + + g_system->logMessage(LogMessageType::kWarning, md5Warning.c_str()); } else { debug(1, "Using MD5 '%s'", res.md5.c_str()); } @@ -1266,7 +1267,6 @@ SaveStateDescriptor ScummMetaEngine::querySaveMetaInfos(const char *target, int Graphics::Surface *thumbnail = ScummEngine::loadThumbnailFromSlot(target, slot); SaveStateDescriptor desc(slot, saveDesc); - desc.setDeletableFlag(true); desc.setThumbnail(thumbnail); SaveStateMetaInfos infos; diff --git a/engines/scumm/he/animation_he.cpp b/engines/scumm/he/animation_he.cpp index 40e99c26a8..be17a3b305 100644 --- a/engines/scumm/he/animation_he.cpp +++ b/engines/scumm/he/animation_he.cpp @@ -40,7 +40,7 @@ MoviePlayer::MoviePlayer(ScummEngine_v90he *vm, Audio::Mixer *mixer) : _vm(vm) { _video = new Video::BinkDecoder(); else #endif - _video = new Video::SmackerDecoder(mixer); + _video = new Video::SmackerDecoder(); _flags = 0; _wizResNum = 0; @@ -61,11 +61,16 @@ int MoviePlayer::load(const char *filename, int flags, int image) { if (_video->isVideoLoaded()) _video->close(); + // Ensure that Bink will use our PixelFormat + _video->setDefaultHighColorFormat(g_system->getScreenFormat()); + if (!_video->loadFile(filename)) { warning("Failed to load video file %s", filename); return -1; } + _video->start(); + debug(1, "Playing video %s", filename); if (flags & 2) diff --git a/engines/scumm/scumm-md5.h b/engines/scumm/scumm-md5.h index 0814e3bfe1..d4eefe8c28 100644 --- a/engines/scumm/scumm-md5.h +++ b/engines/scumm/scumm-md5.h @@ -1,5 +1,5 @@ /* - This file was generated by the md5table tool on Fri Jun 15 09:16:45 2012 + This file was generated by the md5table tool on Sat Jul 07 23:39:27 2012 DO NOT EDIT MANUALLY! */ @@ -191,6 +191,7 @@ static const MD5Table md5table[] = { { "45082a5c9f42ba14dacfe1fdeeba819d", "freddicove", "HE 100", "Demo", 18422, Common::EN_ANY, Common::kPlatformUnknown }, { "45152f7cf2ba8f43cf8a8ea2e740ae09", "monkey", "VGA", "VGA", 8357, Common::ES_ESP, Common::kPlatformPC }, { "4521138d15d1fd7649c31fb981746231", "pajama2", "HE 98.5", "Demo", -1, Common::DE_DEU, Common::kPlatformUnknown }, + { "4522564b3c31aaf218b6a96826a549fd", "maze", "HE 99", "", -1, Common::EN_USA, Common::kPlatformWindows }, { "46b53fd430adcfbed791b48a0d4b079f", "funpack", "", "", -1, Common::EN_ANY, Common::kPlatformPC }, { "470c45b636139bb40716daa1c7edaad0", "loom", "EGA", "EGA", -1, Common::DE_DEU, Common::kPlatformPC }, { "477dbafbd66a53c98416dc01aef019ad", "monkey", "EGA", "EGA", -1, Common::IT_ITA, Common::kPlatformPC }, diff --git a/engines/sword1/animation.cpp b/engines/sword1/animation.cpp index ddafd964eb..f7add4eed2 100644 --- a/engines/sword1/animation.cpp +++ b/engines/sword1/animation.cpp @@ -37,6 +37,7 @@ #include "gui/message.h" +#include "video/dxa_decoder.h" #include "video/psx_decoder.h" #include "video/smk_decoder.h" @@ -96,9 +97,8 @@ static const char *const sequenceListPSX[20] = { // Basic movie player /////////////////////////////////////////////////////////////////////////////// -MoviePlayer::MoviePlayer(SwordEngine *vm, Text *textMan, ResMan *resMan, Audio::Mixer *snd, OSystem *system, Audio::SoundHandle *bgSoundHandle, Video::VideoDecoder *decoder, DecoderType decoderType) - : _vm(vm), _textMan(textMan), _resMan(resMan), _snd(snd), _bgSoundHandle(bgSoundHandle), _system(system) { - _bgSoundStream = NULL; +MoviePlayer::MoviePlayer(SwordEngine *vm, Text *textMan, ResMan *resMan, OSystem *system, Video::VideoDecoder *decoder, DecoderType decoderType) + : _vm(vm), _textMan(textMan), _resMan(resMan), _system(system) { _decoderType = decoderType; _decoder = decoder; @@ -107,7 +107,6 @@ MoviePlayer::MoviePlayer(SwordEngine *vm, Text *textMan, ResMan *resMan, Audio:: } MoviePlayer::~MoviePlayer() { - delete _bgSoundHandle; delete _decoder; } @@ -116,16 +115,12 @@ MoviePlayer::~MoviePlayer() { * @param id the id of the file */ bool MoviePlayer::load(uint32 id) { - Common::File f; Common::String filename; - if (_decoderType == kVideoDecoderDXA) - _bgSoundStream = Audio::SeekableAudioStream::openStreamFile(sequenceList[id]); - else - _bgSoundStream = NULL; - if (SwordEngine::_systemVars.showText) { + Common::File f; filename = Common::String::format("%s.txt", sequenceList[id]); + if (f.open(filename)) { Common::String line; int lineNo = 0; @@ -169,7 +164,6 @@ bool MoviePlayer::load(uint32 id) { _movieTexts.push_back(MovieText(startFrame, endFrame, ptr, color)); lastEnd = endFrame; } - f.close(); } } @@ -189,6 +183,7 @@ bool MoviePlayer::load(uint32 id) { // Need to load here in case it fails in which case we'd need // to go back to paletted mode if (_decoder->loadFile(filename)) { + _decoder->start(); return true; } else { initGraphics(g_system->getWidth(), g_system->getHeight(), true); @@ -197,30 +192,27 @@ bool MoviePlayer::load(uint32 id) { break; } - return _decoder->loadFile(filename.c_str()); -} + if (!_decoder->loadFile(filename)) + return false; -void MoviePlayer::play() { - if (_bgSoundStream) - _snd->playStream(Audio::Mixer::kSFXSoundType, _bgSoundHandle, _bgSoundStream); + // For DXA, also add the external sound file + if (_decoderType == kVideoDecoderDXA) + _decoder->addStreamFileTrack(sequenceList[id]); - bool terminated = false; + _decoder->start(); + return true; +} +void MoviePlayer::play() { _textX = 0; _textY = 0; - terminated = !playVideo(); - - if (terminated) - _snd->stopHandle(*_bgSoundHandle); + playVideo(); _textMan->releaseText(2, false); _movieTexts.clear(); - while (_snd->isSoundHandleActive(*_bgSoundHandle)) - _system->delayMillis(100); - // It's tempting to call _screen->fullRefresh() here to restore the old // palette. However, that causes glitches with DXA movies, where the // previous location would be momentarily drawn, before switching to @@ -320,7 +312,7 @@ bool MoviePlayer::playVideo() { } if (_decoder->hasDirtyPalette()) { - _decoder->setSystemPalette(); + _vm->_system->getPaletteManager()->setPalette(_decoder->getPalette(), 0, 256); if (!_movieTexts.empty()) { // Look for the best color indexes to use to display the subtitles @@ -506,24 +498,12 @@ void MoviePlayer::drawFramePSX(const Graphics::Surface *frame) { scaledFrame.free(); } -DXADecoderWithSound::DXADecoderWithSound(Audio::Mixer *mixer, Audio::SoundHandle *bgSoundHandle) - : _mixer(mixer), _bgSoundHandle(bgSoundHandle) { -} - -uint32 DXADecoderWithSound::getTime() const { - if (_mixer->isSoundHandleActive(*_bgSoundHandle)) - return _mixer->getSoundElapsedTime(*_bgSoundHandle); - - return DXADecoder::getTime(); -} - /////////////////////////////////////////////////////////////////////////////// // Factory function for creating the appropriate cutscene player /////////////////////////////////////////////////////////////////////////////// -MoviePlayer *makeMoviePlayer(uint32 id, SwordEngine *vm, Text *textMan, ResMan *resMan, Audio::Mixer *snd, OSystem *system) { +MoviePlayer *makeMoviePlayer(uint32 id, SwordEngine *vm, Text *textMan, ResMan *resMan, OSystem *system) { Common::String filename; - Audio::SoundHandle *bgSoundHandle = new Audio::SoundHandle; // For the PSX version, we'll try the PlayStation stream files if (vm->isPsx()) { @@ -534,7 +514,7 @@ MoviePlayer *makeMoviePlayer(uint32 id, SwordEngine *vm, Text *textMan, ResMan * #ifdef USE_RGB_COLOR // All BS1 PSX videos run the videos at 2x speed Video::VideoDecoder *psxDecoder = new Video::PSXStreamDecoder(Video::PSXStreamDecoder::kCD2x); - return new MoviePlayer(vm, textMan, resMan, snd, system, bgSoundHandle, psxDecoder, kVideoDecoderPSX); + return new MoviePlayer(vm, textMan, resMan, system, psxDecoder, kVideoDecoderPSX); #else GUI::MessageDialog dialog(Common::String::format(_("PSX stream cutscene '%s' cannot be played in paletted mode"), filename.c_str()), _("OK")); dialog.runModal(); @@ -546,20 +526,20 @@ MoviePlayer *makeMoviePlayer(uint32 id, SwordEngine *vm, Text *textMan, ResMan * filename = Common::String::format("%s.smk", sequenceList[id]); if (Common::File::exists(filename)) { - Video::SmackerDecoder *smkDecoder = new Video::SmackerDecoder(snd); - return new MoviePlayer(vm, textMan, resMan, snd, system, bgSoundHandle, smkDecoder, kVideoDecoderSMK); + Video::SmackerDecoder *smkDecoder = new Video::SmackerDecoder(); + return new MoviePlayer(vm, textMan, resMan, system, smkDecoder, kVideoDecoderSMK); } filename = Common::String::format("%s.dxa", sequenceList[id]); if (Common::File::exists(filename)) { #ifdef USE_ZLIB - DXADecoderWithSound *dxaDecoder = new DXADecoderWithSound(snd, bgSoundHandle); - return new MoviePlayer(vm, textMan, resMan, snd, system, bgSoundHandle, dxaDecoder, kVideoDecoderDXA); + Video::VideoDecoder *dxaDecoder = new Video::DXADecoder(); + return new MoviePlayer(vm, textMan, resMan, system, dxaDecoder, kVideoDecoderDXA); #else GUI::MessageDialog dialog(_("DXA cutscenes found but ScummVM has been built without zlib support"), _("OK")); dialog.runModal(); - return NULL; + return 0; #endif } @@ -569,7 +549,7 @@ MoviePlayer *makeMoviePlayer(uint32 id, SwordEngine *vm, Text *textMan, ResMan * if (Common::File::exists(filename)) { GUI::MessageDialog dialog(_("MPEG2 cutscenes are no longer supported"), _("OK")); dialog.runModal(); - return NULL; + return 0; } if (!vm->isPsx() || scumm_stricmp(sequenceList[id], "enddemo") != 0) { @@ -578,7 +558,7 @@ MoviePlayer *makeMoviePlayer(uint32 id, SwordEngine *vm, Text *textMan, ResMan * dialog.runModal(); } - return NULL; + return 0; } } // End of namespace Sword1 diff --git a/engines/sword1/animation.h b/engines/sword1/animation.h index c2ed86a1a3..d0c61f5eb3 100644 --- a/engines/sword1/animation.h +++ b/engines/sword1/animation.h @@ -23,16 +23,19 @@ #ifndef SWORD1_ANIMATION_H #define SWORD1_ANIMATION_H -#include "video/dxa_decoder.h" -#include "video/video_decoder.h" - #include "common/list.h" -#include "audio/audiostream.h" - #include "sword1/screen.h" #include "sword1/sound.h" +namespace Graphics { +struct Surface; +} + +namespace Video { +class VideoDecoder; +} + namespace Sword1 { enum DecoderType { @@ -55,21 +58,9 @@ public: } }; -class DXADecoderWithSound : public Video::DXADecoder { -public: - DXADecoderWithSound(Audio::Mixer *mixer, Audio::SoundHandle *bgSoundHandle); - ~DXADecoderWithSound() {} - - uint32 getTime() const; - -private: - Audio::Mixer *_mixer; - Audio::SoundHandle *_bgSoundHandle; -}; - class MoviePlayer { public: - MoviePlayer(SwordEngine *vm, Text *textMan, ResMan *resMan, Audio::Mixer *snd, OSystem *system, Audio::SoundHandle *bgSoundHandle, Video::VideoDecoder *decoder, DecoderType decoderType); + MoviePlayer(SwordEngine *vm, Text *textMan, ResMan *resMan, OSystem *system, Video::VideoDecoder *decoder, DecoderType decoderType); virtual ~MoviePlayer(); bool load(uint32 id); void play(); @@ -78,7 +69,6 @@ protected: SwordEngine *_vm; Text *_textMan; ResMan *_resMan; - Audio::Mixer *_snd; OSystem *_system; Common::List<MovieText> _movieTexts; int _textX, _textY, _textWidth, _textHeight; @@ -88,8 +78,6 @@ protected: DecoderType _decoderType; Video::VideoDecoder *_decoder; - Audio::SoundHandle *_bgSoundHandle; - Audio::AudioStream *_bgSoundStream; bool playVideo(); void performPostProcessing(byte *screen); @@ -100,7 +88,7 @@ protected: void convertColor(byte r, byte g, byte b, float &h, float &s, float &v); }; -MoviePlayer *makeMoviePlayer(uint32 id, SwordEngine *vm, Text *textMan, ResMan *resMan, Audio::Mixer *snd, OSystem *system); +MoviePlayer *makeMoviePlayer(uint32 id, SwordEngine *vm, Text *textMan, ResMan *resMan, OSystem *system); } // End of namespace Sword1 diff --git a/engines/sword1/detection.cpp b/engines/sword1/detection.cpp index 087dcd09d8..5662e4672b 100644 --- a/engines/sword1/detection.cpp +++ b/engines/sword1/detection.cpp @@ -268,9 +268,6 @@ SaveStateDescriptor SwordMetaEngine::querySaveMetaInfos(const char *target, int SaveStateDescriptor desc(slot, name); - desc.setDeletableFlag(true); - desc.setWriteProtectedFlag(false); - if (versionSave < 2) // These older version of the savegames used a flag to signal presence of thumbnail in->skip(1); diff --git a/engines/sword1/logic.cpp b/engines/sword1/logic.cpp index 8e04861edf..757d768780 100644 --- a/engines/sword1/logic.cpp +++ b/engines/sword1/logic.cpp @@ -959,7 +959,7 @@ int Logic::fnPlaySequence(Object *cpt, int32 id, int32 sequenceId, int32 d, int3 // meantime, we don't want any looping sound effects still playing. _sound->quitScreen(); - MoviePlayer *player = makeMoviePlayer(sequenceId, _vm, _textMan, _resMan, _mixer, _system); + MoviePlayer *player = makeMoviePlayer(sequenceId, _vm, _textMan, _resMan, _system); if (player) { _screen->clearScreen(); if (player->load(sequenceId)) diff --git a/engines/sword1/objectman.cpp b/engines/sword1/objectman.cpp index d0803590a7..5d1864d58d 100644 --- a/engines/sword1/objectman.cpp +++ b/engines/sword1/objectman.cpp @@ -99,13 +99,64 @@ uint8 ObjectMan::fnCheckForTextLine(uint32 textId) { char *ObjectMan::lockText(uint32 textId) { uint8 lang = SwordEngine::_systemVars.language; + char *text = lockText(textId, lang); + if (text == 0) { + if (lang != BS1_ENGLISH) { + text = lockText(textId, BS1_ENGLISH); + if (text != 0) + warning("Missing translation for textId %u (\"%s\")", textId, text); + unlockText(textId, BS1_ENGLISH); + } + + return _missingSubTitleStr; + } + return text; +} + +char *ObjectMan::lockText(uint32 textId, uint8 lang) { char *addr = (char *)_resMan->openFetchRes(_textList[textId / ITM_PER_SEC][lang]); if (addr == 0) - return _missingSubTitleStr; + return NULL; addr += sizeof(Header); if ((textId & ITM_ID) >= _resMan->readUint32(addr)) { + // Workaround for missing sentences in some langages in the demo. + switch(textId) { + case 8455194: + return const_cast<char *>(_translationId8455194[lang]); + case 8455195: + return const_cast<char *>(_translationId8455195[lang]); + case 8455196: + return const_cast<char *>(_translationId8455196[lang]); + case 8455197: + return const_cast<char *>(_translationId8455197[lang]); + case 8455198: + return const_cast<char *>(_translationId8455198[lang]); + case 8455199: + return const_cast<char *>(_translationId8455199[lang]); + case 8455200: + return const_cast<char *>(_translationId8455200[lang]); + case 8455201: + return const_cast<char *>(_translationId8455201[lang]); + case 8455202: + return const_cast<char *>(_translationId8455202[lang]); + case 8455203: + return const_cast<char *>(_translationId8455203[lang]); + case 8455204: + return const_cast<char *>(_translationId8455204[lang]); + case 8455205: + return const_cast<char *>(_translationId8455205[lang]); + case 6488080: + return const_cast<char *>(_translationId6488080[lang]); + case 6488081: + return const_cast<char *>(_translationId6488081[lang]); + case 6488082: + return const_cast<char *>(_translationId6488082[lang]); + case 6488083: + return const_cast<char *>(_translationId6488083[lang]); + } + warning("ObjectMan::lockText(%d): only %d texts in file", textId & ITM_ID, _resMan->readUint32(addr)); - return _missingSubTitleStr; + return NULL; } uint32 offset = _resMan->readUint32(addr + ((textId & ITM_ID) + 1) * 4); if (offset == 0) { @@ -113,15 +164,19 @@ char *ObjectMan::lockText(uint32 textId) { // We use the hardcoded text in this case. if (textId == 2950145) return const_cast<char *>(_translationId2950145[lang]); - + warning("ObjectMan::lockText(%d): text number has no text lines", textId); - return _missingSubTitleStr; + return NULL; } return addr + offset; } void ObjectMan::unlockText(uint32 textId) { - _resMan->resClose(_textList[textId / ITM_PER_SEC][SwordEngine::_systemVars.language]); + unlockText(textId, SwordEngine::_systemVars.language); +} + +void ObjectMan::unlockText(uint32 textId, uint8 lang) { + _resMan->resClose(_textList[textId / ITM_PER_SEC][lang]); } uint32 ObjectMan::lastTextNumber(int section) { @@ -186,7 +241,193 @@ const char *const ObjectMan::_translationId2950145[7] = { "Eh?", // Italian "\277Eh?", // Spanish "Ano?", // Czech - " " // Portuguese + NULL // Portuguese +}; + +// The translations for the next texts are missing in the demo but are present +// in the full game. The translations were therefore extracted from the full game. + +// Missing translation for textId 8455194 (in the demo). +const char *const ObjectMan::_translationId8455194[7] = { + NULL, // "Who was the guy you were supposed to meet?", // English (not needed) + "Qui \351tait l'homme que vous deviez rencontrer?", // French + "Wer war der Typ, den Du treffen wolltest?", // German + "Chi dovevi incontrare?", // Italian + "\277Qui\351n era el hombre con el que ten\355as que encontrarte?", // Spanish + NULL, // Czech + NULL // Portuguese +}; + +// Missing translation for textId 8455195 (in the demo). +const char *const ObjectMan::_translationId8455195[7] = { + NULL, // "His name was Plantard. I didn't know him, but he called me last night.", // English (not needed) + "Son nom \351tait Plantard. Je ne le connaissais pas, mais il m'avait t\351l\351phon\351 la veille.", // French + "Sein Name war Plantard. Ich kannte ihn nicht, aber er hat mich letzte Nacht angerufen.", // German + "Si chiamava Plantard. Mi ha chiamato ieri sera, ma non lo conoscevo.", // Italian + "Su nombre era Plantard. Yo no lo conoc\355a pero \351l me llam\363 ayer por la noche.", // Spanish + NULL, // Czech + NULL // Portuguese +}; + +// Missing translation for textId 8455196 (in the demo). +const char *const ObjectMan::_translationId8455196[7] = { + NULL, // "He said he had a story which would interest me.", // English (not needed) + "Il a dit qu'il avait une histoire qui devrait m'int\351resser.", // French + "Er sagte, er h\344tte eine Story, die mich interessieren w\374rde.", // German + "Mi disse che aveva una storia che mi poteva interessare.", // Italian + "Dijo que ten\355a una historia que me interesar\355a.", // Spanish + NULL, // Czech + NULL // Portuguese +}; + +// Missing translation for textId 8455197 (in the demo). +const char *const ObjectMan::_translationId8455197[7] = { + NULL, // "He asked me to meet him at the caf\351.", // English (not needed) + "Il m'a demand\351 de le rencontrer au caf\351.", // French + "Er fragte mich, ob wir uns im Caf\351 treffen k\366nnten.", // German + "Mi chiese di incontrarci al bar.", // Italian + "Me pidi\363 que nos encontr\341ramos en el caf\351.", // Spanish + NULL, // Czech + NULL // Portuguese +}; + +// Missing translation for textId 8455198 (in the demo). +const char *const ObjectMan::_translationId8455198[7] = { + NULL, // "I guess I'll never know what he wanted to tell me...", // English (not needed) + "Je suppose que je ne saurai jamais ce qu'il voulait me dire...", // French + "Ich werde wohl nie erfahren, was er mir sagen wollte...", // German + "Penso che non sapr\362 mai che cosa voleva dirmi...", // Italian + "Supongo que nunca sabr\351 qu\351 me quer\355a contar...", // Spanish + NULL, // Czech + NULL // Portuguese +}; + +// Missing translation for textId 8455199 (in the demo). +const char *const ObjectMan::_translationId8455199[7] = { + NULL, // "Not unless you have Rosso's gift for psychic interrogation.", // English (not needed) + "Non, \340 moins d'avoir les dons de Rosso pour les interrogatoires psychiques.", // French + "Es sei denn, Du h\344ttest Rosso's Gabe der parapsychologischen Befragung.", // German + "A meno che tu non riesca a fare interrogatori telepatici come Rosso.", // Italian + "A no ser que tengas el don de Rosso para la interrogaci\363n ps\355quica.", // Spanish + NULL, // Czech + NULL // Portuguese +}; + +// Missing translation for textId 8455200 (in the demo). +const char *const ObjectMan::_translationId8455200[7] = { + NULL, // "How did Plantard get your name?", // English (not needed) + "Comment Plantard a-t-il obtenu votre nom?", // French + "Woher hat Plantard Deinen Namen?", // German + "Come ha fatto Plantard a sapere il tuo nome?", // Italian + "\277C\363mo consigui\363 Plantard tu nombre?", // Spanish + NULL, // Czech + NULL // Portuguese +}; + +// Missing translation for textId 8455201 (in the demo). +const char *const ObjectMan::_translationId8455201[7] = { + NULL, // "Through the newspaper - La Libert\351.", // English (not needed) + "Par mon journal... La Libert\351.", // French + "\334ber die Zeitung - La Libert\351.", // German + "Tramite il giornale La Libert\351.", // Italian + "Por el peri\363dico - La Libert\351.", // Spanish + NULL, // Czech + NULL // Portuguese +}; + +// Missing translation for textId 8455202 (in the demo). +const char *const ObjectMan::_translationId8455202[7] = { + NULL, // "I'd written an article linking two unsolved murders, one in Italy, the other in Japan.", // English (not needed) + "J'ai \351crit un article o\371 je faisais le lien entre deux meurtres inexpliqu\351s, en Italie et au japon.", // French + "Ich habe einen Artikel geschrieben, in dem ich zwei ungel\366ste Morde miteinander in Verbindung bringe, einen in Italien, einen anderen in Japan.", // German + "Ho scritto un articolo che metteva in collegamento due omicidi insoluti in Italia e Giappone.", // Italian + "Yo hab\355a escrito un art\355culo conectando dos asesinatos sin resolver, uno en Italia, el otro en Jap\363n.", // Spanish + NULL, // Czech + NULL // Portuguese +}; + +// Missing translation for textId 8455203 (in the demo). +const char *const ObjectMan::_translationId8455203[7] = { + NULL, // "The cases were remarkably similar...", // English (not needed) + "Les affaires \351taient quasiment identiques...", // French + "Die F\344lle sind sich bemerkenswert \344hnlich...", // German + "I casi erano sorprendentemente uguali...", // Italian + "Los casos eran incre\355blemente parecidos...", // Spanish + NULL, // Czech + NULL // Portuguese +}; + +// Missing translation for textId 8455204 (in the demo). +const char *const ObjectMan::_translationId8455204[7] = { + NULL, // "...a wealthy victim, no apparent motive, and a costumed killer.", // English (not needed) + "...une victime riche, pas de motif apparent, et un tueur d\351guis\351.", // French + "...ein wohlhabendes Opfer, kein offensichtliches Motiv, und ein verkleideter Killer.", // German + "...una vittima ricca, nessun motivo apparente e un assassino in costume.", // Italian + "...una v\355ctima rica, sin motivo aparente, y un asesino disfrazado.", // Spanish + NULL, // Czech + NULL // Portuguese +}; + +// Missing translation for textId 8455205 (in the demo). +const char *const ObjectMan::_translationId8455205[7] = { + NULL, // "Plantard said he could supply me with more information.", // English (not needed) + "Plantard m'a dit qu'il pourrait me fournir des renseignements.", // French + "Plantard sagte, er k\366nne mir weitere Informationen beschaffen.", // German + "Plantard mi disse che mi avrebbe fornito ulteriori informazioni.", // Italian + "Plantard dijo que \351l me pod\355a proporcionar m\341s informaci\363n.", // Spanish + NULL, // Czech + NULL // Portuguese +}; + +// Missing translation for textId 6488080 (in the demo). +const char *const ObjectMan::_translationId6488080[7] = { + NULL, // "I wasn't going to head off all over Paris until I'd investigated some more.", // English (not needed) + "Je ferais mieux d'enqu\351ter un peu par ici avant d'aller me promener ailleurs.", // French + "Ich durchquere nicht ganz Paris, bevor ich etwas mehr herausgefunden habe.", // German + "Non mi sarei incamminato per tutta Parigi prima di ulteriori indagini.", // Italian + "No iba a ponerme a recorrer Par\355s sin haber investigado un poco m\341s.", // Spanish + NULL, // Czech + NULL // Portuguese +}; + +// The next three sentences are specific to the demo and only the english text is present. +// The translations were provided by: +// French: Thierry Crozat +// German: Simon Sawatzki +// Italian: Matteo Angelino +// Spanish: Tomás Maidagan + +// Missing translation for textId 6488081 (in the demo). +const char *const ObjectMan::_translationId6488081[7] = { + NULL, // "I wasn't sure what I was going to do when I caught up with that clown...", // English (not needed) + "Je ne savais pas ce que je ferais quand je rattraperais le clown...", // French + "Ich wu\337te nicht, worauf ich mich einlie\337, als ich dem Clown nachjagte...", // German + "Non sapevo cosa avrei fatto una volta raggiunto quel clown...", // Italian + "No sab\355a muy bien qu\351 es lo que har\355a cuando alcanzara al payaso...", // Spanish + NULL, // Czech + NULL // Portuguese +}; + +// Missing translation for textId 6488082 (in the demo). +const char *const ObjectMan::_translationId6488082[7] = { + NULL, // "...but before I knew it, I was drawn into a desperate race between two ruthless enemies.", // English (not needed) + "...mais avant de m'en rendre compte je me retrouvais happ\351 dans une course effr\351n\351e entre deux ennemis impitoyables.", // French + "... doch bevor ich mich versah, war ich inmitten eines Wettlaufs von zwei r\374cksichtslosen Feinden.", // German + "... ma prima che me ne rendessi conto, fui trascinato in una corsa disperata con due spietati nemici.", // Italian + "...pero sin darme cuenta, acab\351 en medio de una desesperada carrera entre dos despiadados enemigos.", // Spanish + NULL, // Czech + NULL // Portuguese +}; + +// Missing translation for textId 6488083 (in the demo). +const char *const ObjectMan::_translationId6488083[7] = { + NULL, // "The goal: the mysterious power of the Broken Sword.", // English (not needed) + "Le but: les pouvoirs myst\351rieux de l'\351p\351e bris\351e.", // French + "Das Ziel: die geheimnisvolle Macht des zerbrochenen Schwertes.", // German + "Obiettivo: il misterioso potere della Spada spezzata.", // Italian + "El objetivo: el misterioso poder de la Espada Rota.", // Spanish + NULL, // Czech + NULL // Portuguese }; } // End of namespace Sword1 diff --git a/engines/sword1/objectman.h b/engines/sword1/objectman.h index ca3c7c1526..197b437c15 100644 --- a/engines/sword1/objectman.h +++ b/engines/sword1/objectman.h @@ -53,6 +53,9 @@ public: void saveLiveList(uint16 *dest); // for loading/saving void loadLiveList(uint16 *src); private: + char *lockText(uint32 textId, uint8 language); + void unlockText(uint32 textId, uint8 language); + ResMan *_resMan; static const uint32 _objectList[TOTAL_SECTIONS]; //a table of pointers to object files static const uint32 _textList[TOTAL_SECTIONS][7]; //a table of pointers to text files @@ -60,6 +63,22 @@ private: uint8 *_cptData[TOTAL_SECTIONS]; static char _missingSubTitleStr[]; static const char *const _translationId2950145[7]; //translation for textId 2950145 (missing from cluster file for some langages) + static const char *const _translationId8455194[7]; //translation for textId 8455194 (missing in the demo) + static const char *const _translationId8455195[7]; //translation for textId 8455195 (missing in the demo) + static const char *const _translationId8455196[7]; //translation for textId 8455196 (missing in the demo) + static const char *const _translationId8455197[7]; //translation for textId 8455197 (missing in the demo) + static const char *const _translationId8455198[7]; //translation for textId 8455198 (missing in the demo) + static const char *const _translationId8455199[7]; //translation for textId 8455199 (missing in the demo) + static const char *const _translationId8455200[7]; //translation for textId 8455200 (missing in the demo) + static const char *const _translationId8455201[7]; //translation for textId 8455201 (missing in the demo) + static const char *const _translationId8455202[7]; //translation for textId 8455202 (missing in the demo) + static const char *const _translationId8455203[7]; //translation for textId 8455203 (missing in the demo) + static const char *const _translationId8455204[7]; //translation for textId 8455204 (missing in the demo) + static const char *const _translationId8455205[7]; //translation for textId 8455205 (missing in the demo) + static const char *const _translationId6488080[7]; //translation for textId 6488081 (missing in the demo) + static const char *const _translationId6488081[7]; //translation for textId 6488081 (missing in the demo) + static const char *const _translationId6488082[7]; //translation for textId 6488082 (missing in the demo) + static const char *const _translationId6488083[7]; //translation for textId 6488083 (missing in the demo) }; } // End of namespace Sword1 diff --git a/engines/sword1/sound.cpp b/engines/sword1/sound.cpp index 3574074b00..61bf5257ab 100644 --- a/engines/sword1/sound.cpp +++ b/engines/sword1/sound.cpp @@ -142,7 +142,7 @@ void Sound::checkSpeechFileEndianness() { be_diff_sum += fabs((double)(be_value - prev_be_value)); prev_be_value = be_value; } - delete [] data; + delete[] data; } // Set the big endian flag _bigEndianSpeech = (be_diff_sum < le_diff_sum); diff --git a/engines/sword2/animation.cpp b/engines/sword2/animation.cpp index 5e3f8929e9..00260f789a 100644 --- a/engines/sword2/animation.cpp +++ b/engines/sword2/animation.cpp @@ -38,8 +38,11 @@ #include "sword2/screen.h" #include "sword2/animation.h" +#include "graphics/palette.h" + #include "gui/message.h" +#include "video/dxa_decoder.h" #include "video/smk_decoder.h" #include "video/psx_decoder.h" @@ -51,9 +54,8 @@ namespace Sword2 { // Basic movie player /////////////////////////////////////////////////////////////////////////////// -MoviePlayer::MoviePlayer(Sword2Engine *vm, Audio::Mixer *snd, OSystem *system, Audio::SoundHandle *bgSoundHandle, Video::VideoDecoder *decoder, DecoderType decoderType) - : _vm(vm), _snd(snd), _bgSoundHandle(bgSoundHandle), _system(system) { - _bgSoundStream = NULL; +MoviePlayer::MoviePlayer(Sword2Engine *vm, OSystem *system, Video::VideoDecoder *decoder, DecoderType decoderType) + : _vm(vm), _system(system) { _decoderType = decoderType; _decoder = decoder; @@ -62,7 +64,6 @@ MoviePlayer::MoviePlayer(Sword2Engine *vm, Audio::Mixer *snd, OSystem *system, A } MoviePlayer::~MoviePlayer() { - delete _bgSoundHandle; delete _decoder; } @@ -75,11 +76,6 @@ bool MoviePlayer::load(const char *name) { if (_vm->shouldQuit()) return false; - if (_decoderType == kVideoDecoderDXA) - _bgSoundStream = Audio::SeekableAudioStream::openStreamFile(name); - else - _bgSoundStream = NULL; - _textSurface = NULL; Common::String filename; @@ -99,6 +95,7 @@ bool MoviePlayer::load(const char *name) { // Need to load here in case it fails in which case we'd need // to go back to paletted mode if (_decoder->loadFile(filename)) { + _decoder->start(); return true; } else { initGraphics(640, 480, true); @@ -106,7 +103,15 @@ bool MoviePlayer::load(const char *name) { } } - return _decoder->loadFile(filename.c_str()); + if (!_decoder->loadFile(filename)) + return false; + + // For DXA, also add the external sound file + if (_decoderType == kVideoDecoderDXA) + _decoder->addStreamFileTrack(name); + + _decoder->start(); + return true; } void MoviePlayer::play(MovieText *movieTexts, uint32 numMovieTexts, uint32 leadIn, uint32 leadOut) { @@ -122,24 +127,15 @@ void MoviePlayer::play(MovieText *movieTexts, uint32 numMovieTexts, uint32 leadI if (leadIn) _vm->_sound->playMovieSound(leadIn, kLeadInSound); - if (_bgSoundStream) - _snd->playStream(Audio::Mixer::kSFXSoundType, _bgSoundHandle, _bgSoundStream); - - bool terminated = false; - - terminated = !playVideo(); + bool terminated = !playVideo(); closeTextObject(_currentMovieText, NULL, 0); if (terminated) { - _snd->stopHandle(*_bgSoundHandle); _vm->_sound->stopMovieSounds(); _vm->_sound->stopSpeech(); } - while (_snd->isSoundHandleActive(*_bgSoundHandle)) - _system->delayMillis(100); - if (_decoderType == kVideoDecoderPSX) { // Need to jump back to paletted color initGraphics(640, 480, true); @@ -336,7 +332,7 @@ bool MoviePlayer::playVideo() { } if (_decoder->hasDirtyPalette()) { - _decoder->setSystemPalette(); + _vm->_system->getPaletteManager()->setPalette(_decoder->getPalette(), 0, 256); uint32 maxWeight = 0; uint32 minWeight = 0xFFFFFFFF; @@ -406,31 +402,19 @@ void MoviePlayer::drawFramePSX(const Graphics::Surface *frame) { scaledFrame.free(); } -DXADecoderWithSound::DXADecoderWithSound(Audio::Mixer *mixer, Audio::SoundHandle *bgSoundHandle) - : _mixer(mixer), _bgSoundHandle(bgSoundHandle) { -} - -uint32 DXADecoderWithSound::getTime() const { - if (_mixer->isSoundHandleActive(*_bgSoundHandle)) - return _mixer->getSoundElapsedTime(*_bgSoundHandle); - - return DXADecoder::getTime(); -} - /////////////////////////////////////////////////////////////////////////////// // Factory function for creating the appropriate cutscene player /////////////////////////////////////////////////////////////////////////////// -MoviePlayer *makeMoviePlayer(const char *name, Sword2Engine *vm, Audio::Mixer *snd, OSystem *system, uint32 frameCount) { +MoviePlayer *makeMoviePlayer(const char *name, Sword2Engine *vm, OSystem *system, uint32 frameCount) { Common::String filename; - Audio::SoundHandle *bgSoundHandle = new Audio::SoundHandle; filename = Common::String::format("%s.str", name); if (Common::File::exists(filename)) { #ifdef USE_RGB_COLOR Video::VideoDecoder *psxDecoder = new Video::PSXStreamDecoder(Video::PSXStreamDecoder::kCD2x, frameCount); - return new MoviePlayer(vm, snd, system, bgSoundHandle, psxDecoder, kVideoDecoderPSX); + return new MoviePlayer(vm, system, psxDecoder, kVideoDecoderPSX); #else GUI::MessageDialog dialog(_("PSX cutscenes found but ScummVM has been built without RGB color support"), _("OK")); dialog.runModal(); @@ -441,16 +425,16 @@ MoviePlayer *makeMoviePlayer(const char *name, Sword2Engine *vm, Audio::Mixer *s filename = Common::String::format("%s.smk", name); if (Common::File::exists(filename)) { - Video::SmackerDecoder *smkDecoder = new Video::SmackerDecoder(snd); - return new MoviePlayer(vm, snd, system, bgSoundHandle, smkDecoder, kVideoDecoderSMK); + Video::SmackerDecoder *smkDecoder = new Video::SmackerDecoder(); + return new MoviePlayer(vm, system, smkDecoder, kVideoDecoderSMK); } filename = Common::String::format("%s.dxa", name); if (Common::File::exists(filename)) { #ifdef USE_ZLIB - DXADecoderWithSound *dxaDecoder = new DXADecoderWithSound(snd, bgSoundHandle); - return new MoviePlayer(vm, snd, system, bgSoundHandle, dxaDecoder, kVideoDecoderDXA); + Video::DXADecoder *dxaDecoder = new Video::DXADecoder(); + return new MoviePlayer(vm, system, dxaDecoder, kVideoDecoderDXA); #else GUI::MessageDialog dialog(_("DXA cutscenes found but ScummVM has been built without zlib support"), _("OK")); dialog.runModal(); diff --git a/engines/sword2/animation.h b/engines/sword2/animation.h index 3d5c42b7f7..b2a243b2ca 100644 --- a/engines/sword2/animation.h +++ b/engines/sword2/animation.h @@ -25,12 +25,16 @@ #ifndef SWORD2_ANIMATION_H #define SWORD2_ANIMATION_H -#include "video/dxa_decoder.h" -#include "video/video_decoder.h" -#include "audio/mixer.h" - #include "sword2/screen.h" +namespace Graphics { +struct Surface; +} + +namespace Video { +class VideoDecoder; +} + namespace Sword2 { enum DecoderType { @@ -55,20 +59,9 @@ struct MovieText { } }; -class DXADecoderWithSound : public Video::DXADecoder { -public: - DXADecoderWithSound(Audio::Mixer *mixer, Audio::SoundHandle *bgSoundHandle); - ~DXADecoderWithSound() {} - - uint32 getTime() const; -private: - Audio::Mixer *_mixer; - Audio::SoundHandle *_bgSoundHandle; -}; - class MoviePlayer { public: - MoviePlayer(Sword2Engine *vm, Audio::Mixer *snd, OSystem *system, Audio::SoundHandle *bgSoundHandle, Video::VideoDecoder *decoder, DecoderType decoderType); + MoviePlayer(Sword2Engine *vm, OSystem *system, Video::VideoDecoder *decoder, DecoderType decoderType); virtual ~MoviePlayer(); bool load(const char *name); @@ -76,7 +69,6 @@ public: protected: Sword2Engine *_vm; - Audio::Mixer *_snd; OSystem *_system; MovieText *_movieTexts; uint32 _numMovieTexts; @@ -87,8 +79,6 @@ protected: DecoderType _decoderType; Video::VideoDecoder *_decoder; - Audio::SoundHandle *_bgSoundHandle; - Audio::AudioStream *_bgSoundStream; uint32 _leadOut; int _leadOutFrame; @@ -105,7 +95,7 @@ protected: uint32 getWhiteColor(); }; -MoviePlayer *makeMoviePlayer(const char *name, Sword2Engine *vm, Audio::Mixer *snd, OSystem *system, uint32 frameCount); +MoviePlayer *makeMoviePlayer(const char *name, Sword2Engine *vm, OSystem *system, uint32 frameCount); } // End of namespace Sword2 diff --git a/engines/sword2/function.cpp b/engines/sword2/function.cpp index 836b252d6c..07fcaa094b 100644 --- a/engines/sword2/function.cpp +++ b/engines/sword2/function.cpp @@ -2139,7 +2139,7 @@ int32 Logic::fnPlaySequence(int32 *params) { uint32 frameCount = Sword2Engine::isPsx() ? params[1] : 0; - _moviePlayer = makeMoviePlayer(filename, _vm, _vm->_mixer, _vm->_system, frameCount); + _moviePlayer = makeMoviePlayer(filename, _vm, _vm->_system, frameCount); if (_moviePlayer && _moviePlayer->load(filename)) { _moviePlayer->play(_sequenceTextList, _sequenceTextLines, _smackerLeadIn, _smackerLeadOut); diff --git a/engines/sword25/fmv/movieplayer.cpp b/engines/sword25/fmv/movieplayer.cpp index 9ee13b4b6d..a95532ec65 100644 --- a/engines/sword25/fmv/movieplayer.cpp +++ b/engines/sword25/fmv/movieplayer.cpp @@ -61,6 +61,7 @@ bool MoviePlayer::loadMovie(const Common::String &filename, uint z) { // Get the file and load it into the decoder Common::SeekableReadStream *in = Kernel::getInstance()->getPackage()->getStream(filename); _decoder.loadStream(in); + _decoder.start(); GraphicEngine *pGfx = Kernel::getInstance()->getGfx(); diff --git a/engines/sword25/fmv/movieplayer.h b/engines/sword25/fmv/movieplayer.h index 1d256e56ba..2f5614b505 100644 --- a/engines/sword25/fmv/movieplayer.h +++ b/engines/sword25/fmv/movieplayer.h @@ -39,7 +39,7 @@ #include "sword25/gfx/bitmap.h" #ifdef USE_THEORADEC -#include "sword25/fmv/theora_decoder.h" +#include "video/theora_decoder.h" #endif #define THEORA_INDIRECT_RENDERING @@ -141,7 +141,7 @@ private: #ifdef USE_THEORADEC - TheoraDecoder _decoder; + Video::TheoraDecoder _decoder; Graphics::Surface *_backSurface; int _outX, _outY; diff --git a/engines/sword25/fmv/theora_decoder.cpp b/engines/sword25/fmv/theora_decoder.cpp deleted file mode 100644 index d38f5a26cf..0000000000 --- a/engines/sword25/fmv/theora_decoder.cpp +++ /dev/null @@ -1,565 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -/* - * Source is based on the player example from libvorbis package, - * available at: http://svn.xiph.org/trunk/theora/examples/player_example.c - * - * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. - * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS - * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE - * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. - * - * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 - * by the Xiph.Org Foundation and contributors http://www.xiph.org/ - * - */ - -#include "sword25/fmv/theora_decoder.h" - -#ifdef USE_THEORADEC -#include "common/system.h" -#include "common/textconsole.h" -#include "common/util.h" -#include "graphics/yuv_to_rgb.h" -#include "audio/decoders/raw.h" -#include "sword25/kernel/common.h" - -namespace Sword25 { - -#define AUDIOFD_FRAGSIZE 10240 - -static double rint(double v) { - return floor(v + 0.5); -} - -TheoraDecoder::TheoraDecoder(Audio::Mixer::SoundType soundType) { - _fileStream = 0; - - _theoraPacket = 0; - _vorbisPacket = 0; - _theoraDecode = 0; - _theoraSetup = 0; - _nextFrameStartTime = 0.0; - - _soundType = soundType; - _audStream = 0; - _audHandle = new Audio::SoundHandle(); - - ogg_sync_init(&_oggSync); - - _curFrame = -1; - _audiobuf = (ogg_int16_t *)malloc(AUDIOFD_FRAGSIZE * sizeof(ogg_int16_t)); - - reset(); -} - -TheoraDecoder::~TheoraDecoder() { - close(); - delete _fileStream; - delete _audHandle; - free(_audiobuf); -} - -void TheoraDecoder::queuePage(ogg_page *page) { - if (_theoraPacket) - ogg_stream_pagein(&_theoraOut, page); - - if (_vorbisPacket) - ogg_stream_pagein(&_vorbisOut, page); -} - -int TheoraDecoder::bufferData() { - char *buffer = ogg_sync_buffer(&_oggSync, 4096); - int bytes = _fileStream->read(buffer, 4096); - - ogg_sync_wrote(&_oggSync, bytes); - - return bytes; -} - -bool TheoraDecoder::loadStream(Common::SeekableReadStream *stream) { - close(); - - _endOfAudio = false; - _endOfVideo = false; - _fileStream = stream; - - // start up Ogg stream synchronization layer - ogg_sync_init(&_oggSync); - - // init supporting Vorbis structures needed in header parsing - vorbis_info_init(&_vorbisInfo); - vorbis_comment_init(&_vorbisComment); - - // init supporting Theora structures needed in header parsing - th_comment_init(&_theoraComment); - th_info_init(&_theoraInfo); - - // Ogg file open; parse the headers - // Only interested in Vorbis/Theora streams - bool foundHeader = false; - while (!foundHeader) { - int ret = bufferData(); - - if (ret == 0) - break; - - while (ogg_sync_pageout(&_oggSync, &_oggPage) > 0) { - ogg_stream_state test; - - // is this a mandated initial header? If not, stop parsing - if (!ogg_page_bos(&_oggPage)) { - // don't leak the page; get it into the appropriate stream - queuePage(&_oggPage); - foundHeader = true; - break; - } - - ogg_stream_init(&test, ogg_page_serialno(&_oggPage)); - ogg_stream_pagein(&test, &_oggPage); - ogg_stream_packetout(&test, &_oggPacket); - - // identify the codec: try theora - if (!_theoraPacket && th_decode_headerin(&_theoraInfo, &_theoraComment, &_theoraSetup, &_oggPacket) >= 0) { - // it is theora - memcpy(&_theoraOut, &test, sizeof(test)); - _theoraPacket = 1; - } else if (!_vorbisPacket && vorbis_synthesis_headerin(&_vorbisInfo, &_vorbisComment, &_oggPacket) >= 0) { - // it is vorbis - memcpy(&_vorbisOut, &test, sizeof(test)); - _vorbisPacket = 1; - } else { - // whatever it is, we don't care about it - ogg_stream_clear(&test); - } - } - // fall through to non-bos page parsing - } - - // we're expecting more header packets. - while ((_theoraPacket && _theoraPacket < 3) || (_vorbisPacket && _vorbisPacket < 3)) { - int ret; - - // look for further theora headers - while (_theoraPacket && (_theoraPacket < 3) && (ret = ogg_stream_packetout(&_theoraOut, &_oggPacket))) { - if (ret < 0) - error("Error parsing Theora stream headers; corrupt stream?"); - - if (!th_decode_headerin(&_theoraInfo, &_theoraComment, &_theoraSetup, &_oggPacket)) - error("Error parsing Theora stream headers; corrupt stream?"); - - _theoraPacket++; - } - - // look for more vorbis header packets - while (_vorbisPacket && (_vorbisPacket < 3) && (ret = ogg_stream_packetout(&_vorbisOut, &_oggPacket))) { - if (ret < 0) - error("Error parsing Vorbis stream headers; corrupt stream?"); - - if (vorbis_synthesis_headerin(&_vorbisInfo, &_vorbisComment, &_oggPacket)) - error("Error parsing Vorbis stream headers; corrupt stream?"); - - _vorbisPacket++; - - if (_vorbisPacket == 3) - break; - } - - // The header pages/packets will arrive before anything else we - // care about, or the stream is not obeying spec - - if (ogg_sync_pageout(&_oggSync, &_oggPage) > 0) { - queuePage(&_oggPage); // demux into the appropriate stream - } else { - ret = bufferData(); // someone needs more data - - if (ret == 0) - error("End of file while searching for codec headers."); - } - } - - // and now we have it all. initialize decoders - if (_theoraPacket) { - _theoraDecode = th_decode_alloc(&_theoraInfo, _theoraSetup); - debugN(1, "Ogg logical stream %lx is Theora %dx%d %.02f fps", - _theoraOut.serialno, _theoraInfo.pic_width, _theoraInfo.pic_height, - (double)_theoraInfo.fps_numerator / _theoraInfo.fps_denominator); - - switch (_theoraInfo.pixel_fmt) { - case TH_PF_420: - debug(1, " 4:2:0 video"); - break; - case TH_PF_422: - debug(1, " 4:2:2 video"); - break; - case TH_PF_444: - debug(1, " 4:4:4 video"); - break; - case TH_PF_RSVD: - default: - debug(1, " video\n (UNKNOWN Chroma sampling!)"); - break; - } - - if (_theoraInfo.pic_width != _theoraInfo.frame_width || _theoraInfo.pic_height != _theoraInfo.frame_height) - debug(1, " Frame content is %dx%d with offset (%d,%d).", - _theoraInfo.frame_width, _theoraInfo.frame_height, _theoraInfo.pic_x, _theoraInfo.pic_y); - - switch (_theoraInfo.colorspace){ - case TH_CS_UNSPECIFIED: - /* nothing to report */ - break; - case TH_CS_ITU_REC_470M: - debug(1, " encoder specified ITU Rec 470M (NTSC) color."); - break; - case TH_CS_ITU_REC_470BG: - debug(1, " encoder specified ITU Rec 470BG (PAL) color."); - break; - default: - debug(1, "warning: encoder specified unknown colorspace (%d).", _theoraInfo.colorspace); - break; - } - - debug(1, "Encoded by %s", _theoraComment.vendor); - if (_theoraComment.comments) { - debug(1, "theora comment header:"); - for (int i = 0; i < _theoraComment.comments; i++) { - if (_theoraComment.user_comments[i]) { - int len = _theoraComment.comment_lengths[i]; - char *value = (char *)malloc(len + 1); - if (value) { - memcpy(value, _theoraComment.user_comments[i], len); - value[len] = '\0'; - debug(1, "\t%s", value); - free(value); - } - } - } - } - - th_decode_ctl(_theoraDecode, TH_DECCTL_GET_PPLEVEL_MAX, &_ppLevelMax, sizeof(_ppLevelMax)); - _ppLevel = _ppLevelMax; - th_decode_ctl(_theoraDecode, TH_DECCTL_SET_PPLEVEL, &_ppLevel, sizeof(_ppLevel)); - _ppInc = 0; - } else { - // tear down the partial theora setup - th_info_clear(&_theoraInfo); - th_comment_clear(&_theoraComment); - } - - th_setup_free(_theoraSetup); - _theoraSetup = 0; - - if (_vorbisPacket) { - vorbis_synthesis_init(&_vorbisDSP, &_vorbisInfo); - vorbis_block_init(&_vorbisDSP, &_vorbisBlock); - debug(3, "Ogg logical stream %lx is Vorbis %d channel %ld Hz audio.", - _vorbisOut.serialno, _vorbisInfo.channels, _vorbisInfo.rate); - - _audStream = Audio::makeQueuingAudioStream(_vorbisInfo.rate, _vorbisInfo.channels); - - // Get enough audio data to start us off - while (_audStream->numQueuedStreams() == 0) { - // Queue more data - bufferData(); - while (ogg_sync_pageout(&_oggSync, &_oggPage) > 0) - queuePage(&_oggPage); - - queueAudio(); - } - - if (_audStream) - g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, _audHandle, _audStream, -1, getVolume(), getBalance()); - } else { - // tear down the partial vorbis setup - vorbis_info_clear(&_vorbisInfo); - vorbis_comment_clear(&_vorbisComment); - _endOfAudio = true; - } - - _surface.create(_theoraInfo.frame_width, _theoraInfo.frame_height, g_system->getScreenFormat()); - - // Set up a display surface - _displaySurface.pixels = _surface.getBasePtr(_theoraInfo.pic_x, _theoraInfo.pic_y); - _displaySurface.w = _theoraInfo.pic_width; - _displaySurface.h = _theoraInfo.pic_height; - _displaySurface.format = _surface.format; - _displaySurface.pitch = _surface.pitch; - - // Set the frame rate - _frameRate = Common::Rational(_theoraInfo.fps_numerator, _theoraInfo.fps_denominator); - - return true; -} - -void TheoraDecoder::close() { - if (_vorbisPacket) { - ogg_stream_clear(&_vorbisOut); - vorbis_block_clear(&_vorbisBlock); - vorbis_dsp_clear(&_vorbisDSP); - vorbis_comment_clear(&_vorbisComment); - vorbis_info_clear(&_vorbisInfo); - - g_system->getMixer()->stopHandle(*_audHandle); - - _audStream = 0; - _vorbisPacket = false; - } - if (_theoraPacket) { - ogg_stream_clear(&_theoraOut); - th_decode_free(_theoraDecode); - th_comment_clear(&_theoraComment); - th_info_clear(&_theoraInfo); - _theoraDecode = 0; - _theoraPacket = false; - } - - if (!_fileStream) - return; - - ogg_sync_clear(&_oggSync); - - delete _fileStream; - _fileStream = 0; - - _surface.free(); - _displaySurface.pixels = 0; - _displaySurface.free(); - - reset(); -} - -const Graphics::Surface *TheoraDecoder::decodeNextFrame() { - // First, let's get our frame - while (_theoraPacket) { - // theora is one in, one out... - if (ogg_stream_packetout(&_theoraOut, &_oggPacket) > 0) { - - if (_ppInc) { - _ppLevel += _ppInc; - th_decode_ctl(_theoraDecode, TH_DECCTL_SET_PPLEVEL, &_ppLevel, sizeof(_ppLevel)); - _ppInc = 0; - } - - if (th_decode_packetin(_theoraDecode, &_oggPacket, NULL) == 0) { - _curFrame++; - - // Convert YUV data to RGB data - th_ycbcr_buffer yuv; - th_decode_ycbcr_out(_theoraDecode, yuv); - translateYUVtoRGBA(yuv); - - if (_curFrame == 0) - _startTime = g_system->getMillis(); - - double time = th_granule_time(_theoraDecode, _oggPacket.granulepos); - - // We need to calculate when the next frame should be shown - // This is all in floating point because that's what the Ogg code gives us - // Ogg is a lossy container format, so it doesn't always list the time to the - // next frame. In such cases, we need to calculate it ourselves. - if (time == -1.0) - _nextFrameStartTime += _frameRate.getInverse().toDouble(); - else - _nextFrameStartTime = time; - - // break out - break; - } - } else { - // If we can't get any more frames, we're done. - if (_theoraOut.e_o_s || _fileStream->eos()) { - _endOfVideo = true; - break; - } - - // Queue more data - bufferData(); - while (ogg_sync_pageout(&_oggSync, &_oggPage) > 0) - queuePage(&_oggPage); - } - - // Update audio if we can - queueAudio(); - } - - // Force at least some audio to be buffered - // TODO: 5 is very arbitrary. We probably should do something like QuickTime does. - while (!_endOfAudio && _audStream->numQueuedStreams() < 5) { - bufferData(); - while (ogg_sync_pageout(&_oggSync, &_oggPage) > 0) - queuePage(&_oggPage); - - bool queuedAudio = queueAudio(); - if ((_vorbisOut.e_o_s || _fileStream->eos()) && !queuedAudio) { - _endOfAudio = true; - break; - } - } - - return &_displaySurface; -} - -bool TheoraDecoder::queueAudio() { - if (!_audStream) - return false; - - // An audio buffer should have been allocated (either in the constructor or after queuing the current buffer) - if (!_audiobuf) { - warning("[TheoraDecoder::queueAudio] Invalid audio buffer"); - return false; - } - - bool queuedAudio = false; - - for (;;) { - float **pcm; - - // if there's pending, decoded audio, grab it - int ret = vorbis_synthesis_pcmout(&_vorbisDSP, &pcm); - if (ret > 0) { - int count = _audiobufFill / 2; - int maxsamples = ((AUDIOFD_FRAGSIZE - _audiobufFill) / _vorbisInfo.channels) >> 1; - int i; - for (i = 0; i < ret && i < maxsamples; i++) - for (int j = 0; j < _vorbisInfo.channels; j++) { - int val = CLIP((int)rint(pcm[j][i] * 32767.f), -32768, 32767); - _audiobuf[count++] = val; - } - - vorbis_synthesis_read(&_vorbisDSP, i); - _audiobufFill += (i * _vorbisInfo.channels) << 1; - - if (_audiobufFill == AUDIOFD_FRAGSIZE) { - byte flags = Audio::FLAG_16BITS | Audio::FLAG_STEREO; -#ifdef SCUMM_LITTLE_ENDIAN - flags |= Audio::FLAG_LITTLE_ENDIAN; -#endif - _audStream->queueBuffer((byte *)_audiobuf, AUDIOFD_FRAGSIZE, DisposeAfterUse::NO, flags); - - // The audio mixer is now responsible for the old audio buffer. - // We need to create a new one. - _audiobuf = (ogg_int16_t *)malloc(AUDIOFD_FRAGSIZE * sizeof(ogg_int16_t)); - if (!_audiobuf) { - warning("[TheoraDecoder::queueAudio] Cannot allocate memory for audio buffer"); - return false; - } - - _audiobufFill = 0; - queuedAudio = true; - } - } else { - // no pending audio; is there a pending packet to decode? - if (ogg_stream_packetout(&_vorbisOut, &_oggPacket) > 0) { - if (vorbis_synthesis(&_vorbisBlock, &_oggPacket) == 0) // test for success! - vorbis_synthesis_blockin(&_vorbisDSP, &_vorbisBlock); - } else // we've buffered all we have, break out for now - return queuedAudio; - } - } - - // Unreachable - return false; -} - -void TheoraDecoder::reset() { - VideoDecoder::reset(); - - // FIXME: This does a rewind() instead of a reset()! - - if (_fileStream) - _fileStream->seek(0); - - _audiobufFill = 0; - _audiobufReady = false; - - _curFrame = -1; - - _theoraPacket = 0; - _vorbisPacket = 0; -} - -bool TheoraDecoder::endOfVideo() const { - return !isVideoLoaded() || (_endOfVideo && (!_audStream || (_audStream->endOfData() && _endOfAudio))); -} - -uint32 TheoraDecoder::getTimeToNextFrame() const { - if (endOfVideo() || _curFrame < 0) - return 0; - - uint32 elapsedTime = getTime(); - uint32 nextFrameStartTime = (uint32)(_nextFrameStartTime * 1000); - - if (nextFrameStartTime <= elapsedTime) - return 0; - - return nextFrameStartTime - elapsedTime; -} - -uint32 TheoraDecoder::getTime() const { - if (_audStream) - return g_system->getMixer()->getSoundElapsedTime(*_audHandle); - - return VideoDecoder::getTime(); -} - -void TheoraDecoder::pauseVideoIntern(bool pause) { - if (_audStream) - g_system->getMixer()->pauseHandle(*_audHandle, pause); -} - -enum TheoraYUVBuffers { - kBufferY = 0, - kBufferU = 1, - kBufferV = 2 -}; - -void TheoraDecoder::translateYUVtoRGBA(th_ycbcr_buffer &YUVBuffer) { - // Width and height of all buffers have to be divisible by 2. - assert((YUVBuffer[kBufferY].width & 1) == 0); - assert((YUVBuffer[kBufferY].height & 1) == 0); - assert((YUVBuffer[kBufferU].width & 1) == 0); - assert((YUVBuffer[kBufferV].width & 1) == 0); - - // UV images have to have a quarter of the Y image resolution - assert(YUVBuffer[kBufferU].width == YUVBuffer[kBufferY].width >> 1); - assert(YUVBuffer[kBufferV].width == YUVBuffer[kBufferY].width >> 1); - assert(YUVBuffer[kBufferU].height == YUVBuffer[kBufferY].height >> 1); - assert(YUVBuffer[kBufferV].height == YUVBuffer[kBufferY].height >> 1); - - Graphics::convertYUV420ToRGB(&_surface, YUVBuffer[kBufferY].data, YUVBuffer[kBufferU].data, YUVBuffer[kBufferV].data, YUVBuffer[kBufferY].width, YUVBuffer[kBufferY].height, YUVBuffer[kBufferY].stride, YUVBuffer[kBufferU].stride); -} - -void TheoraDecoder::updateVolume() { - if (g_system->getMixer()->isSoundHandleActive(*_audHandle)) - g_system->getMixer()->setChannelVolume(*_audHandle, getVolume()); -} - -void TheoraDecoder::updateBalance() { - if (g_system->getMixer()->isSoundHandleActive(*_audHandle)) - g_system->getMixer()->setChannelBalance(*_audHandle, getBalance()); -} - -} // End of namespace Sword25 - -#endif diff --git a/engines/sword25/fmv/theora_decoder.h b/engines/sword25/fmv/theora_decoder.h deleted file mode 100644 index 739040024f..0000000000 --- a/engines/sword25/fmv/theora_decoder.h +++ /dev/null @@ -1,144 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public 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 SWORD25_THEORADECODER_H -#define SWORD25_THEORADECODER_H - -#include "common/scummsys.h" // for USE_THEORADEC - -#ifdef USE_THEORADEC - -#include "common/rational.h" -#include "video/video_decoder.h" -#include "audio/audiostream.h" -#include "audio/mixer.h" -#include "graphics/pixelformat.h" -#include "graphics/surface.h" - -#include <theora/theoradec.h> -#include <vorbis/codec.h> - -namespace Common { -class SeekableReadStream; -} - -namespace Sword25 { - -/** - * - * Decoder for Theora videos. - * Video decoder used in engines: - * - sword25 - */ -class TheoraDecoder : public Video::VideoDecoder { -public: - TheoraDecoder(Audio::Mixer::SoundType soundType = Audio::Mixer::kMusicSoundType); - virtual ~TheoraDecoder(); - - /** - * Load a video file - * @param stream the stream to load - */ - bool loadStream(Common::SeekableReadStream *stream); - void close(); - void reset(); - - /** - * Decode the next frame and return the frame's surface - * @note the return surface should *not* be freed - * @note this may return 0, in which case the last frame should be kept on screen - */ - const Graphics::Surface *decodeNextFrame(); - - bool isVideoLoaded() const { return _fileStream != 0; } - uint16 getWidth() const { return _displaySurface.w; } - uint16 getHeight() const { return _displaySurface.h; } - - uint32 getFrameCount() const { - // It is not possible to get frame count easily - // I.e. seeking is required - assert(0); - return 0; - } - - Graphics::PixelFormat getPixelFormat() const { return _displaySurface.format; } - uint32 getTime() const; - uint32 getTimeToNextFrame() const; - - bool endOfVideo() const; - -protected: - // VideoDecoder API - void updateVolume(); - void updateBalance(); - void pauseVideoIntern(bool pause); - -private: - void queuePage(ogg_page *page); - bool queueAudio(); - int bufferData(); - void translateYUVtoRGBA(th_ycbcr_buffer &YUVBuffer); - - Common::SeekableReadStream *_fileStream; - Graphics::Surface _surface; - Graphics::Surface _displaySurface; - Common::Rational _frameRate; - double _nextFrameStartTime; - bool _endOfVideo; - bool _endOfAudio; - - Audio::Mixer::SoundType _soundType; - Audio::SoundHandle *_audHandle; - Audio::QueuingAudioStream *_audStream; - - ogg_sync_state _oggSync; - ogg_page _oggPage; - ogg_packet _oggPacket; - ogg_stream_state _vorbisOut; - ogg_stream_state _theoraOut; - th_info _theoraInfo; - th_comment _theoraComment; - th_dec_ctx *_theoraDecode; - th_setup_info *_theoraSetup; - vorbis_info _vorbisInfo; - vorbis_dsp_state _vorbisDSP; - vorbis_block _vorbisBlock; - vorbis_comment _vorbisComment; - - int _theoraPacket; - int _vorbisPacket; - - int _ppLevelMax; - int _ppLevel; - int _ppInc; - - // single audio fragment audio buffering - int _audiobufFill; - bool _audiobufReady; - ogg_int16_t *_audiobuf; -}; - -} // End of namespace Sword25 - -#endif - -#endif diff --git a/engines/sword25/module.mk b/engines/sword25/module.mk index 302120c500..e24a221244 100644 --- a/engines/sword25/module.mk +++ b/engines/sword25/module.mk @@ -85,11 +85,6 @@ MODULE_OBJS := \ util/pluto/pluto.o \ util/pluto/plzio.o -ifdef USE_THEORADEC -MODULE_OBJS += \ - fmv/theora_decoder.o -endif - # This module can be built as a plugin ifeq ($(ENABLE_SWORD25), DYNAMIC_PLUGIN) PLUGIN := 1 diff --git a/engines/sword25/sfx/soundengine.cpp b/engines/sword25/sfx/soundengine.cpp index 78b2db19eb..69fae3dc4e 100644 --- a/engines/sword25/sfx/soundengine.cpp +++ b/engines/sword25/sfx/soundengine.cpp @@ -239,16 +239,20 @@ void SoundEngine::setSoundVolume(uint handle, float volume) { debugC(1, kDebugSound, "SoundEngine::setSoundVolume(%d, %f)", handle, volume); SndHandle* sndHandle = findHandle(handle); - if (sndHandle != NULL) + if (sndHandle != NULL) { + sndHandle->volume = volume; _mixer->setChannelVolume(sndHandle->handle, (byte)(volume * 255)); + } } void SoundEngine::setSoundPanning(uint handle, float pan) { debugC(1, kDebugSound, "SoundEngine::setSoundPanning(%d, %f)", handle, pan); SndHandle* sndHandle = findHandle(handle); - if (sndHandle != NULL) + if (sndHandle != NULL) { + sndHandle->pan = pan; _mixer->setChannelBalance(sndHandle->handle, (int8)(pan * 127)); + } } void SoundEngine::pauseSound(uint handle) { @@ -324,13 +328,16 @@ bool SoundEngine::canLoadResource(const Common::String &fileName) { return fname.hasSuffix(".ogg"); } - - bool SoundEngine::persist(OutputPersistenceBlock &writer) { +bool SoundEngine::persist(OutputPersistenceBlock &writer) { writer.write(_maxHandleId); for (uint i = 0; i < SOUND_HANDLES; i++) { writer.write(_handles[i].id); + // Don't restart sounds which already finished playing. + if (_handles[i].type != kFreeHandle && !_mixer->isSoundHandleActive(_handles[i].handle)) + _handles[i].type = kFreeHandle; + writer.writeString(_handles[i].fileName); writer.write((int)_handles[i].sndType); writer.write(_handles[i].volume); @@ -374,7 +381,8 @@ bool SoundEngine::unpersist(InputPersistenceBlock &reader) { reader.read(layer); if (reader.isGood()) { - playSoundEx(fileName, (SOUND_TYPES)sndType, volume, pan, loop, loopStart, loopEnd, layer, i); + if (sndType != kFreeHandle) + playSoundEx(fileName, (SOUND_TYPES)sndType, volume, pan, loop, loopStart, loopEnd, layer, i); } else return false; } diff --git a/engines/sword25/util/pluto/pluto.cpp b/engines/sword25/util/pluto/pluto.cpp index d645e5ed2a..b7f5e30340 100644 --- a/engines/sword25/util/pluto/pluto.cpp +++ b/engines/sword25/util/pluto/pluto.cpp @@ -895,10 +895,10 @@ static void unpersistnumber(UnpersistInfo *upi) static void unpersiststring(UnpersistInfo *upi) { /* perms reftbl sptbl ref */ - int length; + size_t length; char* string; lua_checkstack(upi->L, 1); - verify(LIF(Z,read)(&upi->zio, &length, sizeof(int)) == 0); + verify(LIF(Z,read)(&upi->zio, &length, sizeof(size_t)) == 0); string = pdep_newvector(upi->L, length, char); verify(LIF(Z,read)(&upi->zio, string, length) == 0); lua_pushlstring(upi->L, string, length); diff --git a/engines/teenagent/callbacks.cpp b/engines/teenagent/callbacks.cpp index 8882531d27..934727a478 100644 --- a/engines/teenagent/callbacks.cpp +++ b/engines/teenagent/callbacks.cpp @@ -2252,7 +2252,7 @@ bool TeenAgentEngine::processCallback(uint16 addr) { return true; case 0x78e0: - processCallback(0x50c5); + processCallback(0x505c); return false; case 0x78e7: @@ -2265,7 +2265,7 @@ bool TeenAgentEngine::processCallback(uint16 addr) { case 0x78f5: if (CHECK_FLAG(0xDB95, 1)) { - displayMessage(0x3575); + displayMessage(0x3E75); return true; } else return false; @@ -3925,7 +3925,7 @@ bool TeenAgentEngine::processCallback(uint16 addr) { displayMessage(0x39ae); break; default: - displayMessage(0x39b7); + displayMessage(0x3ab7); } return true; diff --git a/engines/teenagent/detection.cpp b/engines/teenagent/detection.cpp index dad876dd97..2de6f49c44 100644 --- a/engines/teenagent/detection.cpp +++ b/engines/teenagent/detection.cpp @@ -80,7 +80,7 @@ static const ADGameDescription teenAgentGameDescriptions[] = { }; enum { - MAX_SAVES = 20 + MAX_SAVES = 20 }; class TeenAgentMetaEngine : public AdvancedMetaEngine { @@ -123,16 +123,15 @@ public: virtual SaveStateList listSaves(const char *target) const { Common::String pattern = target; - pattern += ".*"; + pattern += ".??"; Common::StringArray filenames = g_system->getSavefileManager()->listSavefiles(pattern); Common::sort(filenames.begin(), filenames.end()); SaveStateList saveList; for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) { - int slot; - const char *ext = strrchr(file->c_str(), '.'); - if (ext && (slot = atoi(ext + 1)) >= 0 && slot < MAX_SAVES) { + int slot = atoi(file->c_str() + file->size() - 2); + if (slot >= 0 && slot < MAX_SAVES) { Common::ScopedPtr<Common::InSaveFile> in(g_system->getSavefileManager()->openForLoading(*file)); if (!in) continue; @@ -174,7 +173,6 @@ public: return SaveStateDescriptor(slot, desc); SaveStateDescriptor ssd(slot, desc); - ssd.setDeletableFlag(true); //checking for the thumbnail if (Graphics::Surface *const thumb = Graphics::loadThumbnail(*in)) diff --git a/engines/teenagent/resources.cpp b/engines/teenagent/resources.cpp index 597ca670c0..dff58f98e2 100644 --- a/engines/teenagent/resources.cpp +++ b/engines/teenagent/resources.cpp @@ -22,6 +22,7 @@ #include "teenagent/resources.h" #include "teenagent/teenagent.h" #include "common/textconsole.h" +#include "common/translation.h" #include "common/zlib.h" namespace TeenAgent { @@ -64,28 +65,47 @@ bool Resources::loadArchives(const ADGameDescription *gd) { Common::File *dat_file = new Common::File(); if (!dat_file->open("teenagent.dat")) { delete dat_file; - Common::String errorMessage = "You're missing the 'teenagent.dat' file. Get it from the ScummVM website"; - GUIErrorMessage(errorMessage); + Common::String errorMessage = _("You're missing the 'teenagent.dat' file. Get it from the ScummVM website"); warning("%s", errorMessage.c_str()); + GUIErrorMessage(errorMessage); return false; } + + // teenagent.dat used to be compressed with zlib compression. The usage of + // zlib here is no longer needed, and it's maintained only for backwards + // compatibility. Common::SeekableReadStream *dat = Common::wrapCompressedReadStream(dat_file); + +#if !defined(USE_ZLIB) + uint16 header = dat->readUint16BE(); + bool isCompressed = (header == 0x1F8B || + ((header & 0x0F00) == 0x0800 && + header % 31 == 0)); + dat->seek(-2, SEEK_CUR); + + if (isCompressed) { + // teenagent.dat is compressed, but zlib hasn't been compiled in + delete dat; + Common::String errorMessage = _("The teenagent.dat file is compressed and zlib hasn't been included in this executable. Please decompress it"); + warning("%s", errorMessage.c_str()); + GUIErrorMessage(errorMessage); + return false; + } +#endif + cseg.read(dat, 0xb3b0); dseg.read(dat, 0xe790); eseg.read(dat, 0x8be2); - delete dat; - { - FilePack varia; - varia.open("varia.res"); - font7.load(varia, 7); - font7.width_pack = 1; - font7.height = 11; - font8.load(varia, 8); - font8.height = 31; - varia.close(); - } + FilePack varia; + varia.open("varia.res"); + font7.load(varia, 7); + font7.width_pack = 1; + font7.height = 11; + font8.load(varia, 8); + font8.height = 31; + varia.close(); off.open("off.res"); on.open("on.res"); diff --git a/engines/teenagent/teenagent.cpp b/engines/teenagent/teenagent.cpp index f06de6f803..57c069fe59 100644 --- a/engines/teenagent/teenagent.cpp +++ b/engines/teenagent/teenagent.cpp @@ -535,9 +535,8 @@ Common::Error TeenAgentEngine::run() { syncSoundSettings(); - _mixer->playStream(Audio::Mixer::kMusicSoundType, &_musicHandle, music, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, false); setMusic(1); - music->start(); + _mixer->playStream(Audio::Mixer::kMusicSoundType, &_musicHandle, music, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, false); int load_slot = Common::ConfigManager::instance().getInt("save_slot"); if (load_slot >= 0) { diff --git a/engines/testbed/graphics.cpp b/engines/testbed/graphics.cpp index 082694b728..590e6c6d81 100644 --- a/engines/testbed/graphics.cpp +++ b/engines/testbed/graphics.cpp @@ -1028,7 +1028,7 @@ TestExitStatus GFXtests::paletteRotation() { GFXTestSuite::setCustomColor(255, 0, 0); Testsuite::clearScreen(); - if(Testsuite::handleInteractiveInput("Did you see a rotation in colors of rectangles displayed on screen?", "Yes", "No", kOptionRight)) { + if (Testsuite::handleInteractiveInput("Did you see a rotation in colors of rectangles displayed on screen?", "Yes", "No", kOptionRight)) { return kTestFailed; } @@ -1121,7 +1121,7 @@ TestExitStatus GFXtests::pixelFormats() { g_system->updateScreen(); g_system->delayMillis(500); - if(Testsuite::handleInteractiveInput("Were you able to notice the colored rectangles on the screen for this format?", "Yes", "No", kOptionLeft)) { + if (Testsuite::handleInteractiveInput("Were you able to notice the colored rectangles on the screen for this format?", "Yes", "No", kOptionLeft)) { numPassed++; } else { numFailed++; diff --git a/engines/testbed/testsuite.cpp b/engines/testbed/testsuite.cpp index 655179aa74..39eeca31bd 100644 --- a/engines/testbed/testsuite.cpp +++ b/engines/testbed/testsuite.cpp @@ -289,7 +289,7 @@ void Testsuite::execute() { continue; } - if((*i)->isInteractive && !ConfParams.isSessionInteractive()) { + if ((*i)->isInteractive && !ConfParams.isSessionInteractive()) { logPrintf("Info! Skipping Test: %s, non-interactive environment is selected\n", ((*i)->featureName).c_str()); _numTestsSkipped++; continue; diff --git a/engines/tinsel/detection.cpp b/engines/tinsel/detection.cpp index 0f662e22bd..2e4be33e53 100644 --- a/engines/tinsel/detection.cpp +++ b/engines/tinsel/detection.cpp @@ -63,6 +63,14 @@ uint16 TinselEngine::getVersion() const { return _gameDescription->version; } +bool TinselEngine::getIsADGFDemo() const { + return (bool)(_gameDescription->desc.flags & ADGF_DEMO); +} + +bool TinselEngine::isCD() const { + return (bool)(_gameDescription->desc.flags & ADGF_CD); +} + } // End of namespace Tinsel static const PlainGameDescriptor tinselGames[] = { diff --git a/engines/tinsel/detection_tables.h b/engines/tinsel/detection_tables.h index b6b19f6ee7..631c2dce14 100644 --- a/engines/tinsel/detection_tables.h +++ b/engines/tinsel/detection_tables.h @@ -47,7 +47,7 @@ static const TinselGameDescription gameDescriptions[] = { }, GID_DW1, 0, - GF_DEMO, + 0, TINSEL_V0, }, @@ -61,12 +61,12 @@ static const TinselGameDescription gameDescriptions[] = { }, Common::EN_ANY, Common::kPlatformPC, - ADGF_DEMO, + ADGF_DEMO | ADGF_CD, GUIO0() }, GID_DW1, 0, - GF_CD, + 0, TINSEL_V1, }, #if 0 @@ -81,12 +81,12 @@ static const TinselGameDescription gameDescriptions[] = { }, Common::EN_ANY, Common::kPlatformMacintosh, - ADGF_DEMO, + ADGF_DEMO | ADGF_CD, GUIO0() }, GID_DW1, 0, - GF_CD | GF_SCNFILES | GF_BIG_ENDIAN, + GF_SCNFILES, TINSEL_V1, }, #endif @@ -110,7 +110,7 @@ static const TinselGameDescription gameDescriptions[] = { }, GID_DW1, 0, - GF_FLOPPY | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT, + GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT, TINSEL_V1, }, @@ -133,7 +133,7 @@ static const TinselGameDescription gameDescriptions[] = { }, GID_DW1, 0, - GF_FLOPPY | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT, + GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT, TINSEL_V1, }, @@ -156,7 +156,7 @@ static const TinselGameDescription gameDescriptions[] = { }, GID_DW1, 0, - GF_FLOPPY | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT, + GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT, TINSEL_V1, }, @@ -179,7 +179,7 @@ static const TinselGameDescription gameDescriptions[] = { }, GID_DW1, 0, - GF_FLOPPY | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT, + GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT, TINSEL_V1, }, @@ -195,7 +195,7 @@ static const TinselGameDescription gameDescriptions[] = { }, GID_DW1, 0, - GF_FLOPPY | GF_ENHANCED_AUDIO_SUPPORT, + GF_ENHANCED_AUDIO_SUPPORT, TINSEL_V1, }, @@ -214,7 +214,42 @@ static const TinselGameDescription gameDescriptions[] = { }, GID_DW1, 0, - GF_CD | GF_ENHANCED_AUDIO_SUPPORT, + GF_ENHANCED_AUDIO_SUPPORT, + TINSEL_V1, + }, + + { // Polish fan translation CD V1 version, with *.gra files (same as the floppy one, with english.smp) + { + "dw", + "CD", + { + {"dw.gra", 0, "ef05bbd2a754bd11a2e87bcd84ab5ccf", 781864}, + {"english.smp", 0, NULL, -1}, + }, + Common::EN_ANY, + Common::kPlatformPC, + ADGF_CD, + GUIO_NONE + }, + GID_DW1, + 0, + GF_ENHANCED_AUDIO_SUPPORT, + TINSEL_V1, + }, + + { // Polish fan translaction floppy V1 version, with *.gra files + { + "dw", + "Floppy", + AD_ENTRY1s("dw.gra", "ef05bbd2a754bd11a2e87bcd84ab5ccf", 781864), + Common::EN_ANY, + Common::kPlatformPC, + ADGF_NO_FLAGS, + GUIO_NOSPEECH + }, + GID_DW1, + 0, + GF_ENHANCED_AUDIO_SUPPORT, TINSEL_V1, }, @@ -236,7 +271,7 @@ static const TinselGameDescription gameDescriptions[] = { }, GID_DW1, 0, - GF_CD | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT, + GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT, TINSEL_V1, }, @@ -261,7 +296,7 @@ static const TinselGameDescription gameDescriptions[] = { }, GID_DW1, 0, - GF_CD | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT, + GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT, TINSEL_V1, }, @@ -280,12 +315,12 @@ static const TinselGameDescription gameDescriptions[] = { }, Common::DE_DEU, Common::kPlatformPC, - ADGF_DROPLANGUAGE | ADGF_CD, + ADGF_DROPLANGUAGE, GUIO0() }, GID_DW1, 0, - GF_CD | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT, + GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT, TINSEL_V1, }, { @@ -308,7 +343,7 @@ static const TinselGameDescription gameDescriptions[] = { }, GID_DW1, 0, - GF_CD | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT, + GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT, TINSEL_V1, }, { @@ -331,7 +366,7 @@ static const TinselGameDescription gameDescriptions[] = { }, GID_DW1, 0, - GF_CD | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT, + GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT, TINSEL_V1, }, @@ -351,7 +386,7 @@ static const TinselGameDescription gameDescriptions[] = { }, GID_DW1, 0, - GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT, + GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT, TINSEL_V1, }, @@ -371,7 +406,7 @@ static const TinselGameDescription gameDescriptions[] = { }, GID_DW1, 0, - GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT, + GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT, TINSEL_V1, }, @@ -390,7 +425,7 @@ static const TinselGameDescription gameDescriptions[] = { }, GID_DW1, 0, - GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT, + GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT, TINSEL_V1, }, @@ -408,12 +443,12 @@ static const TinselGameDescription gameDescriptions[] = { }, Common::EN_ANY, Common::kPlatformPSX, - ADGF_DEMO, + ADGF_CD | ADGF_DEMO, GUIO0() }, GID_DW1, 0, - GF_CD | GF_SCNFILES, + GF_SCNFILES, TINSEL_V1, }, @@ -434,7 +469,7 @@ static const TinselGameDescription gameDescriptions[] = { }, GID_DW1, 0, - GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT, + GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT, TINSEL_V1, }, #endif @@ -456,7 +491,7 @@ static const TinselGameDescription gameDescriptions[] = { }, GID_DW1, 0, - GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT | GF_BIG_ENDIAN, + GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT, TINSEL_V1, }, @@ -475,7 +510,7 @@ static const TinselGameDescription gameDescriptions[] = { }, GID_DW1, 0, - GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT | GF_ALT_MIDI, + GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT | GF_ALT_MIDI, TINSEL_V1, }, @@ -496,11 +531,32 @@ static const TinselGameDescription gameDescriptions[] = { }, GID_DW1, 0, - GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT, + GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT, + TINSEL_V1, + }, + + { // Polish fan translaction Discworld 1 + { + "dw", + "CD", + { + {"dw.scn", 0, "fa169d2c98660215ebd84b49c1899eef", 776396}, + {"english.txt", 0, "c1a53eb7ec812689dab70e2bb22cf2ab", 224151}, + {"english.smp", 0, NULL, -1}, + {NULL, 0, NULL, 0} + }, + Common::PL_POL, + Common::kPlatformPC, + ADGF_CD, + GUIO_NONE + }, + GID_DW1, + 0, + GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT, TINSEL_V1, }, - { // English DW2 demo + { // English Discworld 2 demo { "dw2", "Demo", @@ -511,12 +567,12 @@ static const TinselGameDescription gameDescriptions[] = { }, Common::EN_ANY, Common::kPlatformPC, - ADGF_DEMO, + ADGF_DEMO | ADGF_CD, GUIO1(GUIO_NOASPECT) }, GID_DW2, 0, - GF_CD | GF_SCNFILES | GF_DEMO, + GF_SCNFILES, TINSEL_V2, }, @@ -531,12 +587,12 @@ static const TinselGameDescription gameDescriptions[] = { }, Common::EN_GRB, Common::kPlatformPC, - ADGF_NO_FLAGS, + ADGF_CD, GUIO1(GUIO_NOASPECT) }, GID_DW2, 0, - GF_CD | GF_SCNFILES, + GF_SCNFILES, TINSEL_V2, }, @@ -551,12 +607,12 @@ static const TinselGameDescription gameDescriptions[] = { }, Common::EN_USA, Common::kPlatformPC, - ADGF_NO_FLAGS, + ADGF_CD, GUIO1(GUIO_NOASPECT) }, GID_DW2, 0, - GF_CD | GF_SCNFILES, + GF_SCNFILES, TINSEL_V2, }, @@ -571,12 +627,12 @@ static const TinselGameDescription gameDescriptions[] = { }, Common::FR_FRA, Common::kPlatformPC, - ADGF_NO_FLAGS, + ADGF_CD, GUIO1(GUIO_NOASPECT) }, GID_DW2, 0, - GF_CD | GF_SCNFILES, + GF_SCNFILES, TINSEL_V2, }, @@ -591,12 +647,12 @@ static const TinselGameDescription gameDescriptions[] = { }, Common::DE_DEU, Common::kPlatformPC, - ADGF_NO_FLAGS, + ADGF_CD, GUIO1(GUIO_NOASPECT) }, GID_DW2, 0, - GF_CD | GF_SCNFILES, + GF_SCNFILES, TINSEL_V2, }, @@ -612,12 +668,12 @@ static const TinselGameDescription gameDescriptions[] = { }, Common::IT_ITA, Common::kPlatformPC, - ADGF_NO_FLAGS, + ADGF_CD, GUIO1(GUIO_NOASPECT) }, GID_DW2, 0, - GF_CD | GF_SCNFILES, + GF_SCNFILES, TINSEL_V2, }, { @@ -632,12 +688,12 @@ static const TinselGameDescription gameDescriptions[] = { }, Common::ES_ESP, Common::kPlatformPC, - ADGF_NO_FLAGS, + ADGF_CD, GUIO1(GUIO_NOASPECT) }, GID_DW2, 0, - GF_CD | GF_SCNFILES, + GF_SCNFILES, TINSEL_V2, }, @@ -653,12 +709,12 @@ static const TinselGameDescription gameDescriptions[] = { }, Common::RU_RUS, Common::kPlatformPC, - ADGF_NO_FLAGS, + ADGF_CD, GUIO1(GUIO_NOASPECT) }, GID_DW2, 0, - GF_CD | GF_SCNFILES, + GF_SCNFILES, TINSEL_V2, }, diff --git a/engines/tinsel/dialogs.cpp b/engines/tinsel/dialogs.cpp index fbe9e8d1f6..56ee2ea752 100644 --- a/engines/tinsel/dialogs.cpp +++ b/engines/tinsel/dialogs.cpp @@ -753,6 +753,11 @@ static CONFBOX t1RestartBox[] = { #endif }; +static CONFBOX t1RestartBoxPSX[] = { + { AAGBUT, INITGAME, TM_NONE, NULL, USE_POINTER, 122, 48, 23, 19, NULL, IX1_TICK1 }, + { AAGBUT, CLOSEWIN, TM_NONE, NULL, USE_POINTER, 82, 48, 23, 19, NULL, IX1_CROSS1 } +}; + static CONFBOX t2RestartBox[] = { { AAGBUT, INITGAME, TM_NONE, NULL, 0, 140, 78, BW, BH, NULL, IX2_TICK1 }, { AAGBUT, CLOSEWIN, TM_NONE, NULL, 0, 60, 78, BW, BH, NULL, IX2_CROSS1 } @@ -763,10 +768,10 @@ static CONFINIT t1ciRestart = { 6, 2, 72, 53, false, t1RestartBox, ARRAYSIZE(t1R #else static CONFINIT t1ciRestart = { 4, 2, 98, 53, false, t1RestartBox, ARRAYSIZE(t1RestartBox), SIX_RESTART_HEADING }; #endif +static CONFINIT t1ciRestartPSX = { 8, 2, 46, 53, false, t1RestartBoxPSX, ARRAYSIZE(t1RestartBoxPSX), SIX_RESTART_HEADING }; static CONFINIT t2ciRestart = { 4, 2, 196, 53, false, t2RestartBox, sizeof(t2RestartBox)/sizeof(CONFBOX), SS_RESTART_HEADING }; -#define ciRestart (TinselV2 ? t2ciRestart : t1ciRestart) -#define restartBox (TinselV2 ? t2RestartBox : t1RestartBox) +#define ciRestart (TinselV2 ? t2ciRestart : (TinselV1PSX ? t1ciRestartPSX : t1ciRestart)) /*-------------------------------------------------------------*\ | This is the sound control 'menu'. In Discworld 2, it also | @@ -1038,18 +1043,20 @@ static bool RePosition(); static bool LanguageChange() { LANGUAGE nLang = _vm->_config->_language; - if (_vm->getFeatures() & GF_USE_3FLAGS) { - // VERY quick dodgy bodge - if (cd.selBox == 0) - nLang = TXT_FRENCH; // = 1 - else if (cd.selBox == 1) - nLang = TXT_GERMAN; // = 2 - else - nLang = TXT_SPANISH; // = 4 - } else if (_vm->getFeatures() & GF_USE_4FLAGS) { - nLang = (LANGUAGE)(cd.selBox + 1); - } else if (_vm->getFeatures() & GF_USE_5FLAGS) { - nLang = (LANGUAGE)cd.selBox; + if ((_vm->getFeatures() & GF_USE_3FLAGS) || (_vm->getFeatures() & GF_USE_4FLAGS) || (_vm->getFeatures() & GF_USE_5FLAGS)) { + // Languages: TXT_ENGLISH, TXT_FRENCH, TXT_GERMAN, TXT_ITALIAN, TXT_SPANISH + // 5 flag versions include English + int selected = (_vm->getFeatures() & GF_USE_5FLAGS) ? cd.selBox : cd.selBox + 1; + // Make sure that a language flag has been selected. If the user has + // changed the language speed slider and hasn't clicked on a flag, it + // won't be selected. + if (selected >= 0 && selected <= 4) { + nLang = (LANGUAGE)selected; + + // 3 flag versions don't include Italian + if (selected >= 3 && (_vm->getFeatures() & GF_USE_3FLAGS)) + nLang = TXT_SPANISH; + } } if (nLang != _vm->_config->_language) { @@ -1256,6 +1263,20 @@ static INV_OBJECT *GetInvObject(int id) { } /** + * Returns true if the given id represents a valid inventory object + */ +bool GetIsInvObject(int id) { + INV_OBJECT *pObject = g_invObjects; + + for (int i = 0; i < g_numObjects; i++, pObject++) { + if (pObject->id == id) + return true; + } + + return false; +} + +/** * Convert item ID number to index. */ static int GetObjectIndex(int id) { diff --git a/engines/tinsel/dialogs.h b/engines/tinsel/dialogs.h index 8c48eb8b76..ab53ba771c 100644 --- a/engines/tinsel/dialogs.h +++ b/engines/tinsel/dialogs.h @@ -152,6 +152,8 @@ void InvSetLimit(int invno, int n); void InvSetSize(int invno, int MinWidth, int MinHeight, int StartWidth, int StartHeight, int MaxWidth, int MaxHeight); +bool GetIsInvObject(int id); + int WhichInventoryOpen(); bool IsTopWindow(); diff --git a/engines/tinsel/drives.cpp b/engines/tinsel/drives.cpp index 5c4b939e4e..3ecef83753 100644 --- a/engines/tinsel/drives.cpp +++ b/engines/tinsel/drives.cpp @@ -149,7 +149,7 @@ bool GotoCD() { bool TinselFile::_warningShown = false; -TinselFile::TinselFile() : ReadStreamEndian((_vm->getFeatures() & GF_BIG_ENDIAN) != 0) { +TinselFile::TinselFile() : ReadStreamEndian(TinselV1Mac) { _stream = NULL; } diff --git a/engines/tinsel/handle.cpp b/engines/tinsel/handle.cpp index c3089db990..14d588dcec 100644 --- a/engines/tinsel/handle.cpp +++ b/engines/tinsel/handle.cpp @@ -99,14 +99,16 @@ void SetupHandleTable() { MEMHANDLE *pH; TinselFile f; - if (f.open(TinselV1PSX? PSX_INDEX_FILENAME : INDEX_FILENAME)) { + const char *indexFileName = TinselV1PSX ? PSX_INDEX_FILENAME : INDEX_FILENAME; + + if (f.open(indexFileName)) { // get size of index file len = f.size(); if (len > 0) { if ((len % RECORD_SIZE) != 0) { // index file is corrupt - error(FILE_IS_CORRUPT, TinselV1PSX? PSX_INDEX_FILENAME : INDEX_FILENAME); + error(FILE_IS_CORRUPT, indexFileName); } // calc number of handles @@ -132,16 +134,16 @@ void SetupHandleTable() { if (f.eos() || f.err()) { // index file is corrupt - error(FILE_IS_CORRUPT, (TinselV1PSX? PSX_INDEX_FILENAME : INDEX_FILENAME)); + error(FILE_IS_CORRUPT, indexFileName); } // close the file f.close(); } else { // index file is corrupt - error(FILE_IS_CORRUPT, (TinselV1PSX? PSX_INDEX_FILENAME : INDEX_FILENAME)); + error(FILE_IS_CORRUPT, indexFileName); } } else { // cannot find the index file - error(CANNOT_FIND_FILE, (TinselV1PSX? PSX_INDEX_FILENAME : INDEX_FILENAME)); + error(CANNOT_FIND_FILE, indexFileName); } // allocate memory nodes and load all permanent graphics diff --git a/engines/tinsel/music.cpp b/engines/tinsel/music.cpp index fa5334a033..b3bfbcc5dc 100644 --- a/engines/tinsel/music.cpp +++ b/engines/tinsel/music.cpp @@ -131,16 +131,13 @@ bool PlayMidiSequence(uint32 dwFileOffset, bool bLoop) { g_currentMidi = dwFileOffset; g_currentLoop = bLoop; - if (_vm->_config->_musicVolume != 0) { - bool mute = false; - if (ConfMan.hasKey("mute")) - mute = ConfMan.getBool("mute"); + bool mute = false; + if (ConfMan.hasKey("mute")) + mute = ConfMan.getBool("mute"); - SetMidiVolume(mute ? 0 : _vm->_config->_musicVolume); - } + SetMidiVolume(mute ? 0 : _vm->_config->_musicVolume); // the index and length of the last tune loaded - static uint32 dwLastMidiIndex = 0; // FIXME: Avoid non-const global vars uint32 dwSeqLen = 0; // length of the sequence // Support for external music from the music enhancement project @@ -181,61 +178,53 @@ bool PlayMidiSequence(uint32 dwFileOffset, bool bLoop) { if (dwFileOffset == 0) return true; - if (dwFileOffset != dwLastMidiIndex) { - Common::File midiStream; - - // open MIDI sequence file in binary mode - if (!midiStream.open(MIDI_FILE)) - error(CANNOT_FIND_FILE, MIDI_FILE); - - // update index of last tune loaded - dwLastMidiIndex = dwFileOffset; - - // move to correct position in the file - midiStream.seek(dwFileOffset, SEEK_SET); - - // read the length of the sequence - dwSeqLen = midiStream.readUint32LE(); - - // make sure buffer is large enough for this sequence - assert(dwSeqLen > 0 && dwSeqLen <= g_midiBuffer.size); - - // stop any currently playing tune - _vm->_midiMusic->stop(); - - // read the sequence - if (midiStream.read(g_midiBuffer.pDat, dwSeqLen) != dwSeqLen) - error(FILE_IS_CORRUPT, MIDI_FILE); - - midiStream.close(); - - // WORKAROUND for bug #2820054 "DW1: No intro music at first start on Wii", - // which actually affects all ports, since it's specific to the GRA version. - // - // The GRA version does not seem to set the channel volume at all for the first - // intro track, thus we need to do that here. We only initialize the channels - // used in that sequence. And we are using 127 as default channel volume. - // - // Only in the GRA version dwFileOffset can be "38888", just to be sure, we - // check for the SCN files feature flag not being set though. - if (_vm->getGameID() == GID_DW1 && dwFileOffset == 38888 && !(_vm->getFeatures() & GF_SCNFILES)) { - _vm->_midiMusic->send(0x7F07B0 | 3); - _vm->_midiMusic->send(0x7F07B0 | 5); - _vm->_midiMusic->send(0x7F07B0 | 8); - _vm->_midiMusic->send(0x7F07B0 | 10); - _vm->_midiMusic->send(0x7F07B0 | 13); - } + Common::File midiStream; - _vm->_midiMusic->playMIDI(dwSeqLen, bLoop); + // open MIDI sequence file in binary mode + if (!midiStream.open(MIDI_FILE)) + error(CANNOT_FIND_FILE, MIDI_FILE); - // Store the length - //dwLastSeqLen = dwSeqLen; - } else { - // dwFileOffset == dwLastMidiIndex - _vm->_midiMusic->stop(); - _vm->_midiMusic->playMIDI(dwSeqLen, bLoop); + // move to correct position in the file + midiStream.seek(dwFileOffset, SEEK_SET); + + // read the length of the sequence + dwSeqLen = midiStream.readUint32LE(); + + // make sure buffer is large enough for this sequence + assert(dwSeqLen > 0 && dwSeqLen <= g_midiBuffer.size); + + // stop any currently playing tune + _vm->_midiMusic->stop(); + + // read the sequence. This needs to be read again before playSEQ() is + // called even if the music is restarting, as playSEQ() reads the file + // name off the buffer itself. However, that function adds SMF headers + // to the buffer, thus if it's read again, the SMF headers will be read + // and the filename will always be 'MThd'. + if (midiStream.read(g_midiBuffer.pDat, dwSeqLen) != dwSeqLen) + error(FILE_IS_CORRUPT, MIDI_FILE); + + midiStream.close(); + + // WORKAROUND for bug #2820054 "DW1: No intro music at first start on Wii", + // which actually affects all ports, since it's specific to the GRA version. + // + // The GRA version does not seem to set the channel volume at all for the first + // intro track, thus we need to do that here. We only initialize the channels + // used in that sequence. And we are using 127 as default channel volume. + // + // Only in the GRA version dwFileOffset can be "38888", just to be sure, we + // check for the SCN files feature flag not being set though. + if (_vm->getGameID() == GID_DW1 && dwFileOffset == 38888 && !(_vm->getFeatures() & GF_SCNFILES)) { + _vm->_midiMusic->send(0x7F07B0 | 3); + _vm->_midiMusic->send(0x7F07B0 | 5); + _vm->_midiMusic->send(0x7F07B0 | 8); + _vm->_midiMusic->send(0x7F07B0 | 10); + _vm->_midiMusic->send(0x7F07B0 | 13); } + _vm->_midiMusic->playMIDI(dwSeqLen, bLoop); + return true; } @@ -279,27 +268,7 @@ int GetMidiVolume() { */ void SetMidiVolume(int vol) { assert(vol >= 0 && vol <= Audio::Mixer::kMaxChannelVolume); - - static int priorVolMusic = 0; // FIXME: Avoid non-const global vars - - if (vol == 0 && priorVolMusic == 0) { - // Nothing to do - } else if (vol == 0 && priorVolMusic != 0) { - // Stop current midi sequence - StopMidi(); - _vm->_midiMusic->setVolume(vol); - } else if (vol != 0 && priorVolMusic == 0) { - // Perhaps restart last midi sequence - if (g_currentLoop) - PlayMidiSequence(g_currentMidi, true); - - _vm->_midiMusic->setVolume(vol); - } else if (vol != 0 && priorVolMusic != 0) { - // Alter current volume - _vm->_midiMusic->setVolume(vol); - } - - priorVolMusic = vol; + _vm->_midiMusic->setVolume(vol); } /** @@ -309,7 +278,7 @@ void OpenMidiFiles() { Common::File midiStream; // Demo version has no midi file - if ((_vm->getFeatures() & GF_DEMO) || (TinselVersion == TINSEL_V2)) + if (TinselV0 || TinselV2) return; if (g_midiBuffer.pDat) @@ -942,14 +911,12 @@ void RestoreMidiFacts(SCNHANDLE Midi, bool Loop) { g_currentMidi = Midi; g_currentLoop = Loop; - if (_vm->_config->_musicVolume != 0 && Loop) { - bool mute = false; - if (ConfMan.hasKey("mute")) - mute = ConfMan.getBool("mute"); + bool mute = false; + if (ConfMan.hasKey("mute")) + mute = ConfMan.getBool("mute"); - PlayMidiSequence(g_currentMidi, true); - SetMidiVolume(mute ? 0 : _vm->_config->_musicVolume); - } + PlayMidiSequence(g_currentMidi, true); + SetMidiVolume(mute ? 0 : _vm->_config->_musicVolume); } #if 0 diff --git a/engines/tinsel/pcode.cpp b/engines/tinsel/pcode.cpp index 60f04b47fd..6ea18c8268 100644 --- a/engines/tinsel/pcode.cpp +++ b/engines/tinsel/pcode.cpp @@ -122,6 +122,8 @@ static uint32 g_hMasterScript; struct WorkaroundEntry { TinselEngineVersion version; ///< Engine version this workaround applies to bool scnFlag; ///< Only applicable for Tinsel 1 (DW 1) + bool isDemo; ///< Flags whether it's for a demo + Common::Platform platform; ///< Platform filter SCNHANDLE hCode; ///< Script to apply fragment to int ip; ///< Script offset to run this fragment before int numBytes; ///< Number of bytes in the script @@ -129,6 +131,7 @@ struct WorkaroundEntry { }; #define FRAGMENT_WORD(x) (byte)(x & 0xFF), (byte)(x >> 8) +#define FRAGMENT_DWORD(x) (byte)(x & 0xFF), (byte)(x >> 8), (byte)(x >> 16), (byte)(x >> 24) static const byte fragment1[] = {OP_ZERO, OP_GSTORE | OPSIZE16, 206, 0}; static const byte fragment2[] = {OP_LIBCALL | OPSIZE8, 110}; @@ -149,6 +152,10 @@ static const byte fragment12[] = {OP_JMPTRUE | OPSIZE16, FRAGMENT_WORD(1491), OP_IMM | OPSIZE16, FRAGMENT_WORD(322), OP_LIBCALL | OPSIZE8, 46, // Give back the whistle OP_JUMP | OPSIZE16, FRAGMENT_WORD(1568)}; static const byte fragment13[] = {OP_ZERO, OP_GSTORE | OPSIZE16, FRAGMENT_WORD(306)}; +static const byte fragment14[] = {OP_LIBCALL | OPSIZE8, 58, + OP_IMM, FRAGMENT_DWORD((42 << 23)), OP_ONE, OP_ZERO, OP_LIBCALL | OPSIZE8, 44, + OP_LIBCALL | OPSIZE8, 97, OP_JUMP | OPSIZE16, FRAGMENT_WORD(2220) +}; #undef FRAGMENT_WORD @@ -157,7 +164,7 @@ const WorkaroundEntry workaroundList[] = { // book back to the present. In the GRA version, it was global 373, // and was reset when he is returned to the past, but was forgotten // in the SCN version, so this ensures the flag is properly reset. - {TINSEL_V1, true, 427942095, 1, sizeof(fragment1), fragment1}, + {TINSEL_V1, true, false, Common::kPlatformUnknown, 427942095, 1, sizeof(fragment1), fragment1}, // DW1-GRA: Rincewind exiting the Inn is blocked by the luggage. // Whilst you can then move into walkable areas, saving and @@ -165,26 +172,26 @@ const WorkaroundEntry workaroundList[] = { // fragment turns off NPC blocking for the Outside Inn rooms so that // the luggage won't block Past Outside Inn. // See bug report #2525010. - {TINSEL_V1, false, 444622076, 0, sizeof(fragment2), fragment2}, + {TINSEL_V1, false, false, Common::kPlatformUnknown, 444622076, 0, sizeof(fragment2), fragment2}, // Present Outside Inn - {TINSEL_V1, false, 352600876, 0, sizeof(fragment2), fragment2}, + {TINSEL_V1, false, false, Common::kPlatformUnknown, 352600876, 0, sizeof(fragment2), fragment2}, // DW1-GRA: Talking to palace guards in Act 2 gives !!!HIGH // STRING||| - this happens if you initiate dialog with one of the // guards, but not the other. So these fragments provide the correct // talk parameters where needed. // See bug report #2831159. - {TINSEL_V1, false, 310506872, 463, sizeof(fragment4), fragment4}, - {TINSEL_V1, false, 310506872, 485, sizeof(fragment5), fragment5}, - {TINSEL_V1, false, 310506872, 513, sizeof(fragment6), fragment6}, - {TINSEL_V1, false, 310506872, 613, sizeof(fragment7), fragment7}, - {TINSEL_V1, false, 310506872, 641, sizeof(fragment8), fragment8}, + {TINSEL_V1, false, false, Common::kPlatformUnknown, 310506872, 463, sizeof(fragment4), fragment4}, + {TINSEL_V1, false, false, Common::kPlatformUnknown, 310506872, 485, sizeof(fragment5), fragment5}, + {TINSEL_V1, false, false, Common::kPlatformUnknown, 310506872, 513, sizeof(fragment6), fragment6}, + {TINSEL_V1, false, false, Common::kPlatformUnknown, 310506872, 613, sizeof(fragment7), fragment7}, + {TINSEL_V1, false, false, Common::kPlatformUnknown, 310506872, 641, sizeof(fragment8), fragment8}, // DW1-SCN: The script for the lovable street-Starfish does a // 'StopSample' after flicking the coin to ensure it's sound is // stopped, but which also accidentally can stop any active // conversation with the Amazon. - {TINSEL_V1, true, 394640351, 121, sizeof(fragment9), fragment9}, + {TINSEL_V1, true, false, Common::kPlatformUnknown, 394640351, 121, sizeof(fragment9), fragment9}, // DW2: In the garden, global #490 is set when the bees begin their // 'out of hive' animation, and reset when done. But if the game is @@ -197,25 +204,29 @@ const WorkaroundEntry workaroundList[] = { // * Stealing the mallets from the wizards (bug #2820788). // This fix ensures that the global is reset when the Garden scene // is loaded (both entering and restoring a game). - {TINSEL_V2, true, 2888147476U, 0, sizeof(fragment3), fragment3}, + {TINSEL_V2, true, false, Common::kPlatformUnknown, 2888147476U, 0, sizeof(fragment3), fragment3}, // DW1-GRA: Corrects text being drawn partially off-screen during // the blackboard description of the Librarian. - {TINSEL_V1, false, 293831402, 133, sizeof(fragment10), fragment10}, + {TINSEL_V1, false, false, Common::kPlatformUnknown, 293831402, 133, sizeof(fragment10), fragment10}, // DW1-GRA/SCN: Corrects the dead-end of being able to give the // whistle back to the pirate before giving him the parrot. // See bug report #2934211. - {TINSEL_V1, true, 352601285, 1569, sizeof(fragment11), fragment11}, - {TINSEL_V1, false, 352602304, 1488, sizeof(fragment12), fragment12}, + {TINSEL_V1, true, false, Common::kPlatformUnknown, 352601285, 1569, sizeof(fragment11), fragment11}, + {TINSEL_V1, false, false, Common::kPlatformUnknown, 352602304, 1488, sizeof(fragment12), fragment12}, // DW2: Corrects a bug with global 306 not being cleared if you leave // the marketplace scene whilst D'Blah is talking (even if it's not // actually audible); returning to the scene and clicking on him multiple // times would cause the game to crash - {TINSEL_V2, true, 1109294728, 0, sizeof(fragment13), fragment13}, + {TINSEL_V2, true, false, Common::kPlatformUnknown, 1109294728, 0, sizeof(fragment13), fragment13}, + + // DW1 PSX DEMO: Alters a script in the PSX DW1 demo to show the Idle animation scene rather than + // quitting the game when no user input happens for a while + {TINSEL_V1, true, true, Common::kPlatformPSX, 0, 2186, sizeof(fragment14), fragment14}, - {TINSEL_V0, false, 0, 0, 0, NULL} + {TINSEL_V0, false, false, Common::kPlatformUnknown, 0, 0, 0, NULL} }; //----------------- LOCAL GLOBAL DATA -------------------- @@ -582,6 +593,8 @@ void Interpret(CORO_PARAM, INT_CONTEXT *ic) { if ((wkEntry->version == TinselVersion) && (wkEntry->hCode == ic->hCode) && (wkEntry->ip == ip) && + (wkEntry->isDemo == _vm->getIsADGFDemo()) && + ((wkEntry->platform == Common::kPlatformUnknown) || (wkEntry->platform == _vm->getPlatform())) && (!TinselV1 || (wkEntry->scnFlag == ((_vm->getFeatures() & GF_SCNFILES) != 0)))) { // Point to start of workaround fragment ip = 0; diff --git a/engines/tinsel/saveload.cpp b/engines/tinsel/saveload.cpp index 0a552c8c2b..518e27f02b 100644 --- a/engines/tinsel/saveload.cpp +++ b/engines/tinsel/saveload.cpp @@ -55,8 +55,7 @@ namespace Tinsel { * only saves/loads those which are valid for the version of the savegame * which is being loaded/saved currently. */ -#define CURRENT_VER 1 -// TODO: Not yet used +#define CURRENT_VER 2 /** * An auxillary macro, used to specify savegame versions. We use this instead @@ -97,12 +96,13 @@ struct SaveGameHeader { TimeDate dateTime; bool scnFlag; byte language; + uint16 numInterpreters; // Savegame version 2 or later only }; enum { DW1_SAVEGAME_ID = 0x44575399, // = 'DWSc' = "DiscWorld 1 ScummVM" DW2_SAVEGAME_ID = 0x44573253, // = 'DW2S' = "DiscWorld 2 ScummVM" - SAVEGAME_HEADER_SIZE = 4 + 4 + 4 + SG_DESC_LEN + 7 + 1 + 1 + SAVEGAME_HEADER_SIZE = 4 + 4 + 4 + SG_DESC_LEN + 7 + 1 + 1 + 2 }; #define SAVEGAME_ID (TinselV2 ? (uint32)DW2_SAVEGAME_ID : (uint32)DW1_SAVEGAME_ID) @@ -186,6 +186,15 @@ static bool syncSaveGameHeader(Common::Serializer &s, SaveGameHeader &hdr) { } } + // Handle the number of interpreter contexts that will be saved in the savegame + if (tmp >= 2) { + tmp -= 2; + hdr.numInterpreters = NUM_INTERPRET; + s.syncAsUint16LE(hdr.numInterpreters); + } else { + hdr.numInterpreters = (TinselV2 ? 70 : 64) - 20; + } + // Skip over any extra bytes s.skip(tmp); return true; @@ -262,7 +271,7 @@ static void syncSoundReel(Common::Serializer &s, SOUNDREELS &sr) { s.syncAsSint32LE(sr.actorCol); } -static void syncSavedData(Common::Serializer &s, SAVED_DATA &sd) { +static void syncSavedData(Common::Serializer &s, SAVED_DATA &sd, int numInterp) { s.syncAsUint32LE(sd.SavedSceneHandle); s.syncAsUint32LE(sd.SavedBgroundHandle); for (int i = 0; i < MAX_MOVERS; ++i) @@ -273,7 +282,7 @@ static void syncSavedData(Common::Serializer &s, SAVED_DATA &sd) { s.syncAsSint32LE(sd.NumSavedActors); s.syncAsSint32LE(sd.SavedLoffset); s.syncAsSint32LE(sd.SavedToffset); - for (int i = 0; i < NUM_INTERPRET; ++i) + for (int i = 0; i < numInterp; ++i) sd.SavedICInfo[i].syncWithSerializer(s); for (int i = 0; i < MAX_POLY; ++i) s.syncAsUint32LE(sd.SavedDeadPolys[i]); @@ -422,7 +431,7 @@ char *ListEntry(int i, letype which) { return NULL; } -static void DoSync(Common::Serializer &s) { +static bool DoSync(Common::Serializer &s, int numInterp) { int sg = 0; if (TinselV2) { @@ -434,7 +443,7 @@ static void DoSync(Common::Serializer &s) { if (TinselV2 && s.isLoading()) HoldItem(INV_NOICON); - syncSavedData(s, *g_srsd); + syncSavedData(s, *g_srsd, numInterp); syncGlobInfo(s); // Glitter globals syncInvInfo(s); // Inventory data @@ -443,6 +452,10 @@ static void DoSync(Common::Serializer &s) { sg = WhichItemHeld(); s.syncAsSint32LE(sg); if (s.isLoading()) { + if (sg != -1 && !GetIsInvObject(sg)) + // Not a valid inventory object, so return false + return false; + if (TinselV2) g_thingHeld = sg; else @@ -459,7 +472,7 @@ static void DoSync(Common::Serializer &s) { if (*g_SaveSceneSsCount != 0) { SAVED_DATA *sdPtr = g_SaveSceneSsData; for (int i = 0; i < *g_SaveSceneSsCount; ++i, ++sdPtr) - syncSavedData(s, *sdPtr); + syncSavedData(s, *sdPtr, numInterp); // Flag that there is a saved scene to return to. Note that in this context 'saved scene' // is a stored scene to return to from another scene, such as from the Summoning Book close-up @@ -469,6 +482,8 @@ static void DoSync(Common::Serializer &s) { if (!TinselV2) syncAllActorsAlive(s); + + return true; } /** @@ -487,8 +502,23 @@ static bool DoRestore() { delete f; // Invalid header, or savegame too new -> skip it return false; } + + // Load in the data. For older savegame versions, we potentially need to load the data twice, once + // for pre 1.5 savegames, and if that fails, a second time for 1.5 savegames + int numInterpreters = hdr.numInterpreters; + int32 currentPos = f->pos(); + for (int tryNumber = 0; tryNumber < ((hdr.ver >= 2) ? 1 : 2); ++tryNumber) { + // If it's the second loop iteration, try with the 1.5 savegame number of interpreter contexts + if (tryNumber == 1) { + f->seek(currentPos); + numInterpreters = 80; + } - DoSync(s); + // Load the savegame data + if (DoSync(s, numInterpreters)) + // Data load was successful (or likely), so break out of loop + break; + } uint32 id = f->readSint32LE(); if (id != (uint32)0xFEEDFACE) @@ -575,7 +605,7 @@ static void DoSave() { return; } - DoSync(s); + DoSync(s, hdr.numInterpreters); // Write out the special Id for Discworld savegames f->writeUint32LE(0xFEEDFACE); diff --git a/engines/tinsel/scene.h b/engines/tinsel/scene.h index baaff27a3e..06e5c096d9 100644 --- a/engines/tinsel/scene.h +++ b/engines/tinsel/scene.h @@ -75,9 +75,9 @@ enum REEL { typedef enum { TRANS_DEF, TRANS_CUT, TRANS_FADE } TRANSITS; // amount to shift scene handles by -#define SCNHANDLE_SHIFT ((TinselV2 && !IsDemo) ? 25 : 23) -#define OFFSETMASK ((TinselV2 && !IsDemo) ? 0x01ffffffL : 0x007fffffL) -#define HANDLEMASK ((TinselV2 && !IsDemo) ? 0xFE000000L : 0xFF800000L) +#define SCNHANDLE_SHIFT ((TinselV2 && !TinselV2Demo) ? 25 : 23) +#define OFFSETMASK ((TinselV2 && !TinselV2Demo) ? 0x01ffffffL : 0x007fffffL) +#define HANDLEMASK ((TinselV2 && !TinselV2Demo) ? 0xFE000000L : 0xFF800000L) void DoHailScene(SCNHANDLE scene); diff --git a/engines/tinsel/sound.cpp b/engines/tinsel/sound.cpp index f575b03270..e052302cfd 100644 --- a/engines/tinsel/sound.cpp +++ b/engines/tinsel/sound.cpp @@ -75,7 +75,7 @@ SoundManager::~SoundManager() { // playSample for DiscWorld 1 bool SoundManager::playSample(int id, Audio::Mixer::SoundType type, Audio::SoundHandle *handle) { // Floppy version has no sample file - if (_vm->getFeatures() & GF_FLOPPY) + if (!_vm->isCD()) return false; // no sample driver? @@ -182,7 +182,7 @@ bool SoundManager::playSample(int id, int sub, bool bLooped, int x, int y, int p Audio::Mixer::SoundType type, Audio::SoundHandle *handle) { // Floppy version has no sample file - if (_vm->getFeatures() & GF_FLOPPY) + if (!_vm->isCD()) return false; // no sample driver? @@ -471,7 +471,7 @@ void SoundManager::setSFXVolumes(uint8 volume) { */ void SoundManager::openSampleFiles() { // Floppy and demo versions have no sample files, except for the Discworld 2 demo - if (_vm->getFeatures() & GF_FLOPPY || (IsDemo && !TinselV2)) + if (!_vm->isCD() || TinselV0) return; TinselFile f; diff --git a/engines/tinsel/tinlib.cpp b/engines/tinsel/tinlib.cpp index 5dda836144..058f8eb6fd 100644 --- a/engines/tinsel/tinlib.cpp +++ b/engines/tinsel/tinlib.cpp @@ -1625,10 +1625,6 @@ static void Play(CORO_PARAM, SCNHANDLE hFilm, int x, int y, bool bComplete, int * Play a midi file. */ static void PlayMidi(CORO_PARAM, SCNHANDLE hMidi, int loop, bool complete) { - // FIXME: This is a workaround for the FIXME below - if (GetMidiVolume() == 0) - return; - CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); @@ -1637,18 +1633,13 @@ static void PlayMidi(CORO_PARAM, SCNHANDLE hMidi, int loop, bool complete) { PlayMidiSequence(hMidi, loop == MIDI_LOOP); - // FIXME: The following check messes up the script arguments when - // entering the secret door in the bookshelf in the library, - // leading to a crash, when the music volume is set to 0 (MidiPlaying() - // always false then). - // - // Why exactly this happens is unclear. An analysis of the involved - // script(s) might reveal more. - // - // Note: This check&sleep was added in DW v2. It was most likely added - // to ensure that the MIDI song started playing before the next opcode + // This check&sleep was added in DW v2. It was most likely added to + // ensure that the MIDI song started playing before the next opcode // is executed. - if (!MidiPlaying()) + // In DW1, it messes up the script arguments when entering the secret + // door in the bookshelf in the library, leading to a crash, when the + // music volume is set to 0. + if (!MidiPlaying() && TinselV2) CORO_SLEEP(1); if (complete) { @@ -3412,7 +3403,7 @@ static void TalkOrSay(CORO_PARAM, SPEECH_TYPE speechType, SCNHANDLE hText, int x // Kick off the sample now (perhaps with a delay) if (g_bNoPause) g_bNoPause = false; - else if (!IsDemo) + else if (!TinselV2Demo) CORO_SLEEP(SysVar(SV_SPEECHDELAY)); //SamplePlay(VOICE, hText, _ctx->sub, false, -1, -1, PRIORITY_TALK); @@ -4244,7 +4235,7 @@ int CallLibraryRoutine(CORO_PARAM, int operand, int32 *pp, const INT_CONTEXT *pi int libCode; if (TinselV0) libCode = DW1DEMO_CODES[operand]; else if (!TinselV2) libCode = DW1_CODES[operand]; - else if (_vm->getFeatures() & GF_DEMO) libCode = DW2DEMO_CODES[operand]; + else if (TinselV2Demo) libCode = DW2DEMO_CODES[operand]; else libCode = DW2_CODES[operand]; debug(7, "CallLibraryRoutine op %d (escOn %d, myEscape %d)", operand, pic->escOn, pic->myEscape); diff --git a/engines/tinsel/tinsel.h b/engines/tinsel/tinsel.h index bac7ef6efb..123249125e 100644 --- a/engines/tinsel/tinsel.h +++ b/engines/tinsel/tinsel.h @@ -63,21 +63,16 @@ enum TinselGameID { }; enum TinselGameFeatures { - GF_DEMO = 1 << 0, - GF_CD = 1 << 1, - GF_FLOPPY = 1 << 2, - GF_SCNFILES = 1 << 3, - GF_ENHANCED_AUDIO_SUPPORT = 1 << 4, - GF_ALT_MIDI = 1 << 5, // Alternate sequence in midi.dat file + GF_SCNFILES = 1 << 0, + GF_ENHANCED_AUDIO_SUPPORT = 1 << 1, + GF_ALT_MIDI = 1 << 2, // Alternate sequence in midi.dat file // The GF_USE_?FLAGS values specify how many country flags are displayed // in the subtitles options dialog. // None of these defined -> 1 language, in ENGLISH.TXT - GF_USE_3FLAGS = 1 << 6, // French, German, Spanish - GF_USE_4FLAGS = 1 << 7, // French, German, Spanish, Italian - GF_USE_5FLAGS = 1 << 8, // All 5 flags - - GF_BIG_ENDIAN = 1 << 9 + GF_USE_3FLAGS = 1 << 3, // French, German, Spanish + GF_USE_4FLAGS = 1 << 4, // French, German, Spanish, Italian + GF_USE_5FLAGS = 1 << 5 // All 5 flags }; /** @@ -134,13 +129,12 @@ typedef bool (*KEYFPTR)(const Common::KeyState &); #define TinselV0 (TinselVersion == TINSEL_V0) #define TinselV1 (TinselVersion == TINSEL_V1) #define TinselV2 (TinselVersion == TINSEL_V2) +#define TinselV2Demo (TinselVersion == TINSEL_V2 && _vm->getIsADGFDemo()) #define TinselV1PSX (TinselVersion == TINSEL_V1 && _vm->getPlatform() == Common::kPlatformPSX) #define TinselV1Mac (TinselVersion == TINSEL_V1 && _vm->getPlatform() == Common::kPlatformMacintosh) -#define IsDemo (_vm->getFeatures() & GF_DEMO) - -#define READ_16(v) ((_vm->getFeatures() & GF_BIG_ENDIAN) ? READ_BE_UINT16(v) : READ_LE_UINT16(v)) -#define READ_32(v) ((_vm->getFeatures() & GF_BIG_ENDIAN) ? READ_BE_UINT32(v) : READ_LE_UINT32(v)) +#define READ_16(v) (TinselV1Mac ? READ_BE_UINT16(v) : READ_LE_UINT16(v)) +#define READ_32(v) (TinselV1Mac ? READ_BE_UINT32(v) : READ_LE_UINT32(v)) // Global reference to the TinselEngine object extern TinselEngine *_vm; @@ -186,6 +180,8 @@ public: uint16 getVersion() const; uint32 getFlags() const; Common::Platform getPlatform() const; + bool getIsADGFDemo() const; + bool isCD() const; const char *getSampleIndex(LANGUAGE lang); const char *getSampleFile(LANGUAGE lang); diff --git a/engines/toltecs/detection.cpp b/engines/toltecs/detection.cpp index c532bbbf09..c1a57638c2 100644 --- a/engines/toltecs/detection.cpp +++ b/engines/toltecs/detection.cpp @@ -273,8 +273,6 @@ SaveStateDescriptor ToltecsMetaEngine::querySaveMetaInfos(const char *target, in if (error == Toltecs::ToltecsEngine::kRSHENoError) { SaveStateDescriptor desc(slot, header.description); - desc.setDeletableFlag(true); - desc.setWriteProtectedFlag(false); desc.setThumbnail(header.thumbnail); if (header.version > 0) { diff --git a/engines/toon/anim.cpp b/engines/toon/anim.cpp index 84f6fa375c..1c85a8d798 100644 --- a/engines/toon/anim.cpp +++ b/engines/toon/anim.cpp @@ -132,7 +132,7 @@ Common::Rect Animation::getRect() { return Common::Rect(_x1, _y1, _x2, _y2); } -void Animation::drawFrame(Graphics::Surface &surface, int32 frame, int32 xx, int32 yy) { +void Animation::drawFrame(Graphics::Surface &surface, int32 frame, int16 xx, int16 yy) { debugC(3, kDebugAnim, "drawFrame(surface, %d, %d, %d)", frame, xx, yy); if (frame < 0) frame = 0; @@ -146,10 +146,13 @@ void Animation::drawFrame(Graphics::Surface &surface, int32 frame, int32 xx, int if (_frames[frame]._ref != -1) frame = _frames[frame]._ref; - int32 rectX = _frames[frame]._x2 - _frames[frame]._x1; - int32 rectY = _frames[frame]._y2 - _frames[frame]._y1; - int32 offsX = 0; - int32 offsY = 0; + if (!_frames[frame]._data) + return; + + int16 rectX = _frames[frame]._x2 - _frames[frame]._x1; + int16 rectY = _frames[frame]._y2 - _frames[frame]._y1; + int16 offsX = 0; + int16 offsY = 0; _vm->addDirtyRect(xx + _x1 + _frames[frame]._x1, yy + _y1 + _frames[frame]._y1, xx + rectX + _x1 + _frames[frame]._x1 , yy + rectY + _y1 + _frames[frame]._y1); @@ -186,10 +189,10 @@ void Animation::drawFrame(Graphics::Surface &surface, int32 frame, int32 xx, int int32 destPitch = surface.pitch; uint8 *srcRow = _frames[frame]._data + offsX + (_frames[frame]._x2 - _frames[frame]._x1) * offsY; uint8 *curRow = (uint8 *)surface.pixels + (yy + _frames[frame]._y1 + _y1 + offsY) * destPitch + (xx + _x1 + _frames[frame]._x1 + offsX); - for (int32 y = 0; y < rectY; y++) { + for (int16 y = 0; y < rectY; y++) { uint8 *cur = curRow; uint8 *c = srcRow + y * (_frames[frame]._x2 - _frames[frame]._x1); - for (int32 x = 0; x < rectX; x++) { + for (int16 x = 0; x < rectX; x++) { if (*c) *cur = *c; c++; @@ -199,27 +202,27 @@ void Animation::drawFrame(Graphics::Surface &surface, int32 frame, int32 xx, int } } -void Animation::drawFrameWithMask(Graphics::Surface &surface, int32 frame, int32 xx, int32 yy, int32 zz, Picture *mask) { +void Animation::drawFrameWithMask(Graphics::Surface &surface, int32 frame, int16 xx, int16 yy, int32 zz, Picture *mask) { debugC(1, kDebugAnim, "drawFrameWithMask(surface, %d, %d, %d, %d, mask)", frame, xx, yy, zz); warning("STUB: drawFrameWithMask()"); } -void Animation::drawFrameWithMaskAndScale(Graphics::Surface &surface, int32 frame, int32 xx, int32 yy, int32 zz, Picture *mask, int32 scale) { +void Animation::drawFrameWithMaskAndScale(Graphics::Surface &surface, int32 frame, int16 xx, int16 yy, int32 zz, Picture *mask, int32 scale) { debugC(5, kDebugAnim, "drawFrameWithMaskAndScale(surface, %d, %d, %d, %d, mask, %d)", frame, xx, yy, zz, scale); if (_frames[frame]._ref != -1) frame = _frames[frame]._ref; - int32 rectX = _frames[frame]._x2 - _frames[frame]._x1; - int32 rectY = _frames[frame]._y2 - _frames[frame]._y1; + int16 rectX = _frames[frame]._x2 - _frames[frame]._x1; + int16 rectY = _frames[frame]._y2 - _frames[frame]._y1; - int32 finalWidth = rectX * scale / 1024; - int32 finalHeight = rectY * scale / 1024; + int16 finalWidth = rectX * scale / 1024; + int16 finalHeight = rectY * scale / 1024; // compute final x1, y1, x2, y2 - int32 xx1 = xx + _x1 + _frames[frame]._x1 * scale / 1024; - int32 yy1 = yy + _y1 + _frames[frame]._y1 * scale / 1024; - int32 xx2 = xx1 + finalWidth; - int32 yy2 = yy1 + finalHeight; - int32 w = _frames[frame]._x2 - _frames[frame]._x1; + int16 xx1 = xx + _x1 + _frames[frame]._x1 * scale / 1024; + int16 yy1 = yy + _y1 + _frames[frame]._y1 * scale / 1024; + int16 xx2 = xx1 + finalWidth; + int16 yy2 = yy1 + finalHeight; + int16 w = _frames[frame]._x2 - _frames[frame]._x1; _vm->addDirtyRect(xx1, yy1, xx2, yy2); @@ -233,8 +236,8 @@ void Animation::drawFrameWithMaskAndScale(Graphics::Surface &surface, int32 fram if (strstr(_name, "SHADOW")) shadowFlag = true; - for (int32 y = yy1; y < yy2; y++) { - for (int32 x = xx1; x < xx2; x++) { + for (int16 y = yy1; y < yy2; y++) { + for (int16 x = xx1; x < xx2; x++) { if (x < 0 || x >= 1280 || y < 0 || y >= 400) continue; @@ -242,8 +245,8 @@ void Animation::drawFrameWithMaskAndScale(Graphics::Surface &surface, int32 fram uint8 *curMask = curRowMask + x + y * destPitchMask; // find the good c - int32 xs = (x - xx1) * 1024 / scale; - int32 ys = (y - yy1) * 1024 / scale; + int16 xs = (x - xx1) * 1024 / scale; + int16 ys = (y - yy1) * 1024 / scale; uint8 *cc = &c[ys * w + xs]; if (*cc && ((*curMask) >= zz)) { if (shadowFlag) @@ -272,7 +275,7 @@ Common::Rect Animation::getFrameRect(int32 frame) { return Common::Rect(_frames[frame]._x1, _frames[frame]._y1, _frames[frame]._x2, _frames[frame]._y2); } -int32 Animation::getFrameWidth(int32 frame) { +int16 Animation::getFrameWidth(int32 frame) { debugC(4, kDebugAnim, "getFrameWidth(%d)", frame); if ((frame < 0) || (frame >= _numFrames)) return 0; @@ -283,7 +286,7 @@ int32 Animation::getFrameWidth(int32 frame) { return _frames[frame]._x2 - _frames[frame]._x1; } -int32 Animation::getFrameHeight(int32 frame) { +int16 Animation::getFrameHeight(int32 frame) { debugC(4, kDebugAnim, "getFrameHeight(%d)", frame); if (frame < 0 || frame >= _numFrames) return 0; @@ -294,15 +297,15 @@ int32 Animation::getFrameHeight(int32 frame) { return _frames[frame]._y2 - _frames[frame]._y1; } -int32 Animation::getWidth() const { +int16 Animation::getWidth() const { return _x2 - _x1; } -int32 Animation::getHeight() const { +int16 Animation::getHeight() const { return _y2 - _y1; } -void Animation::drawFontFrame(Graphics::Surface &surface, int32 frame, int32 xx, int32 yy, byte *colorMap) { +void Animation::drawFontFrame(Graphics::Surface &surface, int32 frame, int16 xx, int16 yy, byte *colorMap) { debugC(4, kDebugAnim, "drawFontFrame(surface, %d, %d, %d, colorMap)", frame, xx, yy); if (frame < 0) frame = 0; @@ -316,8 +319,8 @@ void Animation::drawFontFrame(Graphics::Surface &surface, int32 frame, int32 xx, if (_frames[frame]._ref != -1) frame = _frames[frame]._ref; - int32 rectX = _frames[frame]._x2 - _frames[frame]._x1; - int32 rectY = _frames[frame]._y2 - _frames[frame]._y1; + int16 rectX = _frames[frame]._x2 - _frames[frame]._x1; + int16 rectY = _frames[frame]._y2 - _frames[frame]._y1; if ((xx + _x1 + _frames[frame]._x1 < 0) || (yy + _y1 + _frames[frame]._y1 < 0)) return; @@ -337,9 +340,9 @@ void Animation::drawFontFrame(Graphics::Surface &surface, int32 frame, int32 xx, int32 destPitch = surface.pitch; uint8 *c = _frames[frame]._data; uint8 *curRow = (uint8 *)surface.pixels + (yy + _frames[frame]._y1 + _y1) * destPitch + (xx + _x1 + _frames[frame]._x1); - for (int32 y = 0; y < rectY; y++) { + for (int16 y = 0; y < rectY; y++) { unsigned char *cur = curRow; - for (int32 x = 0; x < rectX; x++) { + for (int16 x = 0; x < rectX; x++) { if (*c && *c < 4) *cur = colorMap[*c]; c++; @@ -349,7 +352,7 @@ void Animation::drawFontFrame(Graphics::Surface &surface, int32 frame, int32 xx, } } -void Animation::drawFrameOnPicture(int32 frame, int32 xx, int32 yy) { +void Animation::drawFrameOnPicture(int32 frame, int16 xx, int16 yy) { debugC(1, kDebugAnim, "drawFrameOnPicture(%d, %d, %d)", frame, xx, yy); if (frame < 0) frame = 0; @@ -363,8 +366,8 @@ void Animation::drawFrameOnPicture(int32 frame, int32 xx, int32 yy) { if (_frames[frame]._ref != -1) frame = _frames[frame]._ref; - int32 rectX = _frames[frame]._x2 - _frames[frame]._x1; - int32 rectY = _frames[frame]._y2 - _frames[frame]._y1; + int16 rectX = _frames[frame]._x2 - _frames[frame]._x1; + int16 rectY = _frames[frame]._y2 - _frames[frame]._y1; Picture *pic = _vm->getPicture(); @@ -386,9 +389,9 @@ void Animation::drawFrameOnPicture(int32 frame, int32 xx, int32 yy) { int32 destPitch = pic->getWidth(); uint8 *c = _frames[frame]._data; uint8 *curRow = (uint8 *)pic->getDataPtr() + (yy + _frames[frame]._y1 + _y1) * destPitch + (xx + _x1 + _frames[frame]._x1); - for (int32 y = 0; y < rectY; y++) { + for (int16 y = 0; y < rectY; y++) { unsigned char *cur = curRow; - for (int32 x = 0; x < rectX; x++) { + for (int16 x = 0; x < rectX; x++) { if (*c) *cur = *c; c++; @@ -455,8 +458,8 @@ void AnimationInstance::render() { if (frame >= _animation->_numFrames) frame = _animation->_numFrames - 1; - int32 x = _x; - int32 y = _y; + int16 x = _x; + int16 y = _y; if (_alignBottom) { int32 offsetX = (_animation->_x2 - _animation->_x1) / 2 * (_scale - 1024); @@ -498,7 +501,7 @@ void AnimationInstance::setAnimation(Animation *animation, bool setRange) { } } -void AnimationInstance::setAnimationRange(int32 rangeStart, int rangeEnd) { +void AnimationInstance::setAnimationRange(int32 rangeStart, int32 rangeEnd) { debugC(5, kDebugAnim, "setAnimationRange(%d, %d)", rangeStart, rangeEnd); _rangeStart = rangeStart; _rangeEnd = rangeEnd; @@ -510,7 +513,7 @@ void AnimationInstance::setAnimationRange(int32 rangeStart, int rangeEnd) { _currentFrame = _rangeEnd; } -void AnimationInstance::setPosition(int32 x, int32 y, int32 z, bool relative) { +void AnimationInstance::setPosition(int16 x, int16 y, int32 z, bool relative) { debugC(5, kDebugAnim, "setPosition(%d, %d, %d, %d)", x, y, z, (relative) ? 1 : 0); if (relative || !_animation) { _x = x; @@ -523,7 +526,7 @@ void AnimationInstance::setPosition(int32 x, int32 y, int32 z, bool relative) { } } -void AnimationInstance::moveRelative(int32 dx, int32 dy, int32 dz) { +void AnimationInstance::moveRelative(int16 dx, int16 dy, int32 dz) { debugC(1, kDebugAnim, "moveRelative(%d, %d, %d)", dx, dy, dz); _x += dx; _y += dy; @@ -568,13 +571,13 @@ void AnimationInstance::setUseMask(bool useMask) { _useMask = useMask; } -void AnimationInstance::getRect(int32 *x1, int32 *y1, int32 *x2, int32 *y2) const { +void AnimationInstance::getRect(int16 *x1, int16 *y1, int16 *x2, int16 *y2) const { debugC(5, kDebugAnim, "getRect(%d, %d, %d, %d)", *x1, *y1, *x2, *y2); - int32 rectX = _animation->_frames[_currentFrame]._x2 - _animation->_frames[_currentFrame]._x1; - int32 rectY = _animation->_frames[_currentFrame]._y2 - _animation->_frames[_currentFrame]._y1; + int16 rectX = _animation->_frames[_currentFrame]._x2 - _animation->_frames[_currentFrame]._x1; + int16 rectY = _animation->_frames[_currentFrame]._y2 - _animation->_frames[_currentFrame]._y1; - int32 finalWidth = rectX * _scale / 1024; - int32 finalHeight = rectY * _scale / 1024; + int16 finalWidth = rectX * _scale / 1024; + int16 finalHeight = rectY * _scale / 1024; // compute final x1, y1, x2, y2 *x1 = _x + _animation->_x1 + _animation->_frames[_currentFrame]._x1 * _scale / 1024; @@ -583,7 +586,7 @@ void AnimationInstance::getRect(int32 *x1, int32 *y1, int32 *x2, int32 *y2) cons *y2 = *y1 + finalHeight; } -void AnimationInstance::setX(int32 x, bool relative) { +void AnimationInstance::setX(int16 x, bool relative) { debugC(1, kDebugAnim, "setX(%d, %d)", x, (relative) ? 1 : 0); if (relative || !_animation) _x = x; @@ -591,7 +594,7 @@ void AnimationInstance::setX(int32 x, bool relative) { _x = x - _animation->_x1; } -void AnimationInstance::setY(int32 y, bool relative) { +void AnimationInstance::setY(int16 y, bool relative) { debugC(1, kDebugAnim, "setY(%d, %d)", y, (relative) ? 1 : 0); if (relative || !_animation) _y = y; @@ -614,11 +617,11 @@ int32 AnimationInstance::getLayerZ() const { return _layerZ; } -int32 AnimationInstance::getX2() const { +int16 AnimationInstance::getX2() const { return _x + _animation->_x1; } -int32 AnimationInstance::getY2() const { +int16 AnimationInstance::getY2() const { return _y + _animation->_y1; } @@ -647,6 +650,7 @@ void AnimationInstance::save(Common::WriteStream *stream) { stream->writeSint32LE(_visible); stream->writeSint32LE(_useMask); } + void AnimationInstance::load(Common::ReadStream *stream) { _currentFrame = stream->readSint32LE(); _currentTime = stream->readSint32LE(); @@ -695,14 +699,13 @@ void AnimationManager::updateInstance(AnimationInstance* instance) { } void AnimationManager::addInstance(AnimationInstance *instance) { - // if the instance already exists, we skip the add for (uint32 i = 0; i < _instances.size(); i++) { if (_instances[i] == instance) return; } - int found = -1; + int32 found = -1; // here we now do an ordered insert (closer to the original game) for (uint32 i = 0; i < _instances.size(); i++) { @@ -712,11 +715,10 @@ void AnimationManager::addInstance(AnimationInstance *instance) { } } - if ( found == -1 ) { + if (found == -1) _instances.push_back(instance); - } else { + else _instances.insert_at(found, instance); - } } void AnimationManager::removeInstance(AnimationInstance *instance) { diff --git a/engines/toon/anim.h b/engines/toon/anim.h index eb8dcbd600..cd550b2621 100644 --- a/engines/toon/anim.h +++ b/engines/toon/anim.h @@ -36,10 +36,10 @@ class Picture; class ToonEngine; struct AnimationFrame { - int32 _x1; - int32 _y1; - int32 _x2; - int32 _y2; + int16 _x1; + int16 _y1; + int16 _x2; + int16 _y2; int32 _ref; uint8 *_data; }; @@ -49,10 +49,10 @@ public: Animation(ToonEngine *vm); ~Animation(); - int32 _x1; - int32 _y1; - int32 _x2; - int32 _y2; + int16 _x1; + int16 _y1; + int16 _x2; + int16 _y2; int32 _numFrames; int32 _fps; AnimationFrame *_frames; @@ -61,18 +61,18 @@ public: char _name[32]; bool loadAnimation(const Common::String &file); - void drawFrame(Graphics::Surface &surface, int32 frame, int32 x, int32 y); - void drawFontFrame(Graphics::Surface &surface, int32 frame, int32 x, int32 y, byte *colorMap); - void drawFrameOnPicture(int32 frame, int32 x, int32 y); - void drawFrameWithMask(Graphics::Surface &surface, int32 frame, int32 xx, int32 yy, int32 zz, Picture *mask); - void drawFrameWithMaskAndScale(Graphics::Surface &surface, int32 frame, int32 xx, int32 yy, int32 zz, Picture *mask, int32 scale); + void drawFrame(Graphics::Surface &surface, int32 frame, int16 x, int16 y); + void drawFontFrame(Graphics::Surface &surface, int32 frame, int16 x, int16 y, byte *colorMap); + void drawFrameOnPicture(int32 frame, int16 x, int16 y); + void drawFrameWithMask(Graphics::Surface &surface, int32 frame, int16 xx, int16 yy, int32 zz, Picture *mask); + void drawFrameWithMaskAndScale(Graphics::Surface &surface, int32 frame, int16 xx, int16 yy, int32 zz, Picture *mask, int32 scale); void drawStrip(int32 offset = 0); void applyPalette(int32 offset, int32 srcOffset, int32 numEntries); Common::Rect getFrameRect(int32 frame); - int32 getFrameWidth(int32 frame); - int32 getFrameHeight(int32 frame); - int32 getWidth() const; - int32 getHeight() const; + int16 getFrameWidth(int32 frame); + int16 getFrameHeight(int32 frame); + int16 getWidth() const; + int16 getHeight() const; Common::Rect getRect(); protected: ToonEngine *_vm; @@ -92,25 +92,25 @@ public: void renderOnPicture(); void setAnimation(Animation *animation, bool setRange = true); void playAnimation(); - void setAnimationRange(int32 rangeStart, int rangeEnd); + void setAnimationRange(int32 rangeStart, int32 rangeEnd); void setFps(int32 fps); void setLooping(bool enable); void stopAnimation(); void setFrame(int32 position); void forceFrame(int32 position); - void setPosition(int32 x, int32 y, int32 z, bool relative = false); + void setPosition(int16 x, int16 y, int32 z, bool relative = false); Animation *getAnimation() const { return _animation; } void setScale(int32 scale, bool align = false); void setVisible(bool visible); bool getVisible() const { return _visible; } void setUseMask(bool useMask); - void moveRelative(int32 dx, int32 dy, int32 dz); - void getRect(int32 *x1, int32 *y1, int32 *x2, int32 *y2) const; - int32 getX() const { return _x; } - int32 getY() const { return _y; } + void moveRelative(int16 dx, int16 dy, int32 dz); + void getRect(int16 *x1, int16 *y1, int16 *x2, int16 *y2) const; + int16 getX() const { return _x; } + int16 getY() const { return _y; } int32 getZ() const { return _z; } - int32 getX2() const; - int32 getY2() const; + int16 getX2() const; + int16 getY2() const; int32 getZ2() const; int32 getFrame() const { return _currentFrame; } void reset(); @@ -120,8 +120,8 @@ public: void setId(int32 id) { _id = id; } int32 getId() const { return _id; } - void setX(int32 x, bool relative = false); - void setY(int32 y, bool relative = false); + void setX(int16 x, bool relative = false); + void setY(int16 y, bool relative = false); void setZ(int32 z, bool relative = false); void setLayerZ(int32 layer); int32 getLayerZ() const; @@ -133,8 +133,8 @@ protected: int32 _currentTime; int32 _fps; Animation *_animation; - int32 _x; - int32 _y; + int16 _x; + int16 _y; int32 _z; int32 _layerZ; int32 _rangeStart; diff --git a/engines/toon/audio.cpp b/engines/toon/audio.cpp index 77822ab078..bc0e051057 100644 --- a/engines/toon/audio.cpp +++ b/engines/toon/audio.cpp @@ -326,7 +326,7 @@ bool AudioStreamInstance::readPacket() { } if (numDecompressedBytes > _bufferMaxSize) { - delete [] _buffer; + delete[] _buffer; _bufferMaxSize = numDecompressedBytes; _buffer = new int16[numDecompressedBytes]; } diff --git a/engines/toon/character.cpp b/engines/toon/character.cpp index 09730626f2..479f4965f3 100644 --- a/engines/toon/character.cpp +++ b/engines/toon/character.cpp @@ -62,7 +62,7 @@ Character::Character(ToonEngine *vm) : _vm(vm) { _speed = 150; // 150 = nominal drew speed _lastWalkTime = 0; _numPixelToWalk = 0; - _nextIdleTime = _vm->getSystem()->getMillis() + (_vm->randRange(0, 600) + 300) * _vm->getTickLength(); + _nextIdleTime = _vm->_system->getMillis() + (_vm->randRange(0, 600) + 300) * _vm->getTickLength(); _lineToSayId = 0; } @@ -101,7 +101,7 @@ void Character::setFacing(int32 facing) { int32 dir = 0; - _lastWalkTime = _vm->getSystem()->getMillis(); + _lastWalkTime = _vm->_system->getMillis(); if ((_facing - facing + 8) % 8 > (facing - _facing + 8) % 8) dir = 1; else @@ -188,7 +188,7 @@ bool Character::walkTo(int16 newPosX, int16 newPosY) { _currentPathNode = 0; stopSpecialAnim(); - _lastWalkTime = _vm->getSystem()->getMillis(); + _lastWalkTime = _vm->_system->getMillis(); _numPixelToWalk = 0; @@ -220,8 +220,8 @@ bool Character::walkTo(int16 newPosX, int16 newPosY) { } // in 1/1000 pixels - _numPixelToWalk += _speed * (_vm->getSystem()->getMillis() - _lastWalkTime) * _scale / 1024; - _lastWalkTime = _vm->getSystem()->getMillis(); + _numPixelToWalk += _speed * (_vm->_system->getMillis() - _lastWalkTime) * _scale / 1024; + _lastWalkTime = _vm->_system->getMillis(); while (_numPixelToWalk >= 1000 && _currentPathNode < _currentPath.size()) { _x = _currentPath[_currentPathNode].x; @@ -356,8 +356,8 @@ void Character::update(int32 timeIncrement) { } // in 1/1000 pixels - _numPixelToWalk += _speed * (_vm->getSystem()->getMillis() - _lastWalkTime) * _scale / 1024; - _lastWalkTime = _vm->getSystem()->getMillis(); + _numPixelToWalk += _speed * (_vm->_system->getMillis() - _lastWalkTime) * _scale / 1024; + _lastWalkTime = _vm->_system->getMillis(); while (_numPixelToWalk > 1000 && _currentPathNode < _currentPath.size()) { _x = _currentPath[_currentPathNode].x; @@ -534,35 +534,33 @@ int32 Character::getFacingFromDirection(int16 dx, int16 dy) { dx = -dx; int32 facingEntry = 0; - int32 ydiff = dy; + int16 ydiff = dy; if (ydiff < 0) { ++facingEntry; ydiff = -ydiff; } - facingEntry <<= 1; + facingEntry *= 2; - int32 xdiff = dx; + int16 xdiff = dx; if (xdiff < 0) { ++facingEntry; xdiff = -xdiff; } - facingEntry <<= 1; + facingEntry *= 2; if (xdiff >= ydiff) { - int32 temp = ydiff; + // Swap xdiff and ydiff + int16 temp = ydiff; ydiff = xdiff; xdiff = temp; - } else { - facingEntry += 1; - } - - facingEntry <<= 1; + } else + facingEntry++; - int32 temp = (ydiff + 1) >> 1; + facingEntry *= 2; - if (xdiff < temp) - facingEntry += 1; + if (xdiff < ((ydiff + 1) / 2)) + facingEntry++; return facingTable[facingEntry]; } diff --git a/engines/toon/detection.cpp b/engines/toon/detection.cpp index 8234934972..3877fa2a6c 100644 --- a/engines/toon/detection.cpp +++ b/engines/toon/detection.cpp @@ -133,7 +133,7 @@ public: } virtual const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const { - return detectGameFilebased(allFiles, Toon::fileBasedFallback); + return detectGameFilebased(allFiles, fslist, Toon::fileBasedFallback); } virtual const char *getName() const { @@ -234,9 +234,6 @@ SaveStateDescriptor ToonMetaEngine::querySaveMetaInfos(const char *target, int s Graphics::Surface *const thumbnail = Graphics::loadThumbnail(*file); desc.setThumbnail(thumbnail); - desc.setDeletableFlag(true); - desc.setWriteProtectedFlag(false); - uint32 saveDate = file->readUint32BE(); uint16 saveTime = file->readUint16BE(); diff --git a/engines/toon/font.cpp b/engines/toon/font.cpp index d58663a00c..1e851ff4ae 100644 --- a/engines/toon/font.cpp +++ b/engines/toon/font.cpp @@ -65,10 +65,10 @@ byte FontRenderer::textToFont(byte c) { return map_textToFont[c - 0x80]; } -void FontRenderer::renderText(int32 x, int32 y, const Common::String &origText, int32 mode) { +void FontRenderer::renderText(int16 x, int16 y, const Common::String &origText, int32 mode) { debugC(5, kDebugFont, "renderText(%d, %d, %s, %d)", x, y, origText.c_str(), mode); - int32 xx, yy; + int16 xx, yy; computeSize(origText, &xx, &yy); if (mode & 2) { @@ -83,8 +83,8 @@ void FontRenderer::renderText(int32 x, int32 y, const Common::String &origText, _vm->addDirtyRect(x, y, x + xx, y + yy); - int32 curX = x; - int32 curY = y; + int16 curX = x; + int16 curY = y; int32 height = 0; const byte *text = (const byte *)origText.c_str(); @@ -98,20 +98,20 @@ void FontRenderer::renderText(int32 x, int32 y, const Common::String &origText, curChar = textToFont(curChar); _currentFont->drawFontFrame(_vm->getMainSurface(), curChar, curX, curY, _currentFontColor); curX = curX + _currentFont->getFrameWidth(curChar) - 1; - height = MAX(height, _currentFont->getFrameHeight(curChar)); + height = MAX<int32>(height, _currentFont->getFrameHeight(curChar)); } text++; } } -void FontRenderer::computeSize(const Common::String &origText, int32 *retX, int32 *retY) { +void FontRenderer::computeSize(const Common::String &origText, int16 *retX, int16 *retY) { debugC(4, kDebugFont, "computeSize(%s, retX, retY)", origText.c_str()); - int32 lineWidth = 0; - int32 lineHeight = 0; - int32 totalHeight = 0; - int32 totalWidth = 0; - int32 lastLineHeight = 0; + int16 lineWidth = 0; + int16 lineHeight = 0; + int16 totalHeight = 0; + int16 totalWidth = 0; + int16 lastLineHeight = 0; const byte *text = (const byte *)origText.c_str(); while (*text) { @@ -127,8 +127,8 @@ void FontRenderer::computeSize(const Common::String &origText, int32 *retX, int3 lastLineHeight = 0; } else { curChar = textToFont(curChar); - int32 charWidth = _currentFont->getFrameWidth(curChar) - 1; - int32 charHeight = _currentFont->getFrameHeight(curChar); + int16 charWidth = _currentFont->getFrameWidth(curChar) - 1; + int16 charHeight = _currentFont->getFrameHeight(curChar); lineWidth += charWidth; lineHeight = MAX(lineHeight, charHeight); @@ -137,7 +137,7 @@ void FontRenderer::computeSize(const Common::String &origText, int32 *retX, int3 // assume we only need to take the lower bound into // consideration. Common::Rect charRect = _currentFont->getFrameRect(curChar); - lastLineHeight = MAX<int32>(lastLineHeight, charRect.bottom); + lastLineHeight = MAX(lastLineHeight, charRect.bottom); } text++; } @@ -189,7 +189,7 @@ void FontRenderer::setFontColor(int32 fontColor1, int32 fontColor2, int32 fontCo _currentFontColor[3] = fontColor3; } -void FontRenderer::renderMultiLineText(int32 x, int32 y, const Common::String &origText, int32 mode) { +void FontRenderer::renderMultiLineText(int16 x, int16 y, const Common::String &origText, int32 mode) { debugC(5, kDebugFont, "renderMultiLineText(%d, %d, %s, %d)", x, y, origText.c_str(), mode); // divide the text in several lines @@ -204,10 +204,10 @@ void FontRenderer::renderMultiLineText(int32 x, int32 y, const Common::String &o byte *it = text; - int32 maxWidth = 0; - int32 curWidth = 0; + int16 maxWidth = 0; + int16 curWidth = 0; - while (1) { + while (true) { byte *lastLine = it; byte *lastSpace = it; int32 lastSpaceX = 0; @@ -223,7 +223,7 @@ void FontRenderer::renderMultiLineText(int32 x, int32 y, const Common::String &o curChar = textToFont(curChar); int width = _currentFont->getFrameWidth(curChar); - curWidth += MAX<int32>(width - 2, 0); + curWidth += MAX(width - 2, 0); it++; curLetterNr++; } @@ -260,8 +260,8 @@ void FontRenderer::renderMultiLineText(int32 x, int32 y, const Common::String &o //numLines++; // get font height (assumed to be constant) - int32 height = _currentFont->getHeight(); - int textSize = (height - 2) * numLines; + int16 height = _currentFont->getHeight(); + int32 textSize = (height - 2) * numLines; y = y - textSize; if (y < 30) y = 30; @@ -278,8 +278,8 @@ void FontRenderer::renderMultiLineText(int32 x, int32 y, const Common::String &o x = TOON_SCREEN_WIDTH - (maxWidth / 2) - 30; // we have good coordinates now, we can render the multi line - int32 curX = x; - int32 curY = y; + int16 curX = x; + int16 curY = y; for (int32 i = 0; i < numLines; i++) { const byte *line = lines[i]; diff --git a/engines/toon/font.h b/engines/toon/font.h index 349d9d1ee7..2a46ad3559 100644 --- a/engines/toon/font.h +++ b/engines/toon/font.h @@ -33,9 +33,9 @@ public: ~FontRenderer(); void setFont(Animation *font); - void computeSize(const Common::String &origText, int32 *retX, int32 *retY); - void renderText(int32 x, int32 y, const Common::String &origText, int32 mode); - void renderMultiLineText(int32 x, int32 y, const Common::String &origText, int32 mode); + void computeSize(const Common::String &origText, int16 *retX, int16 *retY); + void renderText(int16 x, int16 y, const Common::String &origText, int32 mode); + void renderMultiLineText(int16 x, int16 y, const Common::String &origText, int32 mode); void setFontColorByCharacter(int32 characterId); void setFontColor(int32 fontColor1, int32 fontColor2, int32 fontColor3); protected: diff --git a/engines/toon/hotspot.cpp b/engines/toon/hotspot.cpp index ce2cdf1bb9..8b8f0ab577 100644 --- a/engines/toon/hotspot.cpp +++ b/engines/toon/hotspot.cpp @@ -57,7 +57,7 @@ void Hotspots::save(Common::WriteStream *Stream) { } } -int32 Hotspots::FindBasedOnCorner(int32 x, int32 y) { +int32 Hotspots::FindBasedOnCorner(int16 x, int16 y) { debugC(1, kDebugHotspot, "FindBasedOnCorner(%d, %d)", x, y); for (int32 i = 0; i < _numItems; i++) { @@ -73,7 +73,7 @@ int32 Hotspots::FindBasedOnCorner(int32 x, int32 y) { return -1; } -int32 Hotspots::Find(int32 x, int32 y) { +int32 Hotspots::Find(int16 x, int16 y) { debugC(6, kDebugHotspot, "Find(%d, %d)", x, y); int32 priority = -1; diff --git a/engines/toon/hotspot.h b/engines/toon/hotspot.h index 70e80046e1..3852e2e42e 100644 --- a/engines/toon/hotspot.h +++ b/engines/toon/hotspot.h @@ -51,8 +51,8 @@ public: ~Hotspots(); bool LoadRif(const Common::String &rifName, const Common::String &additionalRifName); - int32 Find(int32 x, int32 y); - int32 FindBasedOnCorner(int32 x, int32 y); + int32 Find(int16 x, int16 y); + int32 FindBasedOnCorner(int16 x, int16 y); HotspotData *Get(int32 id); int32 getCount() const { return _numItems; } diff --git a/engines/toon/movie.cpp b/engines/toon/movie.cpp index 8cdf92363f..8c85e20f7c 100644 --- a/engines/toon/movie.cpp +++ b/engines/toon/movie.cpp @@ -25,6 +25,7 @@ #include "common/keyboard.h" #include "common/stream.h" #include "common/system.h" +#include "graphics/palette.h" #include "graphics/surface.h" #include "toon/audio.h" @@ -33,6 +34,10 @@ namespace Toon { +ToonstruckSmackerDecoder::ToonstruckSmackerDecoder() : Video::SmackerDecoder() { + _lowRes = false; +} + void ToonstruckSmackerDecoder::handleAudioTrack(byte track, uint32 chunkSize, uint32 unpackedSize) { debugC(6, kDebugMovie, "handleAudioTrack(%d, %d, %d)", track, chunkSize, unpackedSize); @@ -40,33 +45,21 @@ void ToonstruckSmackerDecoder::handleAudioTrack(byte track, uint32 chunkSize, ui /* uint16 width = */ _fileStream->readUint16LE(); uint16 height = _fileStream->readUint16LE(); _lowRes = (height == getHeight() / 2); - } else + } else { Video::SmackerDecoder::handleAudioTrack(track, chunkSize, unpackedSize); + } } -bool ToonstruckSmackerDecoder::loadFile(const Common::String &filename) { - debugC(1, kDebugMovie, "loadFile(%s)", filename.c_str()); +bool ToonstruckSmackerDecoder::loadStream(Common::SeekableReadStream *stream) { + if (!Video::SmackerDecoder::loadStream(stream)) + return false; _lowRes = false; - - if (Video::SmackerDecoder::loadFile(filename)) { - if (_surface->h == 200) { - if (_surface) { - _surface->free(); - delete _surface; - } - _surface = new Graphics::Surface(); - _surface->create(640, 400, Graphics::PixelFormat::createFormatCLUT8()); - _header.flags = 4; - } - - return true; - } - return false; + return true; } -ToonstruckSmackerDecoder::ToonstruckSmackerDecoder(Audio::Mixer *mixer, Audio::Mixer::SoundType soundType) : Video::SmackerDecoder(mixer, soundType) { - _lowRes = false; +Video::SmackerDecoder::SmackerVideoTrack *ToonstruckSmackerDecoder::createVideoTrack(uint32 width, uint32 height, uint32 frameCount, const Common::Rational &frameRate, uint32 flags, uint32 signature) const { + return Video::SmackerDecoder::createVideoTrack(width, height, frameCount, frameRate, (height == 200) ? 4 : flags, signature); } // decoder is deallocated with Movie destruction i.e. new ToonstruckSmackerDecoder is needed @@ -103,46 +96,49 @@ void Movie::play(const Common::String &video, int32 flags) { bool Movie::playVideo(bool isFirstIntroVideo) { debugC(1, kDebugMovie, "playVideo(isFirstIntroVideo: %d)", isFirstIntroVideo); + + _decoder->start(); + while (!_vm->shouldQuit() && !_decoder->endOfVideo()) { if (_decoder->needsUpdate()) { const Graphics::Surface *frame = _decoder->decodeNextFrame(); if (frame) { if (_decoder->isLowRes()) { // handle manually 2x scaling here - Graphics::Surface* surf = _vm->getSystem()->lockScreen(); + Graphics::Surface* surf = _vm->_system->lockScreen(); for (int y = 0; y < frame->h / 2; y++) { memcpy(surf->getBasePtr(0, y * 2 + 0), frame->getBasePtr(0, y), frame->pitch); memcpy(surf->getBasePtr(0, y * 2 + 1), frame->getBasePtr(0, y), frame->pitch); } - _vm->getSystem()->unlockScreen(); + _vm->_system->unlockScreen(); } else { - _vm->getSystem()->copyRectToScreen(frame->pixels, frame->pitch, 0, 0, frame->w, frame->h); + _vm->_system->copyRectToScreen(frame->pixels, frame->pitch, 0, 0, frame->w, frame->h); // WORKAROUND: There is an encoding glitch in the first intro video. This hides this using the adjacent pixels. if (isFirstIntroVideo) { int32 currentFrame = _decoder->getCurFrame(); if (currentFrame >= 956 && currentFrame <= 1038) { debugC(1, kDebugMovie, "Triggered workaround for glitch in first intro video..."); - _vm->getSystem()->copyRectToScreen(frame->getBasePtr(frame->w-188, 123), frame->pitch, frame->w-188, 124, 188, 1); - _vm->getSystem()->copyRectToScreen(frame->getBasePtr(frame->w-188, 126), frame->pitch, frame->w-188, 125, 188, 1); - _vm->getSystem()->copyRectToScreen(frame->getBasePtr(0, 125), frame->pitch, 0, 126, 64, 1); - _vm->getSystem()->copyRectToScreen(frame->getBasePtr(0, 128), frame->pitch, 0, 127, 64, 1); + _vm->_system->copyRectToScreen(frame->getBasePtr(frame->w-188, 123), frame->pitch, frame->w-188, 124, 188, 1); + _vm->_system->copyRectToScreen(frame->getBasePtr(frame->w-188, 126), frame->pitch, frame->w-188, 125, 188, 1); + _vm->_system->copyRectToScreen(frame->getBasePtr(0, 125), frame->pitch, 0, 126, 64, 1); + _vm->_system->copyRectToScreen(frame->getBasePtr(0, 128), frame->pitch, 0, 127, 64, 1); } } } } - _decoder->setSystemPalette(); - _vm->getSystem()->updateScreen(); + _vm->_system->getPaletteManager()->setPalette(_decoder->getPalette(), 0, 256); + _vm->_system->updateScreen(); } Common::Event event; - while (_vm->getSystem()->getEventManager()->pollEvent(event)) + while (_vm->_system->getEventManager()->pollEvent(event)) if ((event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_ESCAPE)) { _vm->dirtyAllScreen(); return false; } - _vm->getSystem()->delayMillis(10); + _vm->_system->delayMillis(10); } _vm->dirtyAllScreen(); return !_vm->shouldQuit(); diff --git a/engines/toon/movie.h b/engines/toon/movie.h index 2cd33302f2..e795182cba 100644 --- a/engines/toon/movie.h +++ b/engines/toon/movie.h @@ -30,13 +30,17 @@ namespace Toon { class ToonstruckSmackerDecoder : public Video::SmackerDecoder { public: - ToonstruckSmackerDecoder(Audio::Mixer *mixer, Audio::Mixer::SoundType soundType = Audio::Mixer::kSFXSoundType); - virtual ~ToonstruckSmackerDecoder() {} - void handleAudioTrack(byte track, uint32 chunkSize, uint32 unpackedSize); - bool loadFile(const Common::String &filename); + ToonstruckSmackerDecoder(); + + bool loadStream(Common::SeekableReadStream *stream); bool isLowRes() { return _lowRes; } + protected: - bool _lowRes; + void handleAudioTrack(byte track, uint32 chunkSize, uint32 unpackedSize); + SmackerVideoTrack *createVideoTrack(uint32 width, uint32 height, uint32 frameCount, const Common::Rational &frameRate, uint32 flags, uint32 signature) const; + +private: + bool _lowRes; }; class Movie { diff --git a/engines/toon/picture.cpp b/engines/toon/picture.cpp index ff136e5acb..204b0fe576 100644 --- a/engines/toon/picture.cpp +++ b/engines/toon/picture.cpp @@ -150,7 +150,7 @@ void Picture::setupPalette() { _vm->setPaletteEntries(_palette, 1, 128); } -void Picture::drawMask(Graphics::Surface &surface, int32 x, int32 y, int32 dx, int32 dy) { +void Picture::drawMask(Graphics::Surface &surface, int16 x, int16 y, int16 dx, int16 dy) { debugC(1, kDebugPicture, "drawMask(surface, %d, %d, %d, %d)", x, y, dx, dy); for (int32 i = 0; i < 128; i++) { @@ -161,8 +161,8 @@ void Picture::drawMask(Graphics::Surface &surface, int32 x, int32 y, int32 dx, i _vm->setPaletteEntries(color, i, 1); } - int32 rx = MIN(_width, surface.w - x); - int32 ry = MIN(_height, surface.h - y); + int16 rx = MIN<int16>(_width, surface.w - x); + int16 ry = MIN<int16>(_height, surface.h - y); if (rx < 0 || ry < 0) return; @@ -172,10 +172,10 @@ void Picture::drawMask(Graphics::Surface &surface, int32 x, int32 y, int32 dx, i uint8 *c = _data + _width * dy + dx; uint8 *curRow = (uint8 *)surface.pixels + y * destPitch + x; - for (int32 yy = 0; yy < ry; yy++) { + for (int16 yy = 0; yy < ry; yy++) { uint8 *curSrc = c; uint8 *cur = curRow; - for (int32 xx = 0; xx < rx; xx++) { + for (int16 xx = 0; xx < rx; xx++) { //*cur = (*curSrc >> 5) * 8; // & 0x1f; *cur = (*curSrc & 0x1f) ? 127 : 0; @@ -187,10 +187,9 @@ void Picture::drawMask(Graphics::Surface &surface, int32 x, int32 y, int32 dx, i } } -void Picture::drawWithRectList(Graphics::Surface& surface, int32 x, int32 y, int32 dx, int32 dy, Common::Array<Common::Rect>& rectArray) { - - int32 rx = MIN(_width, surface.w - x); - int32 ry = MIN(_height, surface.h - y); +void Picture::drawWithRectList(Graphics::Surface& surface, int16 x, int16 y, int16 dx, int16 dy, Common::Array<Common::Rect>& rectArray) { + int16 rx = MIN<int16>(_width, surface.w - x); + int16 ry = MIN<int16>(_height, surface.h - y); if (rx < 0 || ry < 0) return; @@ -202,16 +201,16 @@ void Picture::drawWithRectList(Graphics::Surface& surface, int32 x, int32 y, int Common::Rect rect = rectArray[i]; - int32 fillRx = MIN<int32>(rx, rect.right - rect.left); - int32 fillRy = MIN<int32>(ry, rect.bottom - rect.top); + int16 fillRx = MIN<int32>(rx, rect.right - rect.left); + int16 fillRy = MIN<int32>(ry, rect.bottom - rect.top); uint8 *c = _data + _width * (dy + rect.top) + (dx + rect.left); uint8 *curRow = (uint8 *)surface.pixels + (y + rect.top) * destPitch + (x + rect.left); - for (int32 yy = 0; yy < fillRy; yy++) { + for (int16 yy = 0; yy < fillRy; yy++) { uint8 *curSrc = c; uint8 *cur = curRow; - for (int32 xx = 0; xx < fillRx; xx++) { + for (int16 xx = 0; xx < fillRx; xx++) { *cur = *curSrc; curSrc++; cur++; @@ -222,11 +221,11 @@ void Picture::drawWithRectList(Graphics::Surface& surface, int32 x, int32 y, int } } -void Picture::draw(Graphics::Surface &surface, int32 x, int32 y, int32 dx, int32 dy) { +void Picture::draw(Graphics::Surface &surface, int16 x, int16 y, int16 dx, int16 dy) { debugC(6, kDebugPicture, "draw(surface, %d, %d, %d, %d)", x, y, dx, dy); - int32 rx = MIN(_width, surface.w - x); - int32 ry = MIN(_height, surface.h - y); + int16 rx = MIN<int16>(_width, surface.w - x); + int16 ry = MIN<int16>(_height, surface.h - y); if (rx < 0 || ry < 0) return; @@ -236,10 +235,10 @@ void Picture::draw(Graphics::Surface &surface, int32 x, int32 y, int32 dx, int32 uint8 *c = _data + _width * dy + dx; uint8 *curRow = (uint8 *)surface.pixels + y * destPitch + x; - for (int32 yy = 0; yy < ry; yy++) { + for (int16 yy = 0; yy < ry; yy++) { uint8 *curSrc = c; uint8 *cur = curRow; - for (int32 xx = 0; xx < rx; xx++) { + for (int16 xx = 0; xx < rx; xx++) { *cur = *curSrc; curSrc++; cur++; @@ -249,7 +248,7 @@ void Picture::draw(Graphics::Surface &surface, int32 x, int32 y, int32 dx, int32 } } -uint8 Picture::getData(int32 x, int32 y) { +uint8 Picture::getData(int16 x, int16 y) { debugC(6, kDebugPicture, "getData(%d, %d)", x, y); if (!_data) @@ -259,7 +258,7 @@ uint8 Picture::getData(int32 x, int32 y) { } // use original work from johndoe -void Picture::floodFillNotWalkableOnMask(int32 x, int32 y) { +void Picture::floodFillNotWalkableOnMask(int16 x, int16 y) { debugC(1, kDebugPicture, "floodFillNotWalkableOnMask(%d, %d)", x, y); // Stack-based floodFill algorithm based on // http://student.kuleuven.be/~m0216922/CG/files/floodfill.cpp @@ -292,10 +291,10 @@ void Picture::floodFillNotWalkableOnMask(int32 x, int32 y) { } } -void Picture::drawLineOnMask(int32 x, int32 y, int32 x2, int32 y2, bool walkable) { +void Picture::drawLineOnMask(int16 x, int16 y, int16 x2, int16 y2, bool walkable) { debugC(1, kDebugPicture, "drawLineOnMask(%d, %d, %d, %d, %d)", x, y, x2, y2, (walkable) ? 1 : 0); - static int32 lastX = 0; - static int32 lastY = 0; + static int16 lastX = 0; + static int16 lastY = 0; if (x == -1) { x = lastX; @@ -303,12 +302,12 @@ void Picture::drawLineOnMask(int32 x, int32 y, int32 x2, int32 y2, bool walkable } uint32 bx = x << 16; - int32 dx = x2 - x; + int16 dx = x2 - x; uint32 by = y << 16; - int32 dy = y2 - y; - uint32 adx = abs(dx); - uint32 ady = abs(dy); - int32 t = 0; + int16 dy = y2 - y; + uint16 adx = abs(dx); + uint16 ady = abs(dy); + int16 t = 0; if (adx <= ady) t = ady; else @@ -317,9 +316,7 @@ void Picture::drawLineOnMask(int32 x, int32 y, int32 x2, int32 y2, bool walkable int32 cdx = (dx << 16) / t; int32 cdy = (dy << 16) / t; - int32 i = t; - while (i) { - + for (int16 i = t; i > 0; i--) { int32 rx = bx >> 16; int32 ry = by >> 16; @@ -337,7 +334,6 @@ void Picture::drawLineOnMask(int32 x, int32 y, int32 x2, int32 y2, bool walkable bx += cdx; by += cdy; - i--; } } } // End of namespace Toon diff --git a/engines/toon/picture.h b/engines/toon/picture.h index 460c5b58a2..5e79612a39 100644 --- a/engines/toon/picture.h +++ b/engines/toon/picture.h @@ -33,25 +33,27 @@ namespace Toon { class ToonEngine; -class Picture { +class Picture { public: Picture(ToonEngine *vm); ~Picture(); + bool loadPicture(const Common::String &file); void setupPalette(); - void draw(Graphics::Surface &surface, int32 x, int32 y, int32 dx, int32 dy); - void drawWithRectList(Graphics::Surface& surface, int32 x, int32 y, int32 dx, int32 dy, Common::Array<Common::Rect>& rectArray); - void drawMask(Graphics::Surface &surface, int32 x, int32 y, int32 dx, int32 dy); - void drawLineOnMask(int32 x, int32 y, int32 x2, int32 y2, bool walkable); - void floodFillNotWalkableOnMask(int32 x, int32 y); - uint8 getData(int32 x, int32 y); + void draw(Graphics::Surface &surface, int16 x, int16 y, int16 dx, int16 dy); + void drawWithRectList(Graphics::Surface& surface, int16 x, int16 y, int16 dx, int16 dy, Common::Array<Common::Rect>& rectArray); + void drawMask(Graphics::Surface &surface, int16 x, int16 y, int16 dx, int16 dy); + void drawLineOnMask(int16 x, int16 y, int16 x2, int16 y2, bool walkable); + void floodFillNotWalkableOnMask(int16 x, int16 y); + uint8 getData(int16 x, int16 y); uint8 *getDataPtr() { return _data; } - int32 getWidth() const { return _width; } - int32 getHeight() const { return _height; } + int16 getWidth() const { return _width; } + int16 getHeight() const { return _height; } + protected: - int32 _width; - int32 _height; + int16 _width; + int16 _height; uint8 *_data; uint8 *_palette; // need to be copied at 3-387 int32 _paletteEntries; diff --git a/engines/toon/script_func.cpp b/engines/toon/script_func.cpp index e9b7534198..1fa4058114 100644 --- a/engines/toon/script_func.cpp +++ b/engines/toon/script_func.cpp @@ -564,9 +564,9 @@ int32 ScriptFunc::sys_Cmd_Exit_Conversation(EMCState *state) { int32 ScriptFunc::sys_Cmd_Set_Mouse_Pos(EMCState *state) { if (_vm->state()->_inCloseUp) { - _vm->getSystem()->warpMouse(stackPos(0), stackPos(1)); + _vm->_system->warpMouse(stackPos(0), stackPos(1)); } else { - _vm->getSystem()->warpMouse(stackPos(0) - _vm->state()->_currentScrollValue, stackPos(1)); + _vm->_system->warpMouse(stackPos(0) - _vm->state()->_currentScrollValue, stackPos(1)); } return 0; } diff --git a/engines/toon/toon.cpp b/engines/toon/toon.cpp index 416daa1fe8..9fd8415676 100644 --- a/engines/toon/toon.cpp +++ b/engines/toon/toon.cpp @@ -51,7 +51,7 @@ void ToonEngine::init() { _currentScriptRegion = 0; _resources = new Resources(this); _animationManager = new AnimationManager(this); - _moviePlayer = new Movie(this, new ToonstruckSmackerDecoder(_mixer)); + _moviePlayer = new Movie(this, new ToonstruckSmackerDecoder()); _hotspots = new Hotspots(this); _mainSurface = new Graphics::Surface(); @@ -820,7 +820,6 @@ Common::Error ToonEngine::run() { ToonEngine::ToonEngine(OSystem *syst, const ADGameDescription *gameDescription) : Engine(syst), _gameDescription(gameDescription), _language(gameDescription->language), _rnd("toon") { - _system = syst; _tickLength = 16; _currentPicture = NULL; _inventoryPicture = NULL; @@ -1224,7 +1223,7 @@ void ToonEngine::loadScene(int32 SceneId, bool forGameLoad) { _script->init(&_sceneAnimationScripts[i]._state, _sceneAnimationScripts[i]._data); if (!forGameLoad) { _script->start(&_sceneAnimationScripts[i]._state, 9 + i); - _sceneAnimationScripts[i]._lastTimer = getSystem()->getMillis(); + _sceneAnimationScripts[i]._lastTimer = _system->getMillis(); _sceneAnimationScripts[i]._frozen = false; _sceneAnimationScripts[i]._frozenForConversation = false; } @@ -1588,12 +1587,12 @@ void ToonEngine::clickEvent() { } void ToonEngine::selectHotspot() { - int32 x1 = 0; - int32 x2 = 0; - int32 y1 = 0; - int32 y2 = 0; + int16 x1 = 0; + int16 x2 = 0; + int16 y1 = 0; + int16 y2 = 0; - int32 mouseX = _mouseX; + int16 mouseX = _mouseX; if (_gameState->_inCutaway) mouseX += TOON_BACKBUFFER_WIDTH; @@ -1693,7 +1692,6 @@ void ToonEngine::selectHotspot() { } void ToonEngine::exitScene() { - fadeOut(5); // disable all scene animation @@ -2831,7 +2829,6 @@ void ToonEngine::playSoundWrong() { } void ToonEngine::getTextPosition(int32 characterId, int32 *retX, int32 *retY) { - if (characterId < 0) characterId = 0; @@ -2852,8 +2849,8 @@ void ToonEngine::getTextPosition(int32 characterId, int32 *retX, int32 *retY) { } } else if (characterId == 1) { // flux - int32 x = _flux->getX(); - int32 y = _flux->getY(); + int16 x = _flux->getX(); + int16 y = _flux->getY(); if (x >= _gameState->_currentScrollValue && x <= _gameState->_currentScrollValue + TOON_SCREEN_WIDTH) { if (!_gameState->_inCutaway) { *retX = x; @@ -2885,7 +2882,7 @@ void ToonEngine::getTextPosition(int32 characterId, int32 *retX, int32 *retY) { if (character && !_gameState->_inCutaway) { if (character->getAnimationInstance()) { if (character->getX() >= _gameState->_currentScrollValue && character->getX() <= _gameState->_currentScrollValue + TOON_SCREEN_WIDTH) { - int32 x1, y1, x2, y2; + int16 x1, y1, x2, y2; character->getAnimationInstance()->getRect(&x1, &y1, &x2, &y2); *retX = (x1 + x2) / 2; *retY = y1; @@ -2896,7 +2893,6 @@ void ToonEngine::getTextPosition(int32 characterId, int32 *retX, int32 *retY) { } Character *ToonEngine::getCharacterById(int32 charId) { - for (int32 i = 0; i < 8; i++) { if (_characters[i] && _characters[i]->getId() == charId) return _characters[i]; diff --git a/engines/toon/toon.h b/engines/toon/toon.h index 540f3e403b..d40c489011 100644 --- a/engines/toon/toon.h +++ b/engines/toon/toon.h @@ -289,10 +289,6 @@ public: return _oldTimer2; } - OSystem *getSystem() { - return _system; - } - AudioManager *getAudioManager() { return _audioManager; } @@ -340,7 +336,6 @@ public: void clearDirtyRects(); protected: - OSystem *_system; int32 _tickLength; Resources *_resources; TextResource *_genericTexts; diff --git a/engines/touche/detection.cpp b/engines/touche/detection.cpp index 35dd54776f..e4bbe0c4c1 100644 --- a/engines/touche/detection.cpp +++ b/engines/touche/detection.cpp @@ -135,19 +135,13 @@ public: } virtual const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const { - const ADGameDescription *matchedDesc = detectGameFilebased(allFiles, Touche::fileBasedFallback); - - if (matchedDesc) { // We got a match - Common::String report = Common::String::format(_("Your game version has been detected using " - "filename matching as a variant of %s."), matchedDesc->gameid); - report += "\n"; - report += _("If this is an original and unmodified version, please report any"); - report += "\n"; - report += _("information previously printed by ScummVM to the team."); - report += "\n"; - g_system->logMessage(LogMessageType::kInfo, report.c_str()); - } + ADFilePropertiesMap filesProps; + + const ADGameDescription *matchedDesc = detectGameFilebased(allFiles, fslist, Touche::fileBasedFallback, &filesProps); + if (!matchedDesc) + return 0; + reportUnknown(fslist.begin()->getParent(), filesProps); return matchedDesc; } diff --git a/engines/tsage/blue_force/blueforce_dialogs.cpp b/engines/tsage/blue_force/blueforce_dialogs.cpp index a76d5839a9..23701c9e5b 100644 --- a/engines/tsage/blue_force/blueforce_dialogs.cpp +++ b/engines/tsage/blue_force/blueforce_dialogs.cpp @@ -88,7 +88,7 @@ RightClickDialog::~RightClickDialog() { void RightClickDialog::draw() { // Save the covered background area - _savedArea = Surface_getArea(g_globals->_gfxManagerInstance.getSurface(), _bounds); + _savedArea = surfaceGetArea(g_globals->_gfxManagerInstance.getSurface(), _bounds); // Draw the dialog image g_globals->gfxManager().copyFrom(_surface, _bounds.left, _bounds.top); @@ -323,7 +323,7 @@ void AmmoBeltDialog::draw() { if (!_savedArea) { // Save the covered background area - _savedArea = Surface_getArea(g_globals->_gfxManagerInstance.getSurface(), _bounds); + _savedArea = surfaceGetArea(g_globals->_gfxManagerInstance.getSurface(), _bounds); } else { bounds.moveTo(0, 0); } diff --git a/engines/tsage/blue_force/blueforce_scenes9.cpp b/engines/tsage/blue_force/blueforce_scenes9.cpp index 2178f31b30..1cb8191640 100644 --- a/engines/tsage/blue_force/blueforce_scenes9.cpp +++ b/engines/tsage/blue_force/blueforce_scenes9.cpp @@ -794,12 +794,12 @@ bool Scene910::Lyle::startAction(CursorType action, Event &event) { Scene910 *scene = (Scene910 *)BF_GLOBALS._sceneManager._scene; if (action == CURSOR_USE) { - if (BF_GLOBALS._v4CEE2 == 0) + if (BF_GLOBALS._nico910State == 0) return NamedObject::startAction(action, event); else return false; } else if (action == CURSOR_TALK) { - if ((BF_GLOBALS._hiddenDoorStatus != 0) || (BF_GLOBALS._v4CEE2 != 0)) { + if ((BF_GLOBALS._hiddenDoorStatus != 0) || (BF_GLOBALS._nico910State != 0)) { scene->_stripManager.start(9100 + _field90, &BF_GLOBALS._stripProxy); if (_field90 < 1) _field90++; @@ -833,7 +833,7 @@ bool Scene910::Nico::startAction(CursorType action, Event &event) { return true; break; case CURSOR_TALK: - if (BF_GLOBALS._v4CEE2 >= 4) + if (BF_GLOBALS._nico910State >= 4) return NamedObject::startAction(action, event); if (BF_GLOBALS._v4CEE6 < 4) @@ -847,8 +847,8 @@ bool Scene910::Nico::startAction(CursorType action, Event &event) { return true; break; case INV_COLT45: - if (BF_GLOBALS._v4CEE2 > 1) { - if (BF_GLOBALS._v4CEE2 != 4) { + if (BF_GLOBALS._nico910State > 1) { + if (BF_GLOBALS._nico910State != 4) { if ((BF_GLOBALS.getFlag(gunDrawn)) && (BF_GLOBALS.getFlag(fGunLoaded)) && (BF_GLOBALS.getHasBullets())) { if (scene->_field2DE0 == 0) { BF_GLOBALS._player.disableControl(); @@ -880,7 +880,7 @@ bool Scene910::Nico::startAction(CursorType action, Event &event) { break; case INV_BADGE: case INV_ID: - if (BF_GLOBALS._v4CEE2 >= 4) + if (BF_GLOBALS._nico910State >= 4) return NamedObject::startAction(action, event); if (BF_GLOBALS._v4CEE6 < 4) @@ -895,11 +895,12 @@ bool Scene910::Nico::startAction(CursorType action, Event &event) { return true; break; case INV_YELLOW_CORD: - if (BF_GLOBALS._v4CEE2 < 4) { + if (BF_GLOBALS._nico910State < 4) { BF_GLOBALS._player.disableControl(); scene->_yellowCord.fixPriority(121); scene->_sceneSubMode = 10; scene->_sceneMode = 9123; + BF_GLOBALS._nico910State = 3; if (BF_GLOBALS._player._visage == 1911) scene->setAction(&scene->_sequenceManager1, scene, 9123, &BF_GLOBALS._player, NULL); else @@ -995,7 +996,7 @@ bool Scene910::Stuart::startAction(CursorType action, Event &event) { return true; } else { BF_GLOBALS._player.disableControl(); - if (BF_GLOBALS._v4CEE2 == 4) { + if (BF_GLOBALS._nico910State == 4) { scene->_sceneSubMode = 11; scene->_sceneMode = 9123; if (BF_GLOBALS._player._visage == 1911) @@ -1136,7 +1137,7 @@ bool Scene910::BreakerBox::startAction(CursorType action, Event &event) { SceneItem::display2(910, 62); return true; } else if (scene->_sceneMode != 9120) { - if (BF_GLOBALS._v4CEE2 == 1) { + if (BF_GLOBALS._nico910State == 1) { BF_GLOBALS._player.disableControl(); scene->_sceneMode = 9118; scene->setAction(&scene->_sequenceManager1, scene, 9118, &BF_GLOBALS._player, &scene->_nico, NULL); @@ -1291,7 +1292,7 @@ bool Scene910::Object13::startAction(CursorType action, Event &event) { switch (_state) { case 1: - if (BF_GLOBALS._v4CEE2 < 1) { + if (BF_GLOBALS._nico910State < 1) { if (_frame == 2) { if (!BF_GLOBALS.getFlag(fGotPointsForClosingDoor)) { T2_GLOBALS._uiElements.addScore(30); @@ -1299,7 +1300,7 @@ bool Scene910::Object13::startAction(CursorType action, Event &event) { } scene->_sceneMode = 0; if (BF_GLOBALS._dayNumber == 5) { - if (BF_GLOBALS._v4CEE2 == 0) { + if (BF_GLOBALS._nico910State == 0) { scene->_breakerBoxInset.remove(); // _objectList.draw(); BF_GLOBALS._player.disableControl(); @@ -1309,7 +1310,7 @@ bool Scene910::Object13::startAction(CursorType action, Event &event) { scene->_nico.postInit(); scene->_sceneMode = 9129; scene->setAction(&scene->_sequenceManager1, scene, 9129, &BF_GLOBALS._player, &scene->_nico, NULL); - } else if (BF_GLOBALS._v4CEE2 == 2) { + } else if (BF_GLOBALS._nico910State == 2) { scene->_breakerBoxInset.remove(); // _objectList.draw(); BF_GLOBALS._player.disableControl(); @@ -1612,7 +1613,7 @@ bool Scene910::BlackPlug::startAction(CursorType action, Event &event) { SET_EXT_FGCOLOR, 13, LIST_END); return true; } - if (BF_GLOBALS._v4CEE2 == 3) { + if (BF_GLOBALS._nico910State == 3) { SceneItem::display(910, 84, SET_WIDTH, 312, SET_X, GLOBALS._sceneManager._scene->_sceneBounds.left + 4, SET_Y, GLOBALS._sceneManager._scene->_sceneBounds.top + UI_INTERFACE_Y + 2, @@ -1830,7 +1831,7 @@ bool Scene910::Generator::startAction(CursorType action, Event &event) { SET_Y, GLOBALS._sceneManager._scene->_sceneBounds.top + UI_INTERFACE_Y + 2, SET_FONT, 4, SET_BG_COLOR, 1, SET_FG_COLOR, 19, SET_EXT_BGCOLOR, 9, SET_EXT_FGCOLOR, 13, LIST_END); - else if (BF_GLOBALS._v4CEE2 == 1) { + else if (BF_GLOBALS._nico910State == 1) { BF_GLOBALS._player.disableControl(); scene->_sceneMode = 9118; scene->setAction(&scene->_sequenceManager1, scene, 9118, &BF_GLOBALS._player, &scene->_nico, NULL); @@ -1869,7 +1870,7 @@ bool Scene910::Item2::startAction(CursorType action, Event &event) { bool Scene910::Item3::startAction(CursorType action, Event &event) { Scene910 *scene = (Scene910 *)BF_GLOBALS._sceneManager._scene; - if ((action == CURSOR_TALK) && (BF_GLOBALS._v4CEE2 == 4) && (BF_GLOBALS._v4CEE4 == 0)) { + if ((action == CURSOR_TALK) && (BF_GLOBALS._nico910State == 4) && (BF_GLOBALS._v4CEE4 == 0)) { BF_GLOBALS._player.disableControl(); scene->_sceneMode = 15; scene->_stripManager.start(9102, scene); @@ -1907,7 +1908,7 @@ bool Scene910::Item15::startAction(CursorType action, Event &event) { bool Scene910::Item16::startAction(CursorType action, Event &event) { Scene910 *scene = (Scene910 *)BF_GLOBALS._sceneManager._scene; - if ((BF_GLOBALS._hiddenDoorStatus == 0) || (BF_GLOBALS._v4CEE2 != 0)) + if ((BF_GLOBALS._hiddenDoorStatus == 0) || (BF_GLOBALS._nico910State != 0)) return false; if (BF_GLOBALS._player._visage == 1911) { @@ -2016,7 +2017,7 @@ void Scene910::postInit(SceneObjectList *OwnerList) { if (BF_GLOBALS._dayNumber < 5) _item17.setDetails(Rect(0, 149, 29, 167), 910, -1, -1, -1, 1, NULL); - if (BF_GLOBALS._v4CEE2 == 0) + if (BF_GLOBALS._nico910State == 0) _item16.setDetails(Rect(265, 18, 319, 102), 910, -1, -1, -1, 1, NULL); _breakerBox.setDetails(910, 6, -1, -1, 1, (SceneItem *)NULL); @@ -2048,7 +2049,7 @@ void Scene910::postInit(SceneObjectList *OwnerList) { || (BF_GLOBALS._sceneManager._previousScene == 190) || (BF_GLOBALS._sceneManager._previousScene == 300)) { BF_GLOBALS._sceneManager._previousScene = 900; - BF_GLOBALS._v4CEE2 = 0; + BF_GLOBALS._nico910State = 0; BF_GLOBALS._v4CEE4 = 0; } @@ -2142,7 +2143,7 @@ void Scene910::postInit(SceneObjectList *OwnerList) { _nico.setPosition(Common::Point(262, 124)); _nico.setStrip(6); BF_GLOBALS._v4CEE6 = 0; - BF_GLOBALS._v4CEE2 = 1; + BF_GLOBALS._nico910State = 1; _nico.setDetails(910, 63, 64, 67, 5, &_item4); BF_GLOBALS._v4CECA = 2; if (BF_GLOBALS._v4CECC == 0) @@ -2157,7 +2158,7 @@ void Scene910::postInit(SceneObjectList *OwnerList) { BF_GLOBALS._player.disableControl(); } - if ((BF_GLOBALS._dayNumber == 5) && (BF_GLOBALS._v4CEE2 == 0)){ + if ((BF_GLOBALS._dayNumber == 5) && (BF_GLOBALS._nico910State == 0)){ _shadow.postInit(); _shadow.setAction(&_action2); } @@ -2297,7 +2298,7 @@ void Scene910::signal() { case 13: BF_GLOBALS._player.disableControl(); BF_GLOBALS._player.setAction(&_sequenceManager2, NULL, 9117, &_nico, NULL); - BF_GLOBALS._v4CEE2 = 2; + BF_GLOBALS._nico910State = 2; // No break on purpose case 15: _stuart.postInit(); @@ -2314,7 +2315,7 @@ void Scene910::signal() { _lyle._field90 = 1; _sceneMode = 10; addFader((const byte *)&black, 2, this); - BF_GLOBALS._v4CEE2 = 1; + BF_GLOBALS._nico910State = 1; BF_GLOBALS._walkRegions.disableRegion(16); BF_GLOBALS._walkRegions.disableRegion(14); BF_GLOBALS._sceneItems.remove(&_item16); @@ -2324,7 +2325,7 @@ void Scene910::signal() { BF_GLOBALS._player._frame = 1; if (_field2DE2 == 0) { _field2DE2 = 1; - if (BF_GLOBALS._v4CEE2 == 4) { + if (BF_GLOBALS._nico910State == 4) { _sceneMode = 9149; setAction(&_sequenceManager1, this, 9149, &BF_GLOBALS._player, NULL); } else { @@ -2452,7 +2453,7 @@ void Scene910::signal() { break; case 9114: _fakeWall.hide(); - if ((BF_GLOBALS._dayNumber == 5) && (BF_GLOBALS._v4CEE2 == 0)) { + if ((BF_GLOBALS._dayNumber == 5) && (BF_GLOBALS._nico910State == 0)) { BF_GLOBALS._player.disableControl(); _nico.postInit(); _nico.setDetails(910, 63, 64, 65, 5, &_item4); @@ -2496,7 +2497,7 @@ void Scene910::signal() { case 9121: _item3.setDetails(7, 910, 96, 60, 61, 3); BF_GLOBALS._v4CEE4 = 2; - if (BF_GLOBALS._v4CEE2 == 4) { + if (BF_GLOBALS._nico910State == 4) { _sceneMode = 20; _stripManager.start(9115, this); } else { @@ -2527,7 +2528,7 @@ void Scene910::signal() { setAction(&_sequenceManager1, this, 9111, &BF_GLOBALS._player, &_blackCord, NULL); break; case 5: - switch (BF_GLOBALS._v4CEE2 - 1) { + switch (BF_GLOBALS._nico910State - 1) { case 0: _sceneMode = 9118; setAction(&_sequenceManager1, this, 9118, &BF_GLOBALS._player, &_nico, NULL); @@ -2598,7 +2599,7 @@ void Scene910::signal() { break; case 9125: BF_GLOBALS.setFlag(fBackupAt340); - BF_GLOBALS._v4CEE2 = 4; + BF_GLOBALS._nico910State = 4; _stuart.postInit(); _nico.setDetails(910, 72, 73, 74, 3, (SceneItem *)NULL); _stuart.setDetails(910, 66, 67, 68, 5, &_nico); @@ -2645,7 +2646,7 @@ void Scene910::signal() { } _lyle.setAction(&_sequenceManager2, NULL, 9131, &_lyle, NULL); BF_GLOBALS._walkRegions.enableRegion(16); - if (BF_GLOBALS._v4CEE2 == 4) + if (BF_GLOBALS._nico910State == 4) BF_INVENTORY.setObjectScene(INV_YELLOW_CORD, 0); else BF_INVENTORY.setObjectScene(INV_HALF_YELLOW_CORD, 910); @@ -2679,7 +2680,7 @@ void Scene910::signal() { } break; case 9143: - if (BF_GLOBALS._v4CEE2 == 0) { + if (BF_GLOBALS._nico910State == 0) { BF_GLOBALS._v51C44 = 1; BF_GLOBALS._sceneManager.changeScene(920); } else { @@ -2730,7 +2731,7 @@ void Scene910::process(Event &event) { if (_item17._bounds.contains(event.mousePos)) { GfxSurface surface = _cursorVisage.getFrame(EXITFRAME_SW); BF_GLOBALS._events.setCursor(surface); - } else if ((BF_GLOBALS._hiddenDoorStatus == 0) || (BF_GLOBALS._v4CEE2 != 0)) { + } else if ((BF_GLOBALS._hiddenDoorStatus == 0) || (BF_GLOBALS._nico910State != 0)) { CursorType cursorId = BF_GLOBALS._events.getCursor(); BF_GLOBALS._events.setCursor(cursorId); } else if (!_item16._bounds.contains(event.mousePos)) { @@ -2755,7 +2756,7 @@ void Scene910::process(Event &event) { _sceneMode = 9123; setAction(&_sequenceManager1, this, 9123, &BF_GLOBALS._player, NULL); event.handled = true; - } else if (BF_GLOBALS._v4CEE2 <= 1) { + } else if (BF_GLOBALS._nico910State <= 1) { if (BF_GLOBALS.getFlag(fCanDrawGun)) { BF_GLOBALS._player.addMover(NULL); BF_GLOBALS._player.disableControl(); @@ -2776,7 +2777,7 @@ void Scene910::process(Event &event) { event.handled = true; break; case CURSOR_WALK: - if (BF_GLOBALS._v4CEE2 == 1) { + if (BF_GLOBALS._nico910State == 1) { BF_GLOBALS._player.disableControl(); if (BF_GLOBALS._player._visage == 1911) { BF_GLOBALS._player.disableControl(); @@ -2826,7 +2827,7 @@ void Scene910::dispatch() { _sceneSubMode = 3; _sceneMode = 9123; setAction(&_sequenceManager1, this, 9123, &BF_GLOBALS._player, NULL); - } else if (BF_GLOBALS._v4CEE2 == 0) { + } else if (BF_GLOBALS._nico910State == 0) { _sceneMode = 9143; setAction(&_sequenceManager1, this, 9143, &BF_GLOBALS._player, NULL); } else { @@ -2840,7 +2841,7 @@ void Scene910::dispatch() { } } - if ((BF_GLOBALS._dayNumber == 5) && (BF_GLOBALS._player._position.x > 250) && (_sceneMode != 9135) && (_sceneMode != 11) && (BF_GLOBALS._hiddenDoorStatus != 0) && (BF_GLOBALS._v4CEE2 == 0)) { + if ((BF_GLOBALS._dayNumber == 5) && (BF_GLOBALS._player._position.x > 250) && (_sceneMode != 9135) && (_sceneMode != 11) && (BF_GLOBALS._hiddenDoorStatus != 0) && (BF_GLOBALS._nico910State == 0)) { BF_GLOBALS._player.disableControl(); _shadow.remove(); _nico.remove(); @@ -2853,7 +2854,7 @@ void Scene910::dispatch() { } void Scene910::checkGun() { - if ((BF_GLOBALS._dayNumber == 5) && (BF_GLOBALS._v4CEE2 == 0) && (BF_GLOBALS._hiddenDoorStatus != 0)) + if ((BF_GLOBALS._dayNumber == 5) && (BF_GLOBALS._nico910State == 0) && (BF_GLOBALS._hiddenDoorStatus != 0)) SceneItem::display(910, 70, SET_WIDTH, 312, SET_X, GLOBALS._sceneManager._scene->_sceneBounds.left + 4, SET_Y, GLOBALS._sceneManager._scene->_sceneBounds.top + UI_INTERFACE_Y + 2, @@ -2900,7 +2901,7 @@ void Scene910::closeHiddenDoor() { setAction(&_sequenceManager1, this, 9115, &_fakeWall, &_object5, NULL); } - if ((BF_GLOBALS._dayNumber == 5) && (BF_GLOBALS._v4CEE2 == 0)) { + if ((BF_GLOBALS._dayNumber == 5) && (BF_GLOBALS._nico910State == 0)) { // _objectList.draw(); if (BF_GLOBALS._sceneObjects->contains(&_breakerBoxInset)) _breakerBoxInset.remove(); diff --git a/engines/tsage/converse.cpp b/engines/tsage/converse.cpp index 06fbffb751..ba27db9104 100644 --- a/engines/tsage/converse.cpp +++ b/engines/tsage/converse.cpp @@ -505,7 +505,7 @@ void ConversationChoiceDialog::draw() { // Make a backup copy of the area the dialog will occupy Rect tempRect = _bounds; tempRect.collapse(-10, -10); - _savedArea = Surface_getArea(g_globals->_gfxManagerInstance.getSurface(), tempRect); + _savedArea = surfaceGetArea(g_globals->_gfxManagerInstance.getSurface(), tempRect); // Fill in the contents of the entire dialog _gfxManager._bounds = Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); diff --git a/engines/tsage/detection.cpp b/engines/tsage/detection.cpp index 0c458f5c35..bcadfdc201 100644 --- a/engines/tsage/detection.cpp +++ b/engines/tsage/detection.cpp @@ -164,8 +164,6 @@ public: // Create the return descriptor SaveStateDescriptor desc(slot, header.saveName); - desc.setDeletableFlag(true); - desc.setWriteProtectedFlag(false); desc.setThumbnail(header.thumbnail); desc.setSaveDate(header.saveYear, header.saveMonth, header.saveDay); desc.setSaveTime(header.saveHour, header.saveMinutes); diff --git a/engines/tsage/detection_tables.h b/engines/tsage/detection_tables.h index d538cbacbf..a84ee5662f 100644 --- a/engines/tsage/detection_tables.h +++ b/engines/tsage/detection_tables.h @@ -105,7 +105,7 @@ static const tSageGameDescription gameDescriptions[] = { AD_ENTRY1s("blue.rlb", "17c3993415e8a2cf93040eef7e88ec93", 1156508), Common::EN_ANY, Common::kPlatformPC, - ADGF_TESTING, + ADGF_UNSTABLE, GUIO2(GUIO_NOSPEECH, GUIO_NOSFX) }, GType_BlueForce, @@ -120,7 +120,7 @@ static const tSageGameDescription gameDescriptions[] = { AD_ENTRY1s("blue.rlb", "17eabb456cb1546c66baf1aff387ba6a", 10032614), Common::EN_ANY, Common::kPlatformPC, - ADGF_TESTING, + ADGF_NO_FLAGS, GUIO2(GUIO_NOSPEECH, GUIO_NOSFX) }, GType_BlueForce, @@ -134,7 +134,7 @@ static const tSageGameDescription gameDescriptions[] = { AD_ENTRY1s("blue.rlb", "99983f48cb218f1f3760cf2f9a7ef11d", 63863322), Common::EN_ANY, Common::kPlatformPC, - ADGF_CD | ADGF_TESTING, + ADGF_CD, GUIO2(GUIO_NOSPEECH, GUIO_NOSFX) }, GType_BlueForce, @@ -150,7 +150,7 @@ static const tSageGameDescription gameDescriptions[] = { AD_ENTRY1s("blue.rlb", "5b2b35c51b62e82d82b0791540bfae2d", 10082565), Common::ES_ESP, Common::kPlatformPC, - ADGF_CD | ADGF_TESTING, + ADGF_CD | ADGF_UNSTABLE, GUIO2(GUIO_NOSPEECH, GUIO_NOSFX) }, GType_BlueForce, diff --git a/engines/tsage/dialogs.cpp b/engines/tsage/dialogs.cpp index 972d591c34..77ac0a25d7 100644 --- a/engines/tsage/dialogs.cpp +++ b/engines/tsage/dialogs.cpp @@ -116,7 +116,7 @@ void ModalDialog::draw() { // Make a backup copy of the area the dialog will occupy Rect tempRect = _bounds; tempRect.collapse(-10, -10); - _savedArea = Surface_getArea(g_globals->_gfxManagerInstance.getSurface(), tempRect); + _savedArea = surfaceGetArea(g_globals->_gfxManagerInstance.getSurface(), tempRect); _gfxManager.activate(); diff --git a/engines/tsage/globals.cpp b/engines/tsage/globals.cpp index 59eb59b194..de9463268b 100644 --- a/engines/tsage/globals.cpp +++ b/engines/tsage/globals.cpp @@ -247,7 +247,7 @@ void BlueForceGlobals::synchronize(Serializer &s) { for (int i = 0; i < 18; i++) s.syncAsByte(_breakerBoxStatusArr[i]); s.syncAsSint16LE(_hiddenDoorStatus); - s.syncAsSint16LE(_v4CEE2); + s.syncAsSint16LE(_nico910State); s.syncAsSint16LE(_v4CEE4); s.syncAsSint16LE(_v4CEE6); s.syncAsSint16LE(_v4CEE8); @@ -320,7 +320,7 @@ void BlueForceGlobals::reset() { _breakerBoxStatusArr[16] = 3; _breakerBoxStatusArr[17] = 0; _hiddenDoorStatus = 0; - _v4CEE2 = 0; + _nico910State = 0; _v4CEE4 = 0; _v4CEE6 = 0; _v4CEE8 = 0; diff --git a/engines/tsage/globals.h b/engines/tsage/globals.h index 45226c921b..d190b6a2a4 100644 --- a/engines/tsage/globals.h +++ b/engines/tsage/globals.h @@ -200,7 +200,7 @@ public: int _v4CECC; int8 _breakerBoxStatusArr[18]; int _hiddenDoorStatus; - int _v4CEE2; + int _nico910State; int _v4CEE4; int _v4CEE6; int _v4CEE8; diff --git a/engines/tsage/graphics.cpp b/engines/tsage/graphics.cpp index 0781ae4544..fb0b0b0cbb 100644 --- a/engines/tsage/graphics.cpp +++ b/engines/tsage/graphics.cpp @@ -38,7 +38,7 @@ namespace TsAGE { * @src Source surface * @bounds Area to backup */ -GfxSurface *Surface_getArea(GfxSurface &src, const Rect &bounds) { +GfxSurface *surfaceGetArea(GfxSurface &src, const Rect &bounds) { assert(bounds.isValidRect()); GfxSurface *dest = new GfxSurface(); dest->create(bounds.width(), bounds.height()); @@ -437,7 +437,7 @@ bool GfxSurface::displayText(const Common::String &msg, const Common::Point &pt) // Make a backup copy of the area the text will occupy Rect saveRect = textRect; saveRect.collapse(-20, -8); - GfxSurface *savedArea = Surface_getArea(gfxManager.getSurface(), saveRect); + GfxSurface *savedArea = surfaceGetArea(gfxManager.getSurface(), saveRect); // Display the text gfxManager._font.writeLines(msg.c_str(), textRect, ALIGN_LEFT); @@ -1073,7 +1073,7 @@ void GfxDialog::draw() { Rect tempRect(_bounds); // Make a backup copy of the area the dialog will occupy - _savedArea = Surface_getArea(g_globals->_gfxManagerInstance.getSurface(), _bounds); + _savedArea = surfaceGetArea(g_globals->_gfxManagerInstance.getSurface(), _bounds); // Set the palette for use in the dialog setPalette(); diff --git a/engines/tsage/graphics.h b/engines/tsage/graphics.h index 9c6f13e407..9175b1050a 100644 --- a/engines/tsage/graphics.h +++ b/engines/tsage/graphics.h @@ -339,7 +339,7 @@ public: static void setPalette(); }; -GfxSurface *Surface_getArea(GfxSurface &src, const Rect &bounds); +GfxSurface *surfaceGetArea(GfxSurface &src, const Rect &bounds); GfxSurface surfaceFromRes(const byte *imgData); GfxSurface surfaceFromRes(int resNum, int rlbNum, int subNum); diff --git a/engines/tsage/ringworld/ringworld_dialogs.cpp b/engines/tsage/ringworld/ringworld_dialogs.cpp index 0e451b8429..4728e66cd9 100644 --- a/engines/tsage/ringworld/ringworld_dialogs.cpp +++ b/engines/tsage/ringworld/ringworld_dialogs.cpp @@ -59,7 +59,7 @@ void RightClickButton::highlight() { _savedButton = NULL; } else { // Highlight button by getting the needed highlighted image resource - _savedButton = Surface_getArea(g_globals->gfxManager().getSurface(), _bounds); + _savedButton = surfaceGetArea(g_globals->gfxManager().getSurface(), _bounds); uint size; byte *imgData = g_resourceManager->getSubResource(7, 2, _buttonIndex, &size); @@ -122,7 +122,7 @@ RightClickButton *RightClickDialog::findButton(const Common::Point &pt) { void RightClickDialog::draw() { // Save the covered background area - _savedArea = Surface_getArea(g_globals->_gfxManagerInstance.getSurface(), _bounds); + _savedArea = surfaceGetArea(g_globals->_gfxManagerInstance.getSurface(), _bounds); // Draw the dialog image g_globals->gfxManager().copyFrom(_surface, _bounds.left, _bounds.top); diff --git a/engines/tsage/ringworld/ringworld_logic.cpp b/engines/tsage/ringworld/ringworld_logic.cpp index 00c219f2ee..7d571b40d7 100644 --- a/engines/tsage/ringworld/ringworld_logic.cpp +++ b/engines/tsage/ringworld/ringworld_logic.cpp @@ -295,7 +295,7 @@ void SceneArea::display() { _bounds.setWidth(_surface.getBounds().width()); _bounds.setHeight(_surface.getBounds().height()); - _savedArea = Surface_getArea(g_globals->_gfxManagerInstance.getSurface(), _bounds); + _savedArea = surfaceGetArea(g_globals->_gfxManagerInstance.getSurface(), _bounds); draw2(); } diff --git a/engines/tsage/ringworld/ringworld_scenes5.cpp b/engines/tsage/ringworld/ringworld_scenes5.cpp index 3b415bdb6a..004ccbbb6d 100644 --- a/engines/tsage/ringworld/ringworld_scenes5.cpp +++ b/engines/tsage/ringworld/ringworld_scenes5.cpp @@ -1893,7 +1893,7 @@ void Scene4045::postInit(SceneObjectList *OwnerList) { _olloFace.setStrip(4); _olloFace.fixPriority(152); - if(g_globals->_sceneManager._previousScene == 4050) { + if (g_globals->_sceneManager._previousScene == 4050) { g_globals->_soundHandler.play(155); g_globals->_player.setPosition(Common::Point(72, 128)); g_globals->_player.enableControl(); diff --git a/engines/tsage/ringworld2/ringworld2_dialogs.cpp b/engines/tsage/ringworld2/ringworld2_dialogs.cpp index 30ae6be7b1..478fdcf5a5 100644 --- a/engines/tsage/ringworld2/ringworld2_dialogs.cpp +++ b/engines/tsage/ringworld2/ringworld2_dialogs.cpp @@ -85,7 +85,7 @@ RightClickDialog::~RightClickDialog() { void RightClickDialog::draw() { // Save the covered background area - _savedArea = Surface_getArea(g_globals->_gfxManagerInstance.getSurface(), _bounds); + _savedArea = surfaceGetArea(g_globals->_gfxManagerInstance.getSurface(), _bounds); // Draw the dialog image g_globals->gfxManager().copyFrom(_surface, _bounds.left, _bounds.top); diff --git a/engines/tsage/ringworld2/ringworld2_speakers.h b/engines/tsage/ringworld2/ringworld2_speakers.h index e336564c5f..fa2946d56c 100644 --- a/engines/tsage/ringworld2/ringworld2_speakers.h +++ b/engines/tsage/ringworld2/ringworld2_speakers.h @@ -504,14 +504,14 @@ public: class SpeakerSoldier300 : public SpeakerSoldier { public: - SpeakerSoldier300() : SpeakerSoldier(60) {}; + SpeakerSoldier300() : SpeakerSoldier(60) {} virtual Common::String getClassName() { return "SpeakerSoldier300"; } virtual void proc15(); }; class SpeakerSoldier1625 : public SpeakerSoldier { public: - SpeakerSoldier1625() : SpeakerSoldier(5) {}; + SpeakerSoldier1625() : SpeakerSoldier(5) {} virtual Common::String getClassName() { return "SpeakerSoldier1625"; } }; @@ -585,7 +585,7 @@ public: class SpeakerWebbster3240 : public SpeakerWebbster { public: - SpeakerWebbster3240() : SpeakerWebbster(10) {}; + SpeakerWebbster3240() : SpeakerWebbster(10) {} virtual Common::String getClassName() { return "SpeakerWebbster3240"; } virtual void proc15(); @@ -593,7 +593,7 @@ public: class SpeakerWebbster3375 : public SpeakerWebbster { public: - SpeakerWebbster3375() : SpeakerWebbster(60) {}; + SpeakerWebbster3375() : SpeakerWebbster(60) {} virtual Common::String getClassName() { return "SpeakerWebbster3375"; } virtual void proc15(); @@ -601,7 +601,7 @@ public: class SpeakerWebbster3385 : public SpeakerWebbster { public: - SpeakerWebbster3385() : SpeakerWebbster(60) {}; + SpeakerWebbster3385() : SpeakerWebbster(60) {} virtual Common::String getClassName() { return "SpeakerWebbster3385"; } virtual void proc15(); @@ -609,7 +609,7 @@ public: class SpeakerWebbster3395 : public SpeakerWebbster { public: - SpeakerWebbster3395() : SpeakerWebbster(60) {}; + SpeakerWebbster3395() : SpeakerWebbster(60) {} virtual Common::String getClassName() { return "SpeakerWebbster3395"; } virtual void proc15(); @@ -617,7 +617,7 @@ public: class SpeakerWebbster3400 : public SpeakerWebbster { public: - SpeakerWebbster3400() : SpeakerWebbster(27) {}; + SpeakerWebbster3400() : SpeakerWebbster(27) {} virtual Common::String getClassName() { return "SpeakerWebbster3400"; } virtual void proc15(); diff --git a/engines/tsage/user_interface.cpp b/engines/tsage/user_interface.cpp index 10cb6961dc..4bd9e49875 100644 --- a/engines/tsage/user_interface.cpp +++ b/engines/tsage/user_interface.cpp @@ -112,7 +112,7 @@ void UIQuestion::showItem(int resNum, int rlbNum, int frameNum) { imgRect.center(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2); // Save the area behind where the image will be displayed - GfxSurface *savedArea = Surface_getArea(GLOBALS.gfxManager().getSurface(), imgRect); + GfxSurface *savedArea = surfaceGetArea(GLOBALS.gfxManager().getSurface(), imgRect); // Draw the image GLOBALS.gfxManager().copyFrom(objImage, imgRect); diff --git a/engines/tucker/sequences.cpp b/engines/tucker/sequences.cpp index 775fd6f1a0..16c4f4f6f0 100644 --- a/engines/tucker/sequences.cpp +++ b/engines/tucker/sequences.cpp @@ -28,6 +28,7 @@ #include "audio/decoders/wave.h" #include "graphics/palette.h" +#include "graphics/surface.h" #include "tucker/tucker.h" #include "tucker/graphics.h" @@ -749,6 +750,7 @@ void AnimationSequencePlayer::openAnimation(int index, const char *fileName) { _seqNum = 1; return; } + _flicPlayer[index].start(); _flicPlayer[index].decodeNextFrame(); if (index == 0) { getRGBPalette(index); @@ -801,7 +803,7 @@ void AnimationSequencePlayer::playIntroSeq19_20() { if (_flicPlayer[0].getCurFrame() >= 115) { surface = _flicPlayer[1].decodeNextFrame(); if (_flicPlayer[1].endOfVideo()) - _flicPlayer[1].reset(); + _flicPlayer[1].rewind(); } bool framesLeft = decodeNextAnimationFrame(0, false); |