diff options
Diffstat (limited to 'engines')
698 files changed, 39322 insertions, 15383 deletions
diff --git a/engines/access/access.cpp b/engines/access/access.cpp index 1855280a24..c1af19026b 100644 --- a/engines/access/access.cpp +++ b/engines/access/access.cpp @@ -488,11 +488,6 @@ Common::Error AccessEngine::loadGameState(int slot) { if (!readSavegameHeader(saveFile, header)) error("Invalid savegame"); - if (header._thumbnail) { - header._thumbnail->free(); - delete header._thumbnail; - } - // Load most of the savegame data synchronize(s); delete saveFile; @@ -537,9 +532,8 @@ void AccessEngine::synchronize(Common::Serializer &s) { const char *const SAVEGAME_STR = "ACCESS"; #define SAVEGAME_STR_SIZE 6 -bool AccessEngine::readSavegameHeader(Common::InSaveFile *in, AccessSavegameHeader &header) { +WARN_UNUSED_RESULT bool AccessEngine::readSavegameHeader(Common::InSaveFile *in, AccessSavegameHeader &header, bool skipThumbnail) { char saveIdentBuffer[SAVEGAME_STR_SIZE + 1]; - header._thumbnail = nullptr; // Validate the header Id in->read(saveIdentBuffer, SAVEGAME_STR_SIZE + 1); @@ -557,9 +551,9 @@ bool AccessEngine::readSavegameHeader(Common::InSaveFile *in, AccessSavegameHead header._saveName += ch; // Get the thumbnail - header._thumbnail = Graphics::loadThumbnail(*in); - if (!header._thumbnail) + if (!Graphics::loadThumbnail(*in, header._thumbnail, skipThumbnail)) { return false; + } // Read in save date/time header._year = in->readSint16LE(); diff --git a/engines/access/access.h b/engines/access/access.h index 972dd4c380..56646f01c9 100644 --- a/engines/access/access.h +++ b/engines/access/access.h @@ -306,7 +306,7 @@ public: /** * Read in a savegame header */ - static bool readSavegameHeader(Common::InSaveFile *in, AccessSavegameHeader &header); + WARN_UNUSED_RESULT static bool readSavegameHeader(Common::InSaveFile *in, AccessSavegameHeader &header, bool skipThumbnail = true); /** * Write out a savegame header diff --git a/engines/access/detection.cpp b/engines/access/detection.cpp index 3e70de3635..186fcbdf06 100644 --- a/engines/access/detection.cpp +++ b/engines/access/detection.cpp @@ -157,11 +157,9 @@ SaveStateList AccessMetaEngine::listSaves(const char *target) const { Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(*file); if (in) { - Access::AccessEngine::readSavegameHeader(in, header); - saveList.push_back(SaveStateDescriptor(slot, header._saveName)); + if (Access::AccessEngine::readSavegameHeader(in, header)) + saveList.push_back(SaveStateDescriptor(slot, header._saveName)); - header._thumbnail->free(); - delete header._thumbnail; delete in; } } @@ -187,7 +185,11 @@ SaveStateDescriptor AccessMetaEngine::querySaveMetaInfos(const char *target, int if (f) { Access::AccessSavegameHeader header; - Access::AccessEngine::readSavegameHeader(f, header); + if (!Access::AccessEngine::readSavegameHeader(f, header, false)) { + delete f; + return SaveStateDescriptor(); + } + delete f; // Create the return descriptor diff --git a/engines/access/scripts.cpp b/engines/access/scripts.cpp index 2cebc3e3c6..a0b4ef68ee 100644 --- a/engines/access/scripts.cpp +++ b/engines/access/scripts.cpp @@ -152,7 +152,7 @@ void Scripts::searchForSequence() { _data->seek(0); int sequenceId; do { - while (_data->readByte() != SCRIPT_START_BYTE) ; + while (_data->readByte() != SCRIPT_START_BYTE) {} sequenceId = _data->readUint16LE(); } while (sequenceId != _sequence); } diff --git a/engines/adl/detection.cpp b/engines/adl/detection.cpp index e63beb2c07..2276576baa 100644 --- a/engines/adl/detection.cpp +++ b/engines/adl/detection.cpp @@ -332,9 +332,9 @@ public: int getMaximumSaveSlot() const { return 'O' - 'A'; } SaveStateList listSaves(const char *target) const; void removeSaveState(const char *target, int slot) const; - virtual ADGameDescList detectGame(const Common::FSNode &parent, const FileMap &allFiles, Common::Language language, Common::Platform platform, const Common::String &extra) const; + ADDetectedGames detectGame(const Common::FSNode &parent, const FileMap &allFiles, Common::Language language, Common::Platform platform, const Common::String &extra) const override; - bool addFileProps(const FileMap &allFiles, Common::String fname, ADFilePropertiesMap &filePropsMap) const; + bool addFileProps(const FileMap &allFiles, Common::String fname, FilePropertiesMap &filePropsMap) const; bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const; }; @@ -401,7 +401,11 @@ SaveStateDescriptor AdlMetaEngine::querySaveMetaInfos(const char *target, int sl return SaveStateDescriptor(); } - Graphics::Surface *const thumbnail = Graphics::loadThumbnail(*inFile); + Graphics::Surface *thumbnail; + if (!Graphics::loadThumbnail(*inFile, thumbnail)) { + delete inFile; + return SaveStateDescriptor(); + } sd.setThumbnail(thumbnail); delete inFile; @@ -488,14 +492,14 @@ Common::Platform getPlatform(const AdlGameDescription &adlDesc) { return adlDesc.desc.platform; } -bool AdlMetaEngine::addFileProps(const FileMap &allFiles, Common::String fname, ADFilePropertiesMap &filePropsMap) const { +bool AdlMetaEngine::addFileProps(const FileMap &allFiles, Common::String fname, FilePropertiesMap &filePropsMap) const { if (filePropsMap.contains(fname)) return true; if (!allFiles.contains(fname)) return false; - ADFileProperties fileProps; + FileProperties fileProps; fileProps.size = computeMD5(allFiles[fname], fileProps.md5, 16384); if (fileProps.size != -1) { @@ -507,42 +511,39 @@ bool AdlMetaEngine::addFileProps(const FileMap &allFiles, Common::String fname, } // Based on AdvancedMetaEngine::detectGame -ADGameDescList AdlMetaEngine::detectGame(const Common::FSNode &parent, const FileMap &allFiles, Common::Language language, Common::Platform platform, const Common::String &extra) const { +ADDetectedGames AdlMetaEngine::detectGame(const Common::FSNode &parent, const FileMap &allFiles, Common::Language language, Common::Platform platform, const Common::String &extra) const { // We run the file-based detector first and then add to the returned list - ADGameDescList matched = AdvancedMetaEngine::detectGame(parent, allFiles, language, platform, extra); + ADDetectedGames matched = AdvancedMetaEngine::detectGame(parent, allFiles, language, platform, extra); debug(3, "Starting disk image detection in dir '%s'", parent.getPath().c_str()); - ADFilePropertiesMap filesProps; - ADGameIdList matchedGameIds; + FilePropertiesMap filesProps; bool gotAnyMatchesWithAllFiles = false; for (uint g = 0; gameDiskDescriptions[g].desc.gameId != 0; ++g) { - const ADGameDescription &desc = gameDiskDescriptions[g].desc; + ADDetectedGame game(&gameDiskDescriptions[g].desc); // Skip games that don't meet the language/platform/extra criteria - if (language != Common::UNK_LANG && desc.language != Common::UNK_LANG) { - if (desc.language != language && !(language == Common::EN_ANY && (desc.flags & ADGF_ADDENGLISH))) - continue; + if (language != Common::UNK_LANG && game.desc->language != Common::UNK_LANG) { + if (game.desc->language != language && !(language == Common::EN_ANY && (game.desc->flags & ADGF_ADDENGLISH))) + continue; } - if (platform != Common::kPlatformUnknown && desc.platform != Common::kPlatformUnknown && desc.platform != platform) + if (platform != Common::kPlatformUnknown && game.desc->platform != Common::kPlatformUnknown && game.desc->platform != platform) continue; - if ((_flags & kADFlagUseExtraAsHint) && !extra.empty() && desc.extra != extra) + if ((_flags & kADFlagUseExtraAsHint) && !extra.empty() && game.desc->extra != extra) continue; - bool fileMissing = false; bool allFilesPresent = true; - bool hashOrSizeMismatch = false; - for (uint f = 0; desc.filesDescriptions[f].fileName; ++f) { - const ADGameFileDescription &fDesc = desc.filesDescriptions[f]; + for (uint f = 0; game.desc->filesDescriptions[f].fileName; ++f) { + const ADGameFileDescription &fDesc = game.desc->filesDescriptions[f]; Common::String fileName; bool foundDiskImage = false; for (uint e = 0; e < ARRAYSIZE(diskImageExts); ++e) { - if (diskImageExts[e].platform == desc.platform) { + if (diskImageExts[e].platform == game.desc->platform) { Common::String testFileName(fDesc.fileName); testFileName += diskImageExts[e].extension; @@ -559,49 +560,41 @@ ADGameDescList AdlMetaEngine::detectGame(const Common::FSNode &parent, const Fil } if (!foundDiskImage) { - fileMissing = true; allFilesPresent = false; break; } - if (hashOrSizeMismatch) + game.matchedFiles[fileName] = filesProps[fileName]; + + if (game.hasUnknownFiles) continue; if (fDesc.md5 && fDesc.md5 != filesProps[fileName].md5) { debug(3, "MD5 Mismatch. Skipping (%s) (%s)", fDesc.md5, filesProps[fileName].md5.c_str()); - fileMissing = true; - hashOrSizeMismatch = true; + game.hasUnknownFiles = true; continue; } if (fDesc.fileSize != -1 && fDesc.fileSize != filesProps[fileName].size) { debug(3, "Size Mismatch. Skipping"); - fileMissing = true; - hashOrSizeMismatch = true; + game.hasUnknownFiles = true; continue; } debug(3, "Matched file: %s", fileName.c_str()); } - if (!fileMissing) { - debug(2, "Found game: %s (%s/%s) (%d)", desc.gameId, getPlatformDescription(desc.platform), getLanguageDescription(desc.language), g); - matched.push_back(&desc); + if (allFilesPresent && !game.hasUnknownFiles) { + debug(2, "Found game: %s (%s/%s) (%d)", game.desc->gameId, getPlatformDescription(game.desc->platform), getLanguageDescription(game.desc->language), g); + gotAnyMatchesWithAllFiles = true; + matched.push_back(game); } else { - if (allFilesPresent) { - gotAnyMatchesWithAllFiles = true; - if (!matchedGameIds.size() || strcmp(matchedGameIds.back(), desc.gameId) != 0) - matchedGameIds.push_back(desc.gameId); + if (allFilesPresent && !gotAnyMatchesWithAllFiles) { + if (matched.empty() || strcmp(matched.back().desc->gameId, game.desc->gameId) != 0) + matched.push_back(game); } - debug(5, "Skipping game: %s (%s/%s) (%d)", desc.gameId, getPlatformDescription(desc.platform), getLanguageDescription(desc.language), g); - } - } - - // TODO: This could be improved to handle matched and unknown games together in a single directory - if (matched.empty()) { - if (!filesProps.empty() && gotAnyMatchesWithAllFiles) { - reportUnknown(parent, filesProps, matchedGameIds); + debug(5, "Skipping game: %s (%s/%s) (%d)", game.desc->gameId, getPlatformDescription(game.desc->platform), getLanguageDescription(game.desc->language), g); } } diff --git a/engines/adl/graphics.cpp b/engines/adl/graphics.cpp index 0f80bac988..2fcd1473a0 100644 --- a/engines/adl/graphics.cpp +++ b/engines/adl/graphics.cpp @@ -350,7 +350,7 @@ void GraphicsMan_v2::fillAt(Common::Point p, const byte pattern) { const bool stopBit = !_display.getPixelBit(p); // Move up into the open space above p - while (--p.y >= _bounds.top && canFillAt(p, stopBit)); + while (--p.y >= _bounds.top && canFillAt(p, stopBit)) {} // Then fill by moving down while (++p.y < _bounds.bottom && canFillAt(p, stopBit)) diff --git a/engines/advancedDetector.cpp b/engines/advancedDetector.cpp index 9d695058dd..6f4efcfb57 100644 --- a/engines/advancedDetector.cpp +++ b/engines/advancedDetector.cpp @@ -33,32 +33,16 @@ #include "engines/advancedDetector.h" #include "engines/obsolete.h" -static GameDescriptor toGameDescriptor(const ADGameDescription &g, const PlainGameDescriptor *sg) { - const char *title = 0; - const char *extra; - - if (g.flags & ADGF_USEEXTRAASTITLE) { - title = g.extra; - extra = ""; - } else { - while (sg->gameId) { - if (!scumm_stricmp(g.gameId, sg->gameId)) - title = sg->description; - sg++; - } +static Common::String sanitizeName(const char *name) { + Common::String res; - extra = g.extra; + while (*name) { + if (Common::isAlnum(*name)) + res += tolower(*name); + name++; } - GameSupportLevel gsl = kStableGame; - if (g.flags & ADGF_UNSTABLE) - gsl = kUnstableGame; - else if (g.flags & ADGF_TESTING) - gsl = kTestingGame; - - GameDescriptor gd(g.gameId, title, g.language, g.platform, 0, gsl); - gd.updateDesc(extra); - return gd; + return res; } /** @@ -67,8 +51,14 @@ static GameDescriptor toGameDescriptor(const ADGameDescription &g, const PlainGa * or (if ADGF_DEMO has been set) * GAMEID-demo-PLAFORM-LANG */ -static Common::String generatePreferredTarget(const Common::String &id, const ADGameDescription *desc) { - Common::String res(id); +static Common::String generatePreferredTarget(const ADGameDescription *desc) { + Common::String res; + + if (desc->flags & ADGF_AUTOGENTARGET && desc->extra && *desc->extra) { + res = sanitizeName(desc->extra); + } else { + res = desc->gameId; + } if (desc->flags & ADGF_DEMO) { res = res + "-demo"; @@ -89,49 +79,50 @@ static Common::String generatePreferredTarget(const Common::String &id, const AD return res; } -static Common::String sanitizeName(const char *name) { - Common::String res; - - while (*name) { - if (Common::isAlnum(*name)) - res += tolower(*name); - name++; - } +DetectedGame AdvancedMetaEngine::toDetectedGame(const ADDetectedGame &adGame) const { + const ADGameDescription *desc = adGame.desc; - return res; -} + const char *gameId = _singleId ? _singleId : desc->gameId; -void AdvancedMetaEngine::updateGameDescriptor(GameDescriptor &desc, const ADGameDescription *realDesc) const { - if (_singleId != NULL) { - desc["preferredtarget"] = desc["gameid"]; - desc["gameid"] = _singleId; + const char *title; + const char *extra; + if (desc->flags & ADGF_USEEXTRAASTITLE) { + title = desc->extra; + extra = ""; + } else { + const PlainGameDescriptor *pgd = findPlainGameDescriptor(desc->gameId, _gameIds); + title = pgd->description; + extra = desc->extra; } - if (!desc.contains("preferredtarget")) - desc["preferredtarget"] = desc["gameid"]; + DetectedGame game(gameId, title, desc->language, desc->platform, extra); + game.hasUnknownFiles = adGame.hasUnknownFiles; + game.matchedFiles = adGame.matchedFiles; + game.preferredTarget = generatePreferredTarget(desc); - if (realDesc->flags & ADGF_AUTOGENTARGET) { - if (*realDesc->extra) - desc["preferredtarget"] = sanitizeName(realDesc->extra); - } + game.gameSupportLevel = kStableGame; + if (desc->flags & ADGF_UNSTABLE) + game.gameSupportLevel = kUnstableGame; + else if (desc->flags & ADGF_TESTING) + game.gameSupportLevel = kTestingGame; - desc["preferredtarget"] = generatePreferredTarget(desc["preferredtarget"], realDesc); + game.setGUIOptions(desc->guiOptions + _guiOptions); + game.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(desc->language)); - if (_flags & kADFlagUseExtraAsHint) - desc["extra"] = realDesc->extra; + if (desc->flags & ADGF_ADDENGLISH) + game.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::EN_ANY)); - desc.setGUIOptions(realDesc->guiOptions + _guiOptions); - desc.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(realDesc->language)); + if (_flags & kADFlagUseExtraAsHint) + game.extra = desc->extra; - if (realDesc->flags & ADGF_ADDENGLISH) - desc.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::EN_ANY)); + return game; } -bool cleanupPirated(ADGameDescList &matched) { +bool cleanupPirated(ADDetectedGames &matched) { // OKay, now let's sense presence of pirated games if (!matched.empty()) { for (uint j = 0; j < matched.size();) { - if (matched[j]->flags & ADGF_PIRATED) + if (matched[j].desc->flags & ADGF_PIRATED) matched.remove_at(j); else ++j; @@ -148,35 +139,46 @@ bool cleanupPirated(ADGameDescList &matched) { } -GameList AdvancedMetaEngine::detectGames(const Common::FSList &fslist) const { - ADGameDescList matches; - GameList detectedGames; +DetectedGames AdvancedMetaEngine::detectGames(const Common::FSList &fslist) const { FileMap allFiles; if (fslist.empty()) - return detectedGames; + return DetectedGames(); // Compose a hashmap of all files in fslist. composeFileHashMap(allFiles, fslist, (_maxScanDepth == 0 ? 1 : _maxScanDepth)); // Run the detector on this - matches = detectGame(fslist.begin()->getParent(), allFiles, Common::UNK_LANG, Common::kPlatformUnknown, ""); + ADDetectedGames matches = detectGame(fslist.begin()->getParent(), allFiles, Common::UNK_LANG, Common::kPlatformUnknown, ""); - if (matches.empty()) { - // Use fallback detector if there were no matches by other means - const ADGameDescription *fallbackDesc = fallbackDetect(allFiles, fslist); - if (fallbackDesc != 0) { - GameDescriptor desc(toGameDescriptor(*fallbackDesc, _gameIds)); - updateGameDescriptor(desc, fallbackDesc); - detectedGames.push_back(desc); + cleanupPirated(matches); + + DetectedGames detectedGames; + for (uint i = 0; i < matches.size(); i++) { + DetectedGame game = toDetectedGame(matches[i]); + + if (game.hasUnknownFiles) { + // Non fallback games with unknown files cannot be added/launched + game.canBeAdded = false; } - } else { - // Otherwise use the found matches - cleanupPirated(matches); - for (uint i = 0; i < matches.size(); i++) { - GameDescriptor desc(toGameDescriptor(*matches[i], _gameIds)); - updateGameDescriptor(desc, matches[i]); - detectedGames.push_back(desc); + + detectedGames.push_back(game); + } + + bool foundKnownGames = false; + for (uint i = 0; i < detectedGames.size(); i++) { + foundKnownGames |= detectedGames[i].canBeAdded; + } + + if (!foundKnownGames) { + // Use fallback detector if there were no matches by other means + ADDetectedGame fallbackDetectionResult = fallbackDetect(allFiles, fslist); + + if (fallbackDetectionResult.desc) { + DetectedGame fallbackDetectedGame = toDetectedGame(fallbackDetectionResult); + fallbackDetectedGame.preferredTarget += "-fallback"; + + detectedGames.push_back(fallbackDetectedGame); } } @@ -214,7 +216,6 @@ const ExtraGuiOptions AdvancedMetaEngine::getExtraGuiOptions(const Common::Strin Common::Error AdvancedMetaEngine::createInstance(OSystem *syst, Engine **engine) const { assert(engine); - const ADGameDescription *agdDesc = 0; Common::Language language = Common::UNK_LANG; Common::Platform platform = Common::kPlatformUnknown; Common::String extra; @@ -235,18 +236,6 @@ Common::Error AdvancedMetaEngine::createInstance(OSystem *syst, Engine **engine) path = ConfMan.get("path"); } else { path = "."; - - // This situation may happen only when game was - // launched from a command line with wrong target and - // no path was provided. - // - // A dummy entry will get created and will keep game path - // We mark this entry, so it will not be added to the - // config file. - // - // Fixes bug #1544799 - ConfMan.setBool("autoadded", true); - warning("No path was provided. Assuming the data files are in the current directory"); } Common::FSNode dir(path); @@ -264,46 +253,43 @@ Common::Error AdvancedMetaEngine::createInstance(OSystem *syst, Engine **engine) composeFileHashMap(allFiles, files, (_maxScanDepth == 0 ? 1 : _maxScanDepth)); // Run the detector on this - ADGameDescList matches = detectGame(files.begin()->getParent(), allFiles, language, platform, extra); + ADDetectedGames matches = detectGame(files.begin()->getParent(), allFiles, language, platform, extra); if (cleanupPirated(matches)) return Common::kNoGameDataFoundError; - if (_singleId == NULL) { - // Find the first match with correct gameid. - for (uint i = 0; i < matches.size(); i++) { - if (matches[i]->gameId == gameid) { - agdDesc = matches[i]; - break; - } + ADDetectedGame agdDesc; + for (uint i = 0; i < matches.size(); i++) { + if ((_singleId || matches[i].desc->gameId == gameid) && !matches[i].hasUnknownFiles) { + agdDesc = matches[i]; + break; } - } else if (matches.size() > 0) { - agdDesc = matches[0]; } - if (agdDesc == 0) { + if (!agdDesc.desc) { // Use fallback detector if there were no matches by other means - agdDesc = fallbackDetect(allFiles, files); - if (agdDesc != 0) { + ADDetectedGame fallbackDetectedGame = fallbackDetect(allFiles, files); + agdDesc = fallbackDetectedGame; + if (agdDesc.desc) { // Seems we found a fallback match. But first perform a basic // sanity check: the gameid must match. - if (_singleId == NULL && agdDesc->gameId != gameid) - agdDesc = 0; + if (!_singleId && agdDesc.desc->gameId != gameid) + agdDesc = ADDetectedGame(); } } - if (agdDesc == 0) + if (!agdDesc.desc) return Common::kNoGameDataFoundError; // If the GUI options were updated, we catch this here and update them in the users config // file transparently. - Common::String lang = getGameGUIOptionsDescriptionLanguage(agdDesc->language); - if (agdDesc->flags & ADGF_ADDENGLISH) + Common::String lang = getGameGUIOptionsDescriptionLanguage(agdDesc.desc->language); + if (agdDesc.desc->flags & ADGF_ADDENGLISH) lang += " " + getGameGUIOptionsDescriptionLanguage(Common::EN_ANY); - Common::updateGameGUIOptions(agdDesc->guiOptions + _guiOptions, lang); + Common::updateGameGUIOptions(agdDesc.desc->guiOptions + _guiOptions, lang); - GameDescriptor gameDescriptor = toGameDescriptor(*agdDesc, _gameIds); + DetectedGame gameDescriptor = toDetectedGame(agdDesc); bool showTestingWarning = false; @@ -311,53 +297,20 @@ Common::Error AdvancedMetaEngine::createInstance(OSystem *syst, Engine **engine) showTestingWarning = true; #endif - if (((gameDescriptor.getSupportLevel() == kUnstableGame - || (gameDescriptor.getSupportLevel() == kTestingGame + if (((gameDescriptor.gameSupportLevel == kUnstableGame + || (gameDescriptor.gameSupportLevel == kTestingGame && showTestingWarning))) && !Engine::warnUserAboutUnsupportedGame()) return Common::kUserCanceled; - debug(2, "Running %s", gameDescriptor.description().c_str()); - initSubSystems(agdDesc); - if (!createInstance(syst, engine, agdDesc)) + debug(2, "Running %s", gameDescriptor.description.c_str()); + initSubSystems(agdDesc.desc); + if (!createInstance(syst, engine, agdDesc.desc)) return Common::kNoGameDataFoundError; else return Common::kNoError; } -void AdvancedMetaEngine::reportUnknown(const Common::FSNode &path, const ADFilePropertiesMap &filesProps, const ADGameIdList &matchedGameIds) const { - Common::String report = Common::String::format( - _("The game in '%s' seems to be an unknown %s engine game " - "variant.\n\nPlease report the following data to the ScummVM " - "team at %s along with the name of the game you tried to add and " - "its version, language, etc.:"), - path.getPath().c_str(), getName(), "https://bugs.scummvm.org/"); - - if (matchedGameIds.size()) { - report += "\n\n"; - report += _("Matched game IDs:"); - report += " "; - - for (ADGameIdList::const_iterator gameId = matchedGameIds.begin(); gameId != matchedGameIds.end(); ++gameId) { - if (gameId != matchedGameIds.begin()) { - report += ", "; - } - report += *gameId; - } - } - - report += "\n\n"; - - report.wordWrap(80); - - 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"; - - g_system->logMessage(LogMessageType::kInfo, report.c_str()); -} - void AdvancedMetaEngine::composeFileHashMap(FileMap &allFiles, const Common::FSList &fslist, int depth, const Common::String &parentName) const { if (depth <= 0) return; @@ -398,7 +351,7 @@ 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 { +bool AdvancedMetaEngine::getFileProperties(const Common::FSNode &parent, const FileMap &allFiles, const ADGameDescription &game, const Common::String fname, FileProperties &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. @@ -428,8 +381,9 @@ bool AdvancedMetaEngine::getFileProperties(const Common::FSNode &parent, const F return true; } -ADGameDescList AdvancedMetaEngine::detectGame(const Common::FSNode &parent, const FileMap &allFiles, Common::Language language, Common::Platform platform, const Common::String &extra) const { - ADFilePropertiesMap filesProps; +ADDetectedGames AdvancedMetaEngine::detectGame(const Common::FSNode &parent, const FileMap &allFiles, Common::Language language, Common::Platform platform, const Common::String &extra) const { + FilePropertiesMap filesProps; + ADDetectedGames matched; const ADGameFileDescription *fileDesc; const ADGameDescription *g; @@ -439,12 +393,12 @@ ADGameDescList AdvancedMetaEngine::detectGame(const Common::FSNode &parent, cons // Check which files are included in some ADGameDescription *and* are present. // Compute MD5s and file sizes for these files. - for (descPtr = _gameDescriptors; ((const ADGameDescription *)descPtr)->gameId != 0; descPtr += _descItemSize) { + for (descPtr = _gameDescriptors; ((const ADGameDescription *)descPtr)->gameId != nullptr; descPtr += _descItemSize) { g = (const ADGameDescription *)descPtr; for (fileDesc = g->filesDescriptions; fileDesc->fileName; fileDesc++) { Common::String fname = fileDesc->fileName; - ADFileProperties tmp; + FileProperties tmp; if (filesProps.contains(fname)) continue; @@ -456,16 +410,13 @@ ADGameDescList AdvancedMetaEngine::detectGame(const Common::FSNode &parent, cons } } - ADGameDescList matched; - ADGameIdList matchedGameIds; int maxFilesMatched = 0; bool gotAnyMatchesWithAllFiles = false; // MD5 based matching uint i; - for (i = 0, descPtr = _gameDescriptors; ((const ADGameDescription *)descPtr)->gameId != 0; descPtr += _descItemSize, ++i) { + for (i = 0, descPtr = _gameDescriptors; ((const ADGameDescription *)descPtr)->gameId != nullptr; descPtr += _descItemSize, ++i) { g = (const ADGameDescription *)descPtr; - bool fileMissing = false; // Do not even bother to look at entries which do not have matching // language and platform (if specified). @@ -478,34 +429,33 @@ ADGameDescList AdvancedMetaEngine::detectGame(const Common::FSNode &parent, cons if ((_flags & kADFlagUseExtraAsHint) && !extra.empty() && g->extra != extra) continue; + ADDetectedGame game(g); bool allFilesPresent = true; int curFilesMatched = 0; - bool hashOrSizeMismatch = false; // Try to match all files for this game - for (fileDesc = g->filesDescriptions; fileDesc->fileName; fileDesc++) { + for (fileDesc = game.desc->filesDescriptions; fileDesc->fileName; fileDesc++) { Common::String tstr = fileDesc->fileName; if (!filesProps.contains(tstr)) { - fileMissing = true; allFilesPresent = false; break; } - if (hashOrSizeMismatch) + game.matchedFiles[tstr] = filesProps[tstr]; + + if (game.hasUnknownFiles) continue; - if (fileDesc->md5 != NULL && fileDesc->md5 != filesProps[tstr].md5) { + if (fileDesc->md5 != nullptr && fileDesc->md5 != filesProps[tstr].md5) { debug(3, "MD5 Mismatch. Skipping (%s) (%s)", fileDesc->md5, filesProps[tstr].md5.c_str()); - fileMissing = true; - hashOrSizeMismatch = true; + game.hasUnknownFiles = true; continue; } if (fileDesc->fileSize != -1 && fileDesc->fileSize != filesProps[tstr].size) { debug(3, "Size Mismatch. Skipping"); - fileMissing = true; - hashOrSizeMismatch = true; + game.hasUnknownFiles = true; continue; } @@ -522,13 +472,12 @@ ADGameDescList AdvancedMetaEngine::detectGame(const Common::FSNode &parent, cons // Potentially this could rule out variants where some particular file // is really missing, but the developers should better know about such // cases. - if (allFilesPresent) { - gotAnyMatchesWithAllFiles = true; - if (!matchedGameIds.size() || strcmp(matchedGameIds.back(), g->gameId) != 0) - matchedGameIds.push_back(g->gameId); + if (allFilesPresent && !gotAnyMatchesWithAllFiles) { + if (matched.empty() || strcmp(matched.back().desc->gameId, g->gameId) != 0) + matched.push_back(game); } - if (!fileMissing) { + if (allFilesPresent && !game.hasUnknownFiles) { debug(2, "Found game: %s (%s %s/%s) (%d)", g->gameId, g->extra, getPlatformDescription(g->platform), getLanguageDescription(g->language), i); @@ -537,37 +486,29 @@ ADGameDescList AdvancedMetaEngine::detectGame(const Common::FSNode &parent, cons maxFilesMatched = curFilesMatched; matched.clear(); // Remove any prior, lower ranked matches. - matched.push_back(g); + matched.push_back(game); } else if (curFilesMatched == maxFilesMatched) { - matched.push_back(g); + matched.push_back(game); } else { debug(2, " ... skipped"); } + gotAnyMatchesWithAllFiles = true; } else { debug(5, "Skipping game: %s (%s %s/%s) (%d)", g->gameId, g->extra, getPlatformDescription(g->platform), getLanguageDescription(g->language), i); } } - // We didn't find a match - if (matched.empty()) { - if (!filesProps.empty() && gotAnyMatchesWithAllFiles) { - reportUnknown(parent, filesProps, matchedGameIds); - } - - // Filename based fallback - } - return matched; } -const ADGameDescription *AdvancedMetaEngine::detectGameFilebased(const FileMap &allFiles, const Common::FSList &fslist, const ADFileBasedFallback *fileBasedFallback, ADFilePropertiesMap *filesProps) const { +ADDetectedGame AdvancedMetaEngine::detectGameFilebased(const FileMap &allFiles, const Common::FSList &fslist, const ADFileBasedFallback *fileBasedFallback) const { const ADFileBasedFallback *ptr; const char* const* filenames; int maxNumMatchedFiles = 0; - const ADGameDescription *matchedDesc = 0; + ADDetectedGame result; for (ptr = fileBasedFallback; ptr->desc; ++ptr) { const ADGameDescription *agdesc = ptr->desc; @@ -588,35 +529,36 @@ const ADGameDescription *AdvancedMetaEngine::detectGameFilebased(const FileMap & debug(4, "Matched: %s", agdesc->gameId); if (numMatchedFiles > maxNumMatchedFiles) { - matchedDesc = agdesc; maxNumMatchedFiles = numMatchedFiles; debug(4, "and overridden"); - if (filesProps) { - for (filenames = ptr->filenames; *filenames; ++filenames) { - ADFileProperties tmp; + ADDetectedGame game(agdesc); + game.hasUnknownFiles = true; + + for (filenames = ptr->filenames; *filenames; ++filenames) { + FileProperties tmp; - if (getFileProperties(fslist.begin()->getParent(), allFiles, *agdesc, *filenames, tmp)) - (*filesProps)[*filenames] = tmp; - } + if (getFileProperties(fslist.begin()->getParent(), allFiles, *agdesc, *filenames, tmp)) + game.matchedFiles[*filenames] = tmp; } + result = game; } } } - return matchedDesc; + return result; } -GameList AdvancedMetaEngine::getSupportedGames() const { +PlainGameList AdvancedMetaEngine::getSupportedGames() const { if (_singleId != NULL) { - GameList gl; + PlainGameList gl; const PlainGameDescriptor *g = _gameIds; while (g->gameId) { if (0 == scumm_stricmp(_singleId, g->gameId)) { - gl.push_back(GameDescriptor(g->gameId, g->description)); + gl.push_back(*g); return gl; } @@ -625,17 +567,17 @@ GameList AdvancedMetaEngine::getSupportedGames() const { error("Engine %s doesn't have its singleid specified in ids list", _singleId); } - return GameList(_gameIds); + return PlainGameList(_gameIds); } -GameDescriptor AdvancedMetaEngine::findGame(const char *gameId) const { +PlainGameDescriptor AdvancedMetaEngine::findGame(const char *gameId) const { // First search the list of supported gameids for a match. const PlainGameDescriptor *g = findPlainGameDescriptor(gameId, _gameIds); if (g) - return GameDescriptor(*g); + return *g; // No match found - return GameDescriptor(); + return PlainGameDescriptor::empty(); } AdvancedMetaEngine::AdvancedMetaEngine(const void *descs, uint descItemSize, const PlainGameDescriptor *gameIds, const ADExtraGuiOptionsMap *extraGuiOptions) diff --git a/engines/advancedDetector.h b/engines/advancedDetector.h index 5160a99679..326cb79c49 100644 --- a/engines/advancedDetector.h +++ b/engines/advancedDetector.h @@ -41,27 +41,13 @@ class FSList; * enable detection. */ struct ADGameFileDescription { - const char *fileName; ///< Name of described file. - uint16 fileType; ///< Optional. Not used during detection, only by engines. - const char *md5; ///< MD5 of (the beginning of) the described file. Optional. Set to NULL to ignore. - int32 fileSize; ///< Size of the described file. Set to -1 to ignore. + const char *fileName; ///< Name of described file. + uint16 fileType; ///< Optional. Not used during detection, only by engines. + const char *md5; ///< MD5 of (the beginning of) the described file. Optional. Set to NULL to ignore. + int32 fileSize; ///< Size of the described file. Set to -1 to ignore. }; /** - * 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. */ @@ -80,18 +66,18 @@ typedef Common::HashMap<Common::String, ADFileProperties, Common::IgnoreCase_Has #define AD_ENTRY1s(f, x, s) {{ f, 0, x, s}, AD_LISTEND} enum ADGameFlags { - ADGF_NO_FLAGS = 0, - ADGF_AUTOGENTARGET = (1 << 20), // automatically generate gameid from extra - ADGF_UNSTABLE = (1 << 21), // flag to designate not yet officially-supported games that are not fit for public testing - ADGF_TESTING = (1 << 22), // flag to designate not yet officially-supported games that are fit for public testing - ADGF_PIRATED = (1 << 23), ///< flag to designate well known pirated versions with cracks - ADGF_ADDENGLISH = (1 << 24), ///< always add English as language option - ADGF_MACRESFORK = (1 << 25), ///< the md5 for this entry will be calculated from the resource fork + ADGF_NO_FLAGS = 0, + ADGF_AUTOGENTARGET = (1 << 20), ///< automatically generate gameid from extra + ADGF_UNSTABLE = (1 << 21), ///< flag to designate not yet officially-supported games that are not fit for public testing + ADGF_TESTING = (1 << 22), ///< flag to designate not yet officially-supported games that are fit for public testing + ADGF_PIRATED = (1 << 23), ///< flag to designate well known pirated versions with cracks + ADGF_ADDENGLISH = (1 << 24), ///< always add English as language option + ADGF_MACRESFORK = (1 << 25), ///< the md5 for this entry will be calculated from the resource fork ADGF_USEEXTRAASTITLE = (1 << 26), ///< Extra field value will be used as main game title, not gameid - ADGF_DROPLANGUAGE = (1 << 27), ///< don't add language to gameid - ADGF_DROPPLATFORM = (1 << 28), ///< don't add platform to gameid - ADGF_CD = (1 << 29), ///< add "-cd" to gameid - ADGF_DEMO = (1 << 30) ///< add "-demo" to gameid + ADGF_DROPLANGUAGE = (1 << 27), ///< don't add language to gameid + ADGF_DROPPLATFORM = (1 << 28), ///< don't add platform to gameid + ADGF_CD = (1 << 29), ///< add "-cd" to gameid + ADGF_DEMO = (1 << 30) ///< add "-demo" to gameid }; struct ADGameDescription { @@ -112,14 +98,19 @@ struct ADGameDescription { }; /** - * A list of pointers to ADGameDescription structs (or subclasses thereof). + * A game installation matching an AD game description */ -typedef Common::Array<const ADGameDescription *> ADGameDescList; +struct ADDetectedGame { + bool hasUnknownFiles; + FilePropertiesMap matchedFiles; + const ADGameDescription *desc; -/** - * A list of raw game ID strings. - */ -typedef Common::Array<const char *> ADGameIdList; + ADDetectedGame() : desc(nullptr), hasUnknownFiles(false) {} + explicit ADDetectedGame(const ADGameDescription *d) : desc(d), hasUnknownFiles(false) {} +}; + +/** A list of games detected by the AD */ +typedef Common::Array<ADDetectedGame> ADDetectedGames; /** * End marker for a table of ADGameDescription structs. Use this to @@ -274,11 +265,11 @@ public: * Returns list of targets supported by the engine. * Distinguishes engines with single ID */ - virtual GameList getSupportedGames() const; + PlainGameList getSupportedGames() const override; - virtual GameDescriptor findGame(const char *gameId) const; + PlainGameDescriptor findGame(const char *gameId) const override; - virtual GameList detectGames(const Common::FSList &fslist) const; + DetectedGames detectGames(const Common::FSList &fslist) const override; virtual Common::Error createInstance(OSystem *syst, Engine **engine) const; @@ -294,8 +285,8 @@ protected: * An (optional) generic fallback detect function which is invoked * if the regular MD5 based detection failed to detect anything. */ - virtual const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const { - return 0; + virtual ADDetectedGame fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const { + return ADDetectedGame(); } private: @@ -313,7 +304,7 @@ protected: * @param extra restrict results to specified extra string (only if kADFlagUseExtraAsHint is set) * @return list of ADGameDescription pointers corresponding to matched games */ - virtual ADGameDescList detectGame(const Common::FSNode &parent, const FileMap &allFiles, Common::Language language, Common::Platform platform, const Common::String &extra) const; + virtual ADDetectedGames detectGame(const Common::FSNode &parent, const FileMap &allFiles, Common::Language language, Common::Platform platform, const Common::String &extra) const; /** * Iterates over all ADFileBasedFallback records inside fileBasedFallback. @@ -327,16 +318,7 @@ protected: * @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. - */ - void reportUnknown(const Common::FSNode &path, const ADFilePropertiesMap &filesProps, const ADGameIdList &matchedGameIds = ADGameIdList()) const; - - // TODO - void updateGameDescriptor(GameDescriptor &desc, const ADGameDescription *realDesc) const; + ADDetectedGame detectGameFilebased(const FileMap &allFiles, const Common::FSList &fslist, const ADFileBasedFallback *fileBasedFallback) const; /** * Compose a hashmap of all files in fslist. @@ -345,7 +327,10 @@ protected: void composeFileHashMap(FileMap &allFiles, const Common::FSList &fslist, int depth, const Common::String &parentName = Common::String()) 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; + bool getFileProperties(const Common::FSNode &parent, const FileMap &allFiles, const ADGameDescription &game, const Common::String fname, FileProperties &fileProps) const; + + /** Convert an AD game description into the shared game description format */ + DetectedGame toDetectedGame(const ADDetectedGame &adGame) const; }; #endif diff --git a/engines/agi/detection.cpp b/engines/agi/detection.cpp index e26f5c84fc..39275c4f70 100644 --- a/engines/agi/detection.cpp +++ b/engines/agi/detection.cpp @@ -220,7 +220,7 @@ public: virtual void removeSaveState(const char *target, int slot) const; SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const; - const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const; + ADDetectedGame fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const override; }; bool AgiMetaEngine::hasFeature(MetaEngineFeature f) const { @@ -375,7 +375,11 @@ SaveStateDescriptor AgiMetaEngine::querySaveMetaInfos(const char *target, int sl char saveVersion = in->readByte(); if (saveVersion >= 4) { - Graphics::Surface *const thumbnail = Graphics::loadThumbnail(*in); + Graphics::Surface *thumbnail; + if (!Graphics::loadThumbnail(*in, thumbnail)) { + delete in; + return SaveStateDescriptor(); + } descriptor.setThumbnail(thumbnail); @@ -417,7 +421,7 @@ SaveStateDescriptor AgiMetaEngine::querySaveMetaInfos(const char *target, int sl } } -const ADGameDescription *AgiMetaEngine::fallbackDetect(const FileMap &allFilesXXX, const Common::FSList &fslist) const { +ADDetectedGame AgiMetaEngine::fallbackDetect(const FileMap &allFilesXXX, const Common::FSList &fslist) const { typedef Common::HashMap<Common::String, int32> IntMap; IntMap allFiles; bool matchedUsingFilenames = false; @@ -580,10 +584,10 @@ const ADGameDescription *AgiMetaEngine::fallbackDetect(const FileMap &allFilesXX g_system->logMessage(LogMessageType::kWarning, fallbackWarning.c_str()); - return (const ADGameDescription *)&g_fallbackDesc; + return ADDetectedGame(&g_fallbackDesc.desc); } - return 0; + return ADDetectedGame(); } #if PLUGIN_ENABLED_DYNAMIC(AGI) diff --git a/engines/agos/detection.cpp b/engines/agos/detection.cpp index dbc4ee9145..1847434200 100644 --- a/engines/agos/detection.cpp +++ b/engines/agos/detection.cpp @@ -99,7 +99,7 @@ public: _directoryGlobs = directoryGlobs; } - virtual GameDescriptor findGame(const char *gameId) const { + PlainGameDescriptor findGame(const char *gameId) const override { return Engines::findGameID(gameId, _gameIds, obsoleteGameIDsTable); } diff --git a/engines/agos/detection_tables.h b/engines/agos/detection_tables.h index 90e5a84829..b9cdce6b99 100644 --- a/engines/agos/detection_tables.h +++ b/engines/agos/detection_tables.h @@ -2401,6 +2401,156 @@ static const AGOSGameDescription gameDescriptions[] = { GF_TALKIE }, + // Simon the Sorcerer 2 - Russian Fan with MT-32 hack (25th Anniversary Edition) + { + { + "simon2", + "25th Anniversary Edition", + + { + { "gsptr30", GAME_BASEFILE, "e26d162e573587f4601b88701292212c", 58851 }, + { "icon.dat", GAME_ICONFILE, "72096a62d36e6034ea9fecc13b2dbdab", 18089 }, + { "simon2.gme", GAME_GMEFILE, "f1727b15b3e389f0248363d890751ee7", 19695662 }, + { "stripped.txt", GAME_STRFILE, "e229f84d46fa83f99b4a7115679f3fb6", 171 }, + { "tbllist", GAME_TBLFILE, "2082f8d02075e590300478853a91ffd9", 513 }, + { NULL, 0, NULL, 0} + }, + Common::RU_RUS, + Common::kPlatformDOS, + ADGF_CD, + GUIO0() + }, + + GType_SIMON2, + GID_SIMON2, + GF_TALKIE + }, + + // Simon the Sorcerer 2 - Polish with MT-32 hack (25th Anniversary Edition) + { + { + "simon2", + "25th Anniversary Edition", + + { + { "gsptr30", GAME_BASEFILE, "657fd873f5d0637097ee02315b447e6f", -1}, + { "icon.dat", GAME_ICONFILE, "72096a62d36e6034ea9fecc13b2dbdab", -1}, + { "simon2.gme", GAME_GMEFILE, "212fa5638a76869537d092d4e76524c0", 20037221}, + { "stripped.txt", GAME_STRFILE, "e229f84d46fa83f99b4a7115679f3fb6", -1}, + { "tbllist", GAME_TBLFILE, "2082f8d02075e590300478853a91ffd9", -1}, + { NULL, 0, NULL, 0} + }, + Common::PL_POL, + Common::kPlatformDOS, + ADGF_CD, + GUIO0() + }, + + GType_SIMON2, + GID_SIMON2, + GF_TALKIE | GF_WAVSFX + }, + + // Simon the Sorcerer 2 - Hebrew with MT-32 hack (25th Anniversary Edition) + { + { + "simon2", + "25th Anniversary Edition", + + { + { "gsptr30", GAME_BASEFILE, "952a2b1be23c3c609ba8d988a9a1627d", 53366}, + { "icon.dat", GAME_ICONFILE, "72096a62d36e6034ea9fecc13b2dbdab", 18089}, + { "simon2.gme", GAME_GMEFILE, "40ac2d4763d97a9268023dc6db17e2ce", 20017302}, + { "stripped.txt", GAME_STRFILE, "de9dbc24158660e153483fa0cf6c3172", 171}, + { "tbllist", GAME_TBLFILE, "2082f8d02075e590300478853a91ffd9", 513}, + { NULL, 0, NULL, 0} + }, + Common::HE_ISR, + Common::kPlatformDOS, + ADGF_CD, + GUIO0() + }, + + GType_SIMON2, + GID_SIMON2, + GF_TALKIE | GF_WAVSFX + }, + + // Simon the Sorcerer 2 - Italian with MT-32 hack (25th Anniversary Edition) + { + { + "simon2", + "25th Anniversary Edition", + + { + { "gsptr30", GAME_BASEFILE, "3e11d400bea0638f360a724687005cd1", -1}, + { "icon.dat", GAME_ICONFILE, "72096a62d36e6034ea9fecc13b2dbdab", -1}, + { "simon2.gme", GAME_GMEFILE, "510d012bcc5775a8513923163ffe4458", 20066490}, + { "stripped.txt", GAME_STRFILE, "bea6843fb9f3b2144fcb146d62db0b9a", -1}, + { "tbllist", GAME_TBLFILE, "2082f8d02075e590300478853a91ffd9", -1}, + { NULL, 0, NULL, 0} + }, + Common::IT_ITA, + Common::kPlatformDOS, + ADGF_CD, + GUIO0() + }, + + GType_SIMON2, + GID_SIMON2, + GF_TALKIE | GF_WAVSFX + }, + + // Simon the Sorcerer 2 - Czech with MT-32 hack (25th Anniversary Edition) + { + { + "simon2", + "25th Anniversary Edition", + + { + { "gsptr30", GAME_BASEFILE, "eb8217f9ec4628d12ca606033146c48c", -1}, + { "icon.dat", GAME_ICONFILE, "72096a62d36e6034ea9fecc13b2dbdab", -1}, + { "simon2.gme", GAME_GMEFILE, "50188f9fde0d063c824476972936a52f", 20054555}, + { "stripped.txt", GAME_STRFILE, "e229f84d46fa83f99b4a7115679f3fb6", -1}, + { "tbllist", GAME_TBLFILE, "2082f8d02075e590300478853a91ffd9", -1}, + { NULL, 0, NULL, 0} + }, + Common::CZ_CZE, + Common::kPlatformDOS, + ADGF_CD, + GUIO0() + }, + + GType_SIMON2, + GID_SIMON2, + GF_TALKIE | GF_WAVSFX + }, + + // Simon the Sorcerer 2 - Spanish with MT-32 hack (25th Anniversary Edition) + { + { + "simon2", + "25th Anniversary Edition", + + { + { "gsptr30", GAME_BASEFILE, "268dc322aa73bcf27bb016b8e8ceb889", -1}, + { "icon.dat", GAME_ICONFILE, "72096a62d36e6034ea9fecc13b2dbdab", -1}, + { "simon2.gme", GAME_GMEFILE, "2b997db3c677fb3d2174c73ba2cc53e1", 20049608}, + { "stripped.txt", GAME_STRFILE, "d13753796bd81bf313a2449f34d8b112", -1}, + { "tbllist", GAME_TBLFILE, "2082f8d02075e590300478853a91ffd9", -1}, + { NULL, 0, NULL, 0} + }, + Common::ES_ESP, + Common::kPlatformDOS, + ADGF_CD, + GUIO0() + }, + + GType_SIMON2, + GID_SIMON2, + GF_TALKIE | GF_WAVSFX + }, + #ifdef ENABLE_AGOS2 // The Feeble Files - English DOS Demo { diff --git a/engines/agos/intern.h b/engines/agos/intern.h index 3f5c8c519b..afc0cad08e 100644 --- a/engines/agos/intern.h +++ b/engines/agos/intern.h @@ -256,7 +256,8 @@ enum GameFeatures { GF_PLANAR = 1 << 7, GF_DEMO = 1 << 8, GF_PACKED = 1 << 9, - GF_BROKEN_FF_RATING = 1 << 10 + GF_BROKEN_FF_RATING = 1 << 10, + GF_WAVSFX = 1 << 11 }; enum GameFileTypes { diff --git a/engines/agos/midi.cpp b/engines/agos/midi.cpp index 3a7158a203..5e28654c41 100644 --- a/engines/agos/midi.cpp +++ b/engines/agos/midi.cpp @@ -482,20 +482,14 @@ void MidiPlayer::pause(bool b) { } void MidiPlayer::setVolume(int musicVol, int sfxVol) { - if (musicVol < 0) - musicVol = 0; - else if (musicVol > 255) - musicVol = 255; - if (sfxVol < 0) - sfxVol = 0; - else if (sfxVol > 255) - sfxVol = 255; + musicVol = CLIP(musicVol, 0, 255); + sfxVol = CLIP(sfxVol, 0, 255); if (_musicVolume == musicVol && _sfxVolume == sfxVol) return; _musicVolume = musicVol; - _sfxVolume = sfxVol; + _sfxVolume = sfxVol; // Now tell all the channels this. Common::StackLock lock(_mutex); diff --git a/engines/agos/sound.cpp b/engines/agos/sound.cpp index 13b5bf761c..ae2412feb4 100644 --- a/engines/agos/sound.cpp +++ b/engines/agos/sound.cpp @@ -519,7 +519,7 @@ void Sound::loadSfxTable(const char *gameFilename, uint32 base) { delete _effects; const bool dataIsUnsigned = true; - if (_vm->getPlatform() == Common::kPlatformWindows) + if (_vm->getPlatform() == Common::kPlatformWindows || (_vm->getFeatures() & GF_WAVSFX)) _effects = new WavSound(_mixer, gameFilename, base); else _effects = new VocSound(_mixer, gameFilename, dataIsUnsigned, base, false); diff --git a/engines/agos/string.cpp b/engines/agos/string.cpp index 533b04fa30..d2f7ac6bd5 100644 --- a/engines/agos/string.cpp +++ b/engines/agos/string.cpp @@ -486,7 +486,7 @@ void AGOSEngine::printScreenText(uint vgaSpriteId, uint color, const char *strin if (_variableArray[141] == 0) _variableArray[141] = 9; _variableArray[85] = _variableArray[141] * talkDelay; - + if (_language == Common::HE_ISR) _variableArray[85] += talkDelay * 2; } else { diff --git a/engines/avalanche/avalanche.cpp b/engines/avalanche/avalanche.cpp index 8726ef784a..303fb0cff8 100644 --- a/engines/avalanche/avalanche.cpp +++ b/engines/avalanche/avalanche.cpp @@ -39,9 +39,6 @@ AvalancheEngine::AvalancheEngine(OSystem *syst, const AvalancheGameDescription * _console = new AvalancheConsole(this); _rnd = new Common::RandomSource("avalanche"); - TimeDate time; - _system->getTimeAndDate(time); - _rnd->setSeed(time.tm_sec + time.tm_min + time.tm_hour); _showDebugLines = false; _clock = nullptr; @@ -60,7 +57,6 @@ AvalancheEngine::AvalancheEngine(OSystem *syst, const AvalancheGameDescription * _help = nullptr; _highscore = nullptr; - _platform = gd->desc.platform; initVariables(); } @@ -182,10 +178,6 @@ GUI::Debugger *AvalancheEngine::getDebugger() { return _console; } -Common::Platform AvalancheEngine::getPlatform() const { - return _platform; -} - bool AvalancheEngine::hasFeature(EngineFeature f) const { return (f == kSupportsSavingDuringRuntime) || (f == kSupportsLoadingDuringRuntime); } diff --git a/engines/avalanche/avalanche.h b/engines/avalanche/avalanche.h index 6eb5e675cc..d50373b07a 100644 --- a/engines/avalanche/avalanche.h +++ b/engines/avalanche/avalanche.h @@ -48,10 +48,10 @@ #include "avalanche/mainmenu.h" #include "avalanche/highscore.h" +#include "common/error.h" #include "common/serializer.h" #include "engines/engine.h" -#include "engines/advancedDetector.h" #include "graphics/cursorman.h" @@ -61,9 +61,7 @@ class RandomSource; namespace Avalanche { -struct AvalancheGameDescription { - ADGameDescription desc; -}; +struct AvalancheGameDescription; static const int kSavegameVersion = 2; @@ -132,7 +130,6 @@ protected: private: AvalancheConsole *_console; - Common::Platform _platform; public: // For Thinkabout: diff --git a/engines/avalanche/detection.cpp b/engines/avalanche/detection.cpp index def395b77f..7f3d4f88a8 100644 --- a/engines/avalanche/detection.cpp +++ b/engines/avalanche/detection.cpp @@ -35,6 +35,10 @@ namespace Avalanche { +struct AvalancheGameDescription { + ADGameDescription desc; +}; + uint32 AvalancheEngine::getFeatures() const { return _gameDescription->desc.flags; } @@ -43,6 +47,10 @@ const char *AvalancheEngine::getGameId() const { return _gameDescription->desc.gameId; } +Common::Platform AvalancheEngine::getPlatform() const { + return _gameDescription->desc.platform; +} + static const PlainGameDescriptor avalancheGames[] = { {"avalanche", "Lord Avalot d'Argent"}, {0, 0} @@ -193,7 +201,12 @@ SaveStateDescriptor AvalancheMetaEngine::querySaveMetaInfos(const char *target, SaveStateDescriptor desc(slot, description); - Graphics::Surface *const thumbnail = Graphics::loadThumbnail(*f); + Graphics::Surface *thumbnail; + if (!Graphics::loadThumbnail(*f, thumbnail)) { + warning("Cannot read thumbnail data, possibly broken savegame"); + delete f; + return SaveStateDescriptor(); + } desc.setThumbnail(thumbnail); delete f; diff --git a/engines/avalanche/dropdown.cpp b/engines/avalanche/dropdown.cpp index 97adfc2581..ba4e452aaa 100644 --- a/engines/avalanche/dropdown.cpp +++ b/engines/avalanche/dropdown.cpp @@ -684,9 +684,9 @@ void DropDownMenu::update() { Common::Point cursorPos = _vm->getMousePos(); while (!_activeMenuItem._activeNow && (cursorPos.y <= 21) && _vm->_holdLeftMouse) { _menuBar.chooseMenuItem(cursorPos.x); - do + do { _vm->updateEvents(); - while (_vm->_holdLeftMouse && !_vm->shouldQuit()); + } while (_vm->_holdLeftMouse && !_vm->shouldQuit()); while (!_vm->shouldQuit()) { do { diff --git a/engines/bbvs/bbvs.h b/engines/bbvs/bbvs.h index 9fb6b9cac3..a9d37c2551 100644 --- a/engines/bbvs/bbvs.h +++ b/engines/bbvs/bbvs.h @@ -417,7 +417,7 @@ public: const char *getSavegameFilename(int num); bool existsSavegame(int num); static Common::String getSavegameFilename(const Common::String &target, int num); - static kReadSaveHeaderError readSaveHeader(Common::SeekableReadStream *in, bool loadThumbnail, SaveHeader &header); + WARN_UNUSED_RESULT static kReadSaveHeaderError readSaveHeader(Common::SeekableReadStream *in, SaveHeader &header, bool skipThumbnail = true); void allocSnapshot(); void freeSnapshot(); diff --git a/engines/bbvs/detection.cpp b/engines/bbvs/detection.cpp index 1b2c644dda..b30c6d3f2d 100644 --- a/engines/bbvs/detection.cpp +++ b/engines/bbvs/detection.cpp @@ -124,7 +124,7 @@ SaveStateList BbvsMetaEngine::listSaves(const char *target) const { if (slotNum >= 0 && slotNum <= 999) { Common::InSaveFile *in = saveFileMan->openForLoading(file->c_str()); if (in) { - if (Bbvs::BbvsEngine::readSaveHeader(in, false, header) == Bbvs::BbvsEngine::kRSHENoError) { + if (Bbvs::BbvsEngine::readSaveHeader(in, header) == Bbvs::BbvsEngine::kRSHENoError) { saveList.push_back(SaveStateDescriptor(slotNum, header.description)); } delete in; @@ -142,7 +142,7 @@ SaveStateDescriptor BbvsMetaEngine::querySaveMetaInfos(const char *target, int s if (in) { Bbvs::BbvsEngine::SaveHeader header; Bbvs::BbvsEngine::kReadSaveHeaderError error; - error = Bbvs::BbvsEngine::readSaveHeader(in, true, header); + error = Bbvs::BbvsEngine::readSaveHeader(in, header, false); delete in; if (error == Bbvs::BbvsEngine::kRSHENoError) { SaveStateDescriptor desc(slot, header.description); diff --git a/engines/bbvs/saveload.cpp b/engines/bbvs/saveload.cpp index 74c255c860..d4782aad39 100644 --- a/engines/bbvs/saveload.cpp +++ b/engines/bbvs/saveload.cpp @@ -27,7 +27,7 @@ namespace Bbvs { -BbvsEngine::kReadSaveHeaderError BbvsEngine::readSaveHeader(Common::SeekableReadStream *in, bool loadThumbnail, SaveHeader &header) { +WARN_UNUSED_RESULT BbvsEngine::kReadSaveHeaderError BbvsEngine::readSaveHeader(Common::SeekableReadStream *in, SaveHeader &header, bool skipThumbnail) { header.version = in->readUint32LE(); if (header.version > BBVS_SAVEGAME_VERSION) @@ -38,10 +38,8 @@ BbvsEngine::kReadSaveHeaderError BbvsEngine::readSaveHeader(Common::SeekableRead while (descriptionLen--) header.description += (char)in->readByte(); - if (loadThumbnail) { - header.thumbnail = Graphics::loadThumbnail(*in); - } else { - Graphics::skipThumbnail(*in); + if (!Graphics::loadThumbnail(*in, header.thumbnail, skipThumbnail)) { + return kRSHEIoError; } // Not used yet, reserved for future usage @@ -101,7 +99,7 @@ void BbvsEngine::loadgame(const char *filename) { SaveHeader header; - kReadSaveHeaderError errorCode = readSaveHeader(in, false, header); + kReadSaveHeaderError errorCode = readSaveHeader(in, header); if (errorCode != kRSHENoError) { warning("Error loading savegame '%s'", filename); diff --git a/engines/bladerunner/actor.cpp b/engines/bladerunner/actor.cpp index d4c4eeaee3..f9446aae0a 100644 --- a/engines/bladerunner/actor.cpp +++ b/engines/bladerunner/actor.cpp @@ -22,16 +22,17 @@ #include "bladerunner/actor.h" -#include "bladerunner/bladerunner.h" #include "bladerunner/actor_clues.h" #include "bladerunner/actor_combat.h" #include "bladerunner/actor_walk.h" #include "bladerunner/audio_speech.h" +#include "bladerunner/bladerunner.h" #include "bladerunner/boundingbox.h" #include "bladerunner/game_info.h" #include "bladerunner/items.h" #include "bladerunner/mouse.h" #include "bladerunner/movement_track.h" +#include "bladerunner/savefile.h" #include "bladerunner/scene.h" #include "bladerunner/scene_objects.h" #include "bladerunner/script/scene_script.h" @@ -51,7 +52,6 @@ Actor::Actor(BladeRunnerEngine *vm, int actorId) { _walkInfo = new ActorWalk(vm); _movementTrack = new MovementTrack(); _clues = new ActorClues(vm, (actorId && actorId != 99) ? 2 : 4); - _bbox = new BoundingBox(); _combatInfo = new ActorCombat(vm); _friendlinessToOther.resize(_vm->_gameInfo->getActorCount()); @@ -64,7 +64,6 @@ Actor::Actor(BladeRunnerEngine *vm, int actorId) { Actor::~Actor() { delete _combatInfo; - delete _bbox; delete _clues; delete _movementTrack; delete _walkInfo; @@ -97,6 +96,8 @@ void Actor::setup(int actorId) { _retiredHeight = 0; _scale = 1.0f; + _timer4RemainDefault = 60000; + _movementTrackWalkingToWaypointId = -1; _movementTrackDelayOnNextWaypoint = -1; @@ -120,9 +121,6 @@ void Actor::setup(int actorId) { _movementTrackNextAngle = -1; _movementTrackNextRunning = false; - // Timer for exchanging clues - _timersLeft[4] = 60000; - _animationMode = -1; _screenRectangle = Common::Rect(-1, -1, -1, -1); @@ -226,6 +224,8 @@ void Actor::timerUpdate(int timerId) { break; case 4: // Exchange clues between actors + acquireCluesByRelations(); + _timersLeft[4] = _timer4RemainDefault; break; case 5: // Actor animation frame timer @@ -354,7 +354,7 @@ void Actor::setAtXYZ(const Vector3 &position, int facing, bool snapFacing, bool _vm->_sceneObjects->remove(_id + kSceneObjectOffsetActors); if (_vm->_scene->getSetId() == _setId) { - _vm->_sceneObjects->addActor(_id + kSceneObjectOffsetActors, _bbox, &_screenRectangle, true, moving, _isTarget, retired); + _vm->_sceneObjects->addActor(_id + kSceneObjectOffsetActors, _bbox, _screenRectangle, true, moving, _isTarget, retired); } } @@ -468,7 +468,7 @@ bool Actor::loopWalkToItem(int itemId, int destinationOffset, int interruptible, return loopWalk(itemPosition, destinationOffset, interruptible, runFlag, _position, width, 24.0f, a5, isRunningFlag, false); } -bool Actor::loopWalkToSceneObject(const char *objectName, int destinationOffset, bool interruptible, bool runFlag, bool a5, bool *isRunningFlag) { +bool Actor::loopWalkToSceneObject(const Common::String &objectName, int destinationOffset, bool interruptible, bool runFlag, bool a5, bool *isRunningFlag) { int sceneObject = _vm->_scene->_set->findObject(objectName); if (sceneObject < 0) { return true; @@ -671,6 +671,12 @@ bool Actor::tick(bool forceDraw, Common::Rect *screenRect) { return isVisible; } +void Actor::tickCombat() { + if (_id != kActorMcCoy && !_isRetired && _inCombat) { + _combatInfo->tick(); + } +} + bool Actor::draw(Common::Rect *screenRect) { Vector3 drawPosition(_position.x, -_position.z, _position.y + 2.0); float drawAngle = M_PI - _facing * (M_PI / 512.0f); @@ -753,21 +759,21 @@ void Actor::setFacing(int facing, bool halfOrSet) { void Actor::setBoundingBox(const Vector3 &position, bool retired) { if (retired) { - _bbox->setXYZ(position.x - (_retiredWidth / 2.0f), - position.y, - position.z - (_retiredWidth / 2.0f), + _bbox.setXYZ(position.x - (_retiredWidth / 2.0f), + position.y, + position.z - (_retiredWidth / 2.0f), - position.x + (_retiredWidth / 2.0f), - position.y + _retiredHeight, - position.z + (_retiredWidth / 2.0f)); + position.x + (_retiredWidth / 2.0f), + position.y + _retiredHeight, + position.z + (_retiredWidth / 2.0f)); } else { - _bbox->setXYZ(position.x - 12.0f, - position.y + 6.0f, - position.z - 12.0f, + _bbox.setXYZ(position.x - 12.0f, + position.y + 6.0f, + position.z - 12.0f, - position.x + 12.0f, - position.y + 72.0f, - position.z + 12.0f); + position.x + 12.0f, + position.y + 72.0f, + position.z + 12.0f); } } @@ -813,7 +819,7 @@ void Actor::faceActor(int otherActorId, bool animate) { faceXYZ(otherActor->_position, animate); } -void Actor::faceObject(const char *objectName, bool animate) { +void Actor::faceObject(const Common::String &objectName, bool animate) { int objectId = _vm->_scene->findObject(objectName); if (objectId == -1) { return; @@ -880,6 +886,21 @@ void Actor::setFriendlinessToOther(int otherActorId, int friendliness) { _friendlinessToOther[otherActorId] = friendliness; } +bool Actor::checkFriendlinessAndHonesty(int otherActorId) { + int honestyDiff = 2 * _friendlinessToOther[otherActorId] - _honesty; + int friendlinessRange; + + if (honestyDiff > 30) { + friendlinessRange = 100; + } else if (honestyDiff >= 0 && honestyDiff <= 30) { + friendlinessRange = 50; + } else { + friendlinessRange = 0; + } + + return _vm->_rnd.getRandomNumberRng(1, 100) <= friendlinessRange; +} + void Actor::setHonesty(int honesty) { _honesty = honesty; } @@ -904,16 +925,6 @@ void Actor::setImmunityToObstacles(bool isImmune) { _isImmuneToObstacles = isImmune; } -void Actor::modifyCurrentHP(signed int change) { - _currentHP = CLIP(_currentHP + change, 0, 100); - if (_currentHP > 0) - retire(false, 0, 0, -1); -} - -void Actor::modifyMaxHP(signed int change) { - _maxHP = CLIP(_maxHP + change, 0, 100); -} - void Actor::modifyCombatAggressiveness(signed int change) { _combatAggressiveness = CLIP(_combatAggressiveness + change, 0, 100); } @@ -955,6 +966,13 @@ void Actor::setTarget(bool target) { _isTarget = target; } +void Actor::setCurrentHP(int hp) { + _currentHP = hp; + if (hp > 0) { + retire(false, 0, 0, -1); + } +} + void Actor::setHealth(int hp, int maxHp) { _currentHP = hp; _maxHP = maxHp; @@ -963,18 +981,29 @@ void Actor::setHealth(int hp, int maxHp) { } } -void Actor::combatModeOn(int a2, int a3, int otherActorId, int a5, int animationModeCombatIdle, int animationModeCombatWalk, int animationModeCombatRun, int a9, int a10, int a11, int ammoDamage, int a13, int a14) { +void Actor::modifyCurrentHP(signed int change) { + _currentHP = CLIP(_currentHP + change, 0, 100); + if (_currentHP > 0) { + retire(false, 0, 0, -1); + } +} + +void Actor::modifyMaxHP(signed int change) { + _maxHP = CLIP(_maxHP + change, 0, 100); +} + + +void Actor::combatModeOn(int initialState, bool rangedAttack, int enemyId, int waypointType, int animationModeCombatIdle, int animationModeCombatWalk, int animationModeCombatRun, int fleeRatio, int coverRatio, int actionRatio, int damage, int range, bool unstoppable) { _animationModeCombatIdle = animationModeCombatIdle; _animationModeCombatWalk = animationModeCombatWalk; _animationModeCombatRun = animationModeCombatRun; _inCombat = true; if (_id != kActorMcCoy) { - _combatInfo->combatOn(_id, a2, a3, otherActorId, a5, a9, a10, a11, ammoDamage, a13, a14); + _combatInfo->combatOn(_id, initialState, rangedAttack, enemyId, waypointType, fleeRatio, coverRatio, actionRatio, damage, range, unstoppable); } stopWalking(false); changeAnimationMode(_animationModeCombatIdle, false); - int i; - for (i = 0; i < (int)_vm->_gameInfo->getActorCount(); i++) { + for (int i = 0; i < (int)_vm->_gameInfo->getActorCount(); i++) { Actor *otherActor = _vm->_actors[i]; if (i != _id && otherActor->_setId == _setId && !otherActor->_isRetired) { _vm->_aiScripts->otherAgentEnteredCombatMode(i, _id, true); @@ -989,8 +1018,7 @@ void Actor::combatModeOff() { _inCombat = false; stopWalking(false); changeAnimationMode(kAnimationModeIdle, false); - int i; - for (i = 0; i < (int)_vm->_gameInfo->getActorCount(); i++) { + for (int i = 0; i < (int)_vm->_gameInfo->getActorCount(); i++) { Actor *otherActor = _vm->_actors[i]; if (i != _id && otherActor->_setId == _setId && !otherActor->_isRetired) { _vm->_aiScripts->otherAgentEnteredCombatMode(i, _id, false); @@ -1002,6 +1030,16 @@ float Actor::distanceFromActor(int otherActorId) { return (_position - _vm->_actors[otherActorId]->_position).length(); } +int Actor::angleTo(const Vector3 &target) const { + int angle = angle_1024(_position.x, _position.z, target.x, target.z) - _facing; + if (angle < -512) { + angle += 1024; + } else if (angle > 512) { + angle -= 1024; + } + return angle; +} + float Actor::getX() const { return _position.x; } @@ -1014,10 +1052,8 @@ float Actor::getZ() const { return _position.z; } -void Actor::getXYZ(float *x, float *y, float *z) const { - *x = _position.x; - *y = _position.y; - *z = _position.z; +Vector3 Actor::getXYZ() const { + return _position; } int Actor::getFacing() const { @@ -1044,13 +1080,10 @@ int Actor::getGoal() const { } void Actor::speechPlay(int sentenceId, bool voiceOver) { - char name[13]; - sprintf(name, "%02d-%04d%s.AUD", _id, sentenceId, _vm->_languageCode); - int balance; + Common::String name = Common::String::format( "%02d-%04d%s.AUD", _id, sentenceId, _vm->_languageCode.c_str()); - if (voiceOver || _id == BladeRunnerEngine::kActorVoiceOver) { - balance = 0; - } else { + int balance = 0; + if (!voiceOver && _id != BladeRunnerEngine::kActorVoiceOver) { // Vector3 pos = _vm->_view->_frameViewMatrix * _position; int screenX = 320; //, screenY = 0; //TODO: transform to screen space using fov; @@ -1102,6 +1135,18 @@ void Actor::copyClues(int actorId) { } } +void Actor::acquireCluesByRelations() { + if (_setId >= 0 && _setId != kSetFreeSlotG && _setId != _vm->_actors[0]->_setId) { + for (int i = 0; i < _vm->_gameInfo->getActorCount(); i++) { + if (i != _id && _vm->_actors[i]->_setId == _setId && i && _id + && checkFriendlinessAndHonesty(i) + && _vm->_actors[i]->checkFriendlinessAndHonesty(_id)) { + _clues->acquireCluesByRelations(_id, i); + } + } + } +} + int Actor::soundVolume() const { float dist = distanceFromView(_vm->_view); return 35.0f * CLIP(1.0f - (dist / 1200.0f), 0.0f, 1.0f); @@ -1112,8 +1157,8 @@ int Actor::soundBalance() const { return 35.0f * (CLIP(screenPosition.x / 640.0f, 0.0f, 1.0f) * 2.0f - 1.0f); } -bool Actor::isObstacleBetween(float targetX, float targetZ) { - return _vm->_sceneObjects->isObstacleBetween(_position.x, _position.z, targetX, targetZ, _position.y, -1); +bool Actor::isObstacleBetween(const Vector3 &target) { + return _vm->_sceneObjects->isObstacleBetween(_position, target, -1); } int Actor::findTargetUnderMouse(BladeRunnerEngine *vm, int mouseX, int mouseY) { @@ -1204,4 +1249,157 @@ bool Actor::walkToNearestPoint(const Vector3 &destination, float distance) { return false; } +void Actor::save(SaveFileWriteStream &f) { + f.writeInt(_id); + f.writeInt(_setId); + f.writeVector3(_position); + f.writeInt(_facing); + f.writeInt(_targetFacing); + f.writeInt(_timer4RemainDefault); + + f.writeInt(_honesty); + f.writeInt(_intelligence); + f.writeInt(_stability); + f.writeInt(_combatAggressiveness); + f.writeInt(_goalNumber); + + f.writeInt(_currentHP); + f.writeInt(_maxHP); + + f.writeBool(_movementTrackPaused); + f.writeInt(_movementTrackNextWaypointId); + f.writeInt(_movementTrackNextDelay); + f.writeInt(_movementTrackNextAngle); + f.writeBool(_movementTrackNextRunning); + + f.writeInt(0); // TODO: _clueType + f.writeBool(_isMoving); + f.writeBool(_isTarget); + f.writeBool(_inCombat); + f.writeBool(_isInvisible); + f.writeBool(_isRetired); + f.writeBool(_isImmuneToObstacles); + + f.writeInt(_animationMode); + f.writeInt(_fps); + f.writeInt(_frameMs); + f.writeInt(_animationId); + f.writeInt(_animationFrame); + + f.writeInt(_movementTrackWalkingToWaypointId); + f.writeInt(_movementTrackDelayOnNextWaypoint); + + f.writeRect(_screenRectangle); + f.writeInt(_retiredWidth); + f.writeInt(_retiredHeight); + f.writeInt(_damageAnimIfMoving); + f.writeInt(0); // TODO: _actorFieldU6 + f.writeInt(0); // TODO: _actorFieldU7 + f.writeFloat(_scale); + + for (int i = 0; i < 7; ++i) { + f.writeInt(_timersLeft[i]); + } + + uint32 now = _vm->getTotalPlayTime(); // TODO: should be last lock time + for (int i = 0; i < 7; ++i) { + f.writeInt(_timersLast[i] - now); + } + + int actorCount = _vm->_gameInfo->getActorCount(); + for (int i = 0; i != actorCount; ++i) { + f.writeInt(_friendlinessToOther[i]); + } + + _clues->save(f); + + _movementTrack->save(f); + + _walkInfo->save(f); + + f.writeBoundingBox(_bbox); + + _combatInfo->save(f); + f.writeInt(_animationModeCombatIdle); + f.writeInt(_animationModeCombatWalk); + f.writeInt(_animationModeCombatRun); +} + +void Actor::load(SaveFileReadStream &f) { + _id = f.readInt(); + _setId = f.readInt(); + _position = f.readVector3(); + _facing = f.readInt(); + _targetFacing = f.readInt(); + _timer4RemainDefault = f.readInt(); + + _honesty = f.readInt(); + _intelligence = f.readInt(); + _stability = f.readInt(); + _combatAggressiveness = f.readInt(); + _goalNumber = f.readInt(); + + _currentHP = f.readInt(); + _maxHP = f.readInt(); + + _movementTrackPaused = f.readBool(); + _movementTrackNextWaypointId = f.readInt(); + _movementTrackNextDelay = f.readInt(); + _movementTrackNextAngle = f.readInt(); + _movementTrackNextRunning = f.readBool(); + + f.skip(4); // TODO: _clueType + _isMoving = f.readBool(); + _isTarget = f.readBool(); + _inCombat = f.readBool(); + _isInvisible = f.readBool(); + _isRetired = f.readBool(); + _isImmuneToObstacles = f.readBool(); + + _animationMode = f.readInt(); + _fps = f.readInt(); + _frameMs = f.readInt(); + _animationId = f.readInt(); + _animationFrame = f.readInt(); + + _movementTrackWalkingToWaypointId = f.readInt(); + _movementTrackDelayOnNextWaypoint = f.readInt(); + + _screenRectangle = f.readRect(); + _retiredWidth = f.readInt(); + _retiredHeight = f.readInt(); + _damageAnimIfMoving = f.readInt(); + f.skip(4); // TODO: _actorFieldU6 + f.skip(4); // TODO: _actorFieldU7 + _scale = f.readFloat(); + + for (int i = 0; i < 7; ++i) { + _timersLeft[i] = f.readInt(); + } + + uint32 now = _vm->getTotalPlayTime(); // TODO: should be last lock time + for (int i = 0; i < 7; ++i) { + _timersLast[i] = f.readInt() + now; + } + + int actorCount = _vm->_gameInfo->getActorCount(); + for (int i = 0; i != actorCount; ++i) { + _friendlinessToOther[i] = f.readInt(); + } + + _clues->load(f); + + _movementTrack->load(f); + + _walkInfo->load(f); + + _bbox = f.readBoundingBox(); + + _combatInfo->load(f); + + _animationModeCombatIdle = f.readInt(); + _animationModeCombatWalk = f.readInt(); + _animationModeCombatRun = f.readInt(); +} + } // End of namespace BladeRunner diff --git a/engines/bladerunner/actor.h b/engines/bladerunner/actor.h index af0c14e367..6540c0593a 100644 --- a/engines/bladerunner/actor.h +++ b/engines/bladerunner/actor.h @@ -23,6 +23,7 @@ #ifndef BLADERUNNER_ACTOR_H #define BLADERUNNER_ACTOR_H +#include "bladerunner/boundingbox.h" #include "bladerunner/vector.h" #include "common/array.h" @@ -36,21 +37,22 @@ class ActorWalk; class BladeRunnerEngine; class BoundingBox; class MovementTrack; +class SaveFileReadStream; +class SaveFileWriteStream; class View; class Actor { - friend class ScriptBase; - friend class KIA; - BladeRunnerEngine *_vm; -private: - BoundingBox *_bbox; +public: + BoundingBox _bbox; Common::Rect _screenRectangle; MovementTrack *_movementTrack; ActorWalk *_walkInfo; ActorCombat *_combatInfo; + ActorClues *_clues; +private: int _honesty; int _intelligence; int _stability; @@ -61,8 +63,6 @@ private: int _currentHP; int _maxHP; - ActorClues *_clues; - int _id; int _setId; Vector3 _position; @@ -70,6 +70,8 @@ private: int _targetFacing; int _walkboxId; + int _timer4RemainDefault; + // Flags bool _isTarget; bool _isInvisible; @@ -124,7 +126,7 @@ public: float getX() const; float getY() const; float getZ() const; - void getXYZ(float *x, float *y, float *z) const; + Vector3 getXYZ() const; int getFacing() const; int getAnimationMode() const; @@ -149,7 +151,7 @@ public: bool walkTo(bool runFlag, const Vector3 &destination, bool a3); bool loopWalkToActor(int otherActorId, int destinationOffset, int interruptible, bool runFlag, bool a5, bool *isRunningFlag); bool loopWalkToItem(int itemId, int destinationOffset, int interruptible, bool runFlag, bool a5, bool *isRunningFlag); - bool loopWalkToSceneObject(const char *objectName, int destinationOffset, bool interruptible, bool runFlag, bool a5, bool *isRunningFlag); + bool loopWalkToSceneObject(const Common::String &objectName, int destinationOffset, bool interruptible, bool runFlag, bool a5, bool *isRunningFlag); bool loopWalkToWaypoint(int waypointId, int destinationOffset, int interruptible, bool runFlag, bool a5, bool *isRunningFlag); bool loopWalkToXYZ(const Vector3 &destination, int destinationOffset, bool interruptible, bool runFlag, bool a5, bool *isRunningFlag); bool asyncWalkToWaypoint(int waypointId, int destinationOffset, bool runFlag, bool a5); @@ -157,60 +159,83 @@ public: void run(); bool tick(bool forceUpdate, Common::Rect *screenRect); + void tickCombat(); bool draw(Common::Rect *screenRect); int getSetId() const; void setSetId(int setId); - BoundingBox *getBoundingBox() const { return _bbox; } - Common::Rect *getScreenRectangle() { return &_screenRectangle; } + const BoundingBox &getBoundingBox() const { return _bbox; } + const Common::Rect &getScreenRectangle() { return _screenRectangle; } int getWalkbox() const { return _walkboxId; } + bool isRetired() const { return _isRetired; } bool isTarget() const { return _isTarget; } void setTarget(bool targetable); bool isImmuneToObstacles() const { return _isImmuneToObstacles; } bool inCombat() const { return _inCombat; } + bool isMoving() const { return _isMoving; } void setMoving(bool value) { _isMoving = value; } + bool inWalkLoop() const { return _inWalkLoop; } bool isWalking() const; bool isRunning() const; void stopWalking(bool value); void faceActor(int otherActorId, bool animate); - void faceObject(const char *objectName, bool animate); + void faceObject(const Common::String &objectName, bool animate); void faceItem(int itemId, bool animate); void faceWaypoint(int waypointId, bool animate); void faceXYZ(float x, float y, float z, bool animate); void faceXYZ(const Vector3 &pos, bool animate); void faceCurrentCamera(bool animate); void faceHeading(int heading, bool animate); - void modifyFriendlinessToOther(int otherActorId, signed int change); + void setFacing(int facing, bool halfOrSet = true); + + int getCurrentHP() const { return _currentHP; } + int getMaxHP() const { return _maxHP; } + void setCurrentHP(int hp); + void setHealth(int hp, int maxHp); + void modifyCurrentHP(signed int change); + void modifyMaxHP(signed int change); + + int getFriendlinessToOther(int otherActorId) const { return _friendlinessToOther[otherActorId]; } void setFriendlinessToOther(int otherActorId, int friendliness); + void modifyFriendlinessToOther(int otherActorId, signed int change); + bool checkFriendlinessAndHonesty(int otherActorId); + + int getHonesty() const { return _honesty; } void setHonesty(int honesty); + void modifyHonesty(signed int change); + + int getIntelligence() const { return _intelligence; } void setIntelligence(int intelligence); + void modifyIntelligence(signed int change); + + int getStability() const { return _stability; } void setStability(int stability); + void modifyStability(signed int change); + + int getCombatAggressiveness() const { return _combatAggressiveness; } void setCombatAggressiveness(int combatAggressiveness); + void modifyCombatAggressiveness(signed int change); + void setInvisible(bool isInvisible); void setImmunityToObstacles(bool isImmune); - void modifyCurrentHP(signed int change); - void modifyMaxHP(signed int change); - void modifyCombatAggressiveness(signed int change); - void modifyHonesty(signed int change); - void modifyIntelligence(signed int change); - void modifyStability(signed int change); + void setFlagDamageAnimIfMoving(bool value); - bool getFlagDamageAnimIfMoving() const; - void setHealth(int hp, int maxHp); + bool getFlagDamageAnimIfMoving() const; void retire(bool isRetired, int width, int height, int retiredByActorId); - void combatModeOn(int a2, int a3, int a4, int a5, int combatAnimationMode, int a7, int a8, int a9, int a10, int a11, int a12, int a13, int a14); + void combatModeOn(int initialState, bool rangedAttack, int enemyId, int waypointType, int animationModeCombatIdle, int animationModeCombatWalk, int animationModeCombatRun, int fleeRatio, int coverRatio, int actionRatio, int damage, int range, bool unstoppable); void combatModeOff(); void setGoal(int goalNumber); int getGoal() const; float distanceFromActor(int otherActorId); + int angleTo(const Vector3 &target) const; void speechPlay(int sentenceId, bool voiceOver); void speechStop(); @@ -221,15 +246,19 @@ public: void loseClue(int clueId); bool hasClue(int clueId) const; void copyClues(int actorId); + void acquireCluesByRelations(); int soundVolume() const; int soundBalance() const; - bool isObstacleBetween(float targetX, float targetZ); + bool isObstacleBetween(const Vector3 &target); + + void save(SaveFileWriteStream &f); + void load(SaveFileReadStream &f); static int findTargetUnderMouse(BladeRunnerEngine *vm, int mouseX, int mouseY); + private: - void setFacing(int facing, bool halfOrSet = true); void setBoundingBox(const Vector3 &position, bool retired); float distanceFromView(View *view) const; diff --git a/engines/bladerunner/actor_clues.cpp b/engines/bladerunner/actor_clues.cpp index 39fbc77d4e..b34e67b46e 100644 --- a/engines/bladerunner/actor_clues.cpp +++ b/engines/bladerunner/actor_clues.cpp @@ -21,10 +21,13 @@ */ #include "bladerunner/actor_clues.h" +#include "bladerunner/actor.h" +#include "bladerunner/script/ai_script.h" #include "bladerunner/bladerunner.h" #include "bladerunner/game_info.h" #include "bladerunner/crimes_database.h" +#include "bladerunner/savefile.h" #include "common/debug.h" @@ -87,6 +90,147 @@ bool ActorClues::isAcquired(int clueId) const { #endif } +int ActorClues::getWeight(int clueId) const { + int clueIndex = findClueIndex(clueId); + if (clueIndex == -1) { + return 0; + } + return _clues[clueIndex].weight; +} + +int ActorClues::getModifier(int actorId, int otherActorId, int clueId) { + Actor *actor = _vm->_actors[actorId]; + int modifier1, modifier2, modifier3, modifier4; + + int friendliness = actor->getFriendlinessToOther(otherActorId); + int clueWeight = actor->_clues->getWeight(clueId); + + if (actor->_clues->isFlag2(clueId)) { + modifier1 = 100 - actor->getHonesty() - friendliness; + } else { + modifier1 = 0; + } + modifier2 = 0; + modifier3 = _vm->_aiScripts->callGetFriendlinessModifierIfGetsClue(otherActorId, actorId, clueId); + + for (int i = 0; i < _vm->_gameInfo->getActorCount(); i++) { + if (i != actorId && i != otherActorId) { + modifier2 += (friendliness - 50) * _vm->_aiScripts->callGetFriendlinessModifierIfGetsClue(i, otherActorId, clueId) / 100; + } + } + modifier4 = _vm->_rnd.getRandomNumberRng(0, (100 - actor->getIntelligence()) / 10); + + if (_vm->_rnd.getRandomNumberRng(0, 1) == 1) { + modifier4 = -modifier4; + } + + return modifier1 + modifier2 + modifier3 + modifier4 + clueWeight; +} + +static int cluesCompare(const void *p1, const void *p2) { + const ActorClues::CluesUS *clue1 = (const ActorClues::CluesUS *)p1; + const ActorClues::CluesUS *clue2 = (const ActorClues::CluesUS *)p2; + + if (clue1->modifier > clue2->modifier) + return -1; + + return (clue1->modifier < clue2->modifier); +} + +void ActorClues::acquireCluesByRelations(int actorId, int otherActorId) { + CluesUS clues1[kClueCount], clues2[kClueCount]; + + int count1 = findAcquirableCluesFromActor(actorId, otherActorId, clues1, kClueCount); + int count2 = findAcquirableCluesFromActor(otherActorId, actorId, clues2, kClueCount); + + if (count1 || count2) { + for (int i = 0; i < count1; i++) { + clues1[i].modifier = getModifier(actorId, otherActorId, clues1[i].clueId); + } + qsort(clues1, count1, sizeof(CluesUS), cluesCompare); + + for (int i = 0; i < count2; i++) { + clues2[i].modifier = getModifier(otherActorId, actorId, clues2[i].clueId); + } + qsort(clues2, count2, sizeof(CluesUS), cluesCompare); + + Actor *actor = _vm->_actors[actorId]; + Actor *otherActor = _vm->_actors[otherActorId]; + + int avgParameters = (otherActor->getHonesty() + otherActor->getIntelligence() + actor->getFriendlinessToOther(otherActorId)) / 3; + int clue1count = avgParameters * count1 / 100; + + if (avgParameters >= 50 && !clue1count && count1 == 1) { + clue1count = 1; + } + + avgParameters = (actor->getHonesty() + actor->getIntelligence() + otherActor->getFriendlinessToOther(actorId)) / 3; + int clue2count = avgParameters * count2 / 100; + + if (avgParameters >= 50 && !clue2count && count2 == 1) { + clue2count = 1; + } + + for (int i = 0; i < clue2count; i++) { + bool flag = false; + if (otherActor->_clues->isFlag2(clues2[i].clueId)) { + avgParameters = (2 * otherActor->getFriendlinessToOther(actorId) + otherActor->getHonesty()) / 3; + + if (avgParameters > 70) { + avgParameters = 100; + } else if (avgParameters < 30) { + avgParameters = 0; + } + if (_vm->_rnd.getRandomNumberRng(1, 100) <= avgParameters) { + flag = true; + } + } + + actor->_clues->acquire(clues2[i].clueId, flag, otherActorId); + } + + for (int i = 0; i < clue1count; i++) { + bool flag = false; + if (actor->_clues->isFlag2(clues1[i].clueId)) { + avgParameters = (2 * actor->getFriendlinessToOther(otherActorId) + actor->getHonesty()) / 3; + + if (avgParameters > 70) { + avgParameters = 100; + } else if (avgParameters < 30) { + avgParameters = 0; + } + if (_vm->_rnd.getRandomNumberRng(1, 100) <= avgParameters) { + flag = true; + } + } + + otherActor->_clues->acquire(clues1[i].clueId, flag, actorId); + } + } +} + +int ActorClues::findAcquirableCluesFromActor(int actorId, int targetActorId, CluesUS *list, int size) { + Actor *actor = _vm->_actors[actorId]; + Actor *otherActor = _vm->_actors[targetActorId]; + int count = 0; + int cluesCount = actor->_clues->getCount(); + + for (int i = 0; i < cluesCount; i++) { + int clueId = actor->_clues->getClueIdByIndex(i); + + if (actor->_clues->isAcquired(clueId) + && otherActor->_clues->getWeight(clueId) > 0 + && !otherActor->_clues->isAcquired(clueId)) { + list[count].clueId = clueId; + list[count].modifier = 0; + + count++; + } + } + + return count; +} + int ActorClues::getFromActorId(int clueId) const { int clueIndex = findClueIndex(clueId); if (clueIndex == -1) { @@ -166,6 +310,10 @@ int ActorClues::getCount() const { return _count; } +int ActorClues::getClueIdByIndex(int index) const { + return _clues[index].clueId; +} + void ActorClues::removeAll() { _count = 0; for (int i = 0; i < _maxCount; ++i) { @@ -216,6 +364,44 @@ void ActorClues::remove(int index) { _clues[index].field8 = 0; } +void ActorClues::save(SaveFileWriteStream &f) { + f.writeInt(_count); + f.writeInt(_maxCount); + for (int i = 0; i < _count; ++i) { + Clue &c = _clues[i]; + f.writeInt(c.clueId); + f.writeInt(c.weight); + f.writeInt(c.fromActorId); + f.writeInt(c.field3); + f.writeInt(c.field4); + f.writeInt(c.field5); + f.writeInt(c.field6); + f.writeInt(c.field7); + f.writeInt(c.field8); + f.writeByte(c.flags); + } +} + +void ActorClues::load(SaveFileReadStream &f) { + _count = f.readInt(); + _maxCount = f.readInt(); + _clues.clear(); + _clues.resize(_maxCount); + for (int i = 0; i < _count; ++i) { + Clue &c = _clues[i]; + c.clueId = f.readInt(); + c.weight = f.readInt(); + c.fromActorId = f.readInt(); + c.field3 = f.readInt(); + c.field4 = f.readInt(); + c.field5 = f.readInt(); + c.field6 = f.readInt(); + c.field7 = f.readInt(); + c.field8 = f.readInt(); + c.flags = f.readByte(); + } +} + bool ActorClues::exists(int clueId) const { return findClueIndex(clueId) != -1; } diff --git a/engines/bladerunner/actor_clues.h b/engines/bladerunner/actor_clues.h index b2b39d8b89..76690329d3 100644 --- a/engines/bladerunner/actor_clues.h +++ b/engines/bladerunner/actor_clues.h @@ -28,8 +28,12 @@ namespace BladeRunner { class BladeRunnerEngine; +class SaveFileReadStream; +class SaveFileWriteStream; class ActorClues { + static const int kClueCount = 288; + struct Clue { int clueId; int weight; @@ -40,7 +44,7 @@ class ActorClues { int field6; int field7; int field8; - unsigned char flags; + byte flags; }; BladeRunnerEngine *_vm; @@ -50,6 +54,12 @@ class ActorClues { Common::Array<Clue> _clues; public: + struct CluesUS { + int clueId; + int modifier; + }; + +public: ActorClues(BladeRunnerEngine *_vm, int cluesType); void add(int actorId, int clueId, int unknown, bool acquired, bool unknownFlag, int fromActorId); @@ -57,6 +67,12 @@ public: void acquire(int clueId, bool flag2, int fromActorId); void lose(int clueId); bool isAcquired(int clueId) const; + int getWeight(int clueId) const; + + int getModifier(int actorId, int otherActorId, int clueId); + + void acquireCluesByRelations(int actorId, int otherActorId); + int findAcquirableCluesFromActor(int actorId, int targetActorId, CluesUS *list, int size); int getFromActorId(int clueId) const; @@ -71,11 +87,12 @@ public: int getField1(int clueId) const; int getCount() const; + int getClueIdByIndex(int index) const; void removeAll(); - //savegame - //loadgame + void save(SaveFileWriteStream &f); + void load(SaveFileReadStream &f); private: bool exists(int clueId) const; diff --git a/engines/bladerunner/actor_combat.cpp b/engines/bladerunner/actor_combat.cpp index 4bd8d17c67..4bcce5d3bf 100644 --- a/engines/bladerunner/actor_combat.cpp +++ b/engines/bladerunner/actor_combat.cpp @@ -22,25 +22,673 @@ #include "bladerunner/actor_combat.h" +#include "bladerunner/actor.h" +#include "bladerunner/audio_speech.h" +#include "bladerunner/bladerunner.h" +#include "bladerunner/combat.h" +#include "bladerunner/game_constants.h" +#include "bladerunner/game_info.h" +#include "bladerunner/movement_track.h" +#include "bladerunner/savefile.h" +#include "bladerunner/scene.h" +#include "bladerunner/scene_objects.h" +#include "bladerunner/script/ai_script.h" +#include "bladerunner/set.h" +#include "bladerunner/settings.h" + namespace BladeRunner { ActorCombat::ActorCombat(BladeRunnerEngine *vm) { _vm = vm; + reset(); } ActorCombat::~ActorCombat() { } -void ActorCombat::hitAttempt() { +void ActorCombat::setup() { + reset(); } -void ActorCombat::combatOn(int actorId, int a3, int a4, int otherActorId, int a6, int a7, int a8, int a9, int ammoDamage, int a11, int a12) { +void ActorCombat::combatOn(int actorId, int initialState, bool rangedAttackFlag, int enemyId, int waypointType, int fleeRatio, int coverRatio, int actionRatio, int damage, int range, bool unstoppable) { + _actorId = actorId; + _state = initialState; + _rangedAttack = rangedAttackFlag; + _enemyId = enemyId; + _waypointType = waypointType; + _damage = damage; + _fleeRatioConst = fleeRatio; + _coverRatioConst = coverRatio; + _actionRatioConst = actionRatio; + _fleeRatio = fleeRatio; + _coverRatio = coverRatio; + _actionRatio = actionRatio; + _active = true; + if (_rangedAttack) { + _range = range; + } else { + _range = 300; + } + _unstoppable = unstoppable; + + Actor *actor = _vm->_actors[_actorId]; + + _actorPosition = actor->getXYZ(); + _enemyPosition = _vm->_actors[_enemyId]->getXYZ(); + + actor->_movementTrack->flush(); + actor->stopWalking(false); + + if (_enemyId == kActorMcCoy) { + actor->setTarget(true); + } + + _actorHp = actor->getCurrentHP(); + + _coversWaypointCount = 0; + for (int i = 0; i < (int)_vm->_gameInfo->getCoverWaypointCount(); ++i) { + if (_vm->_combat->_coverWaypoints[i].type == waypointType && _vm->_combat->_coverWaypoints[i].setId == actor->getSetId()) { + ++_coversWaypointCount; + } + } + if (_coversWaypointCount == 0) { + _coverRatioConst = 0; + _coverRatio = 0; + } + + _fleeWaypointsCount = 0; + for (int i = 0; i < (int)_vm->_gameInfo->getFleeWaypointCount(); ++i) { + if (_vm->_combat->_fleeWaypoints[i].type == waypointType && _vm->_combat->_fleeWaypoints[i].setId == actor->getSetId()) { + ++_fleeWaypointsCount; + } + } + if (_fleeWaypointsCount == 0) { + _fleeRatioConst = 0; + _fleeRatio = 0; + } } void ActorCombat::combatOff() { + _active = false; + reset(); } -void ActorCombat::setup() { +void ActorCombat::tick() { + static int processingCounter = 0; + + if (!_active || processingCounter > 0) { + return; + } + + Actor *actor = _vm->_actors[_actorId]; + Actor *enemy = _vm->_actors[_enemyId]; + + if (actor->getSetId() != enemy->getSetId()) { + actor->combatModeOff(); + return; + } + + ++processingCounter; + + _actorPosition = actor->getXYZ(); + _enemyPosition = enemy->getXYZ(); + + if (_actionRatioConst >= 0) { + _actionRatio = _actionRatioConst; + } else { + _actionRatio = calculateActionRatio(); + } + + if (_vm->_combat->findCoverWaypoint(_waypointType, _actorId, _enemyId) != -1) { + if (_coverRatioConst >= 0) { + _coverRatio = _coverRatioConst; + } else { + _coverRatio = calculateCoverRatio(); + } + } else { + _coverRatio = 0; + } + + if (_fleeRatioConst >= 0) { + _fleeRatio = _fleeRatioConst; + } else { + _fleeRatio = calculateFleeRatio(); + } + + float dist = actor->distanceFromActor(_enemyId); + int oldState = _state; + + if (_actionRatio < _fleeRatio || _actionRatio < _coverRatio) { + if (_coverRatio >= _fleeRatio && _coverRatio >= _actionRatio) { + _state = kActorCombatStateCover; + } else { + _state = kActorCombatStateFlee; + } + } else { + if (_rangedAttack) { + if (dist > _range) { + _state = kActorCombatStateApproachRangedAttack; + } else { + if (actor->isObstacleBetween(_enemyPosition)) { + _state = kActorCombatStateUncover; + } else { + _state = kActorCombatStateRangedAttack; + } + } + } else { + if (dist > 36.0f) { + _state = kActorCombatStateApproachCloseAttack; + } else { + _state = kActorCombatStateCloseAttack; + } + } + } + + if (enemy->isRetired()) { + _state = kActorCombatStateIdle; + } + + if (actor->getAnimationMode() == kAnimationModeHit || actor->getAnimationMode() == kAnimationModeCombatHit) { + _state = kActorCombatStateIdle; + } else { + if (_state != oldState) { + actor->stopWalking(false); + } + } + switch (_state) { + case kActorCombatStateCover: + cover(); + break; + case kActorCombatStateApproachCloseAttack: + approachToCloseAttack(); + break; + case kActorCombatStateUncover: + uncover(); + break; + case kActorCombatStateAim: + aim(); + break; + case kActorCombatStateRangedAttack: + rangedAttack(); + break; + case kActorCombatStateCloseAttack: + closeAttack(); + break; + case kActorCombatStateFlee: + flee(); + break; + case kActorCombatStateApproachRangedAttack: + approachToRangedAttack(); + break; + } + --processingCounter; +} + +void ActorCombat::hitAttempt() { + Actor *actor = _vm->_actors[_actorId]; + Actor *enemy = _vm->_actors[_enemyId]; + + if (_enemyId == kActorMcCoy && !_vm->playerHasControl() && !_unstoppable) { + return; + } + + if (actor->isRetired()) { + return; + } + + int aggressiveness = 0; + if (_rangedAttack) { + aggressiveness = _rangedAttack == 1 ? getaggressivenessRangedAttack() : 0; + } else { + aggressiveness = getaggressivenessCloseAttack(); + } + + if (aggressiveness == 0) { + return; + } + + int random = _vm->_rnd.getRandomNumberRng(1, 100); + + if (random <= aggressiveness) { + if (enemy->isWalking()) { + enemy->stopWalking(true); + } + + int sentenceId = _vm->_rnd.getRandomNumberRng(0, 1) ? 9000 : 9005; + if (enemy->inCombat()) { + enemy->changeAnimationMode(22, false); + } else { + enemy->changeAnimationMode(21, false); + } + + int damage = 0; + if (_rangedAttack) { + damage = getDamageRangedAttack(random, aggressiveness); + } else { + damage = getDamageCloseAttack(random, aggressiveness); + } + + int enemyHp = MAX(enemy->getCurrentHP() - damage, 0); + enemy->setCurrentHP(enemyHp); + + if (enemyHp <= 0) { + if (!enemy->isRetired()) { + if (enemy->inCombat()) { + enemy->changeAnimationMode(49, false); + } else { + enemy->changeAnimationMode(48, false); + } + sentenceId = 9020; + } + enemy->retire(true, 6, 3, _actorId); + } + + if (_enemyId == kActorMcCoy) { + sentenceId += 900; + } + + _vm->_audioSpeech->playSpeechLine(_enemyId, sentenceId, 75, enemy->soundBalance(), 99); + } +} + +void ActorCombat::save(SaveFileWriteStream &f) { + f.writeInt(_actorId); + f.writeBool(_active); + f.writeInt(_state); + f.writeBool(_rangedAttack); + f.writeInt(_enemyId); + f.writeInt(_waypointType); + f.writeInt(_damage); + f.writeInt(_fleeRatio); + f.writeInt(_coverRatio); + f.writeInt(_actionRatio); + f.writeInt(_fleeRatioConst); + f.writeInt(_coverRatioConst); + f.writeInt(_actionRatioConst); + f.writeInt(_range); + f.writeInt(_unstoppable); + f.writeInt(_actorHp); + f.writeInt(_fleeingTowards); + f.writeVector3(_actorPosition); + f.writeVector3(_enemyPosition); + f.writeInt(_coversWaypointCount); + f.writeInt(_fleeWaypointsCount); +} + +void ActorCombat::load(SaveFileReadStream &f) { + _actorId = f.readInt(); + _active = f.readBool(); + _state = f.readInt(); + _rangedAttack = f.readBool(); + _enemyId = f.readInt(); + _waypointType = f.readInt(); + _damage = f.readInt(); + _fleeRatio = f.readInt(); + _coverRatio = f.readInt(); + _actionRatio = f.readInt(); + _fleeRatioConst = f.readInt(); + _coverRatioConst = f.readInt(); + _actionRatioConst = f.readInt(); + _range = f.readInt(); + _unstoppable = f.readInt(); + _actorHp = f.readInt(); + _fleeingTowards = f.readInt(); + _actorPosition = f.readVector3(); + _enemyPosition = f.readVector3(); + _coversWaypointCount = f.readInt(); + _fleeWaypointsCount = f.readInt(); +} + +void ActorCombat::reset() { + _active = false; + _actorId = -1; + _state = -1; + _rangedAttack = false; + _enemyId = -1; + _waypointType = -1; + _damage = 0; + _fleeRatio = -1; + _coverRatio = -1; + _actionRatio = -1; + _fleeRatioConst = -1; + _coverRatioConst = -1; + _actionRatioConst = -1; + _actorHp = 0; + _range = 300; + _unstoppable = false; + _actorPosition = Vector3(0.0f, 0.0f, 0.0f); + _enemyPosition = Vector3(0.0f, 0.0f, 0.0f); + _coversWaypointCount = 0; + _fleeWaypointsCount = 0; + _fleeingTowards = -1; +} + +void ActorCombat::cover() { + Actor *actor = _vm->_actors[_actorId]; + + if (actor->isWalking()) { + return; + } + + if (actor->isObstacleBetween(_enemyPosition)) { + faceEnemy(); + return; + } + + int coverWaypointId = _vm->_combat->findCoverWaypoint(_waypointType, _actorId, _enemyId); + if (coverWaypointId == -1) { + _state = kActorCombatStateIdle; + } else { + actor->asyncWalkToXYZ(_vm->_combat->_coverWaypoints[coverWaypointId].position, 0, true, 0); + } +} + +void ActorCombat::approachToCloseAttack() { + Actor *actor = _vm->_actors[_actorId]; + Actor *enemy = _vm->_actors[_enemyId]; + + float dist = actor->distanceFromActor(_enemyId); + if (dist > 36.0f) { + if (!actor->isWalking() || enemy->isWalking()) { + Vector3 target; + if (findClosestPositionToEnemy(target)) { + actor->asyncWalkToXYZ(target, 0, dist >= 240.0f, 0); + } else { + _state = kActorCombatStateCover; + } + } + } else { + if (actor->isWalking()) { + actor->stopWalking(false); + } + faceEnemy(); + _state = kActorCombatStateCloseAttack; + } +} + +void ActorCombat::approachToRangedAttack() { + Actor *actor = _vm->_actors[_actorId]; + Actor *enemy = _vm->_actors[_enemyId]; + + float dist = actor->distanceFromActor(_enemyId); + if (dist > _range) { + if (!actor->isWalking() || enemy->isWalking()) { + Vector3 target; + if (findClosestPositionToEnemy(target)) { + actor->asyncWalkToXYZ(target, 0, dist >= 240.0f, 0); + } else { + _state = kActorCombatStateCover; + } + } + } else { + if (actor->isWalking()) { + actor->stopWalking(false); + } + faceEnemy(); + _state = kActorCombatStateRangedAttack; + } +} + +void ActorCombat::uncover() { + Actor *actor = _vm->_actors[_actorId]; + Actor *enemy = _vm->_actors[_enemyId]; + + if (actor->isObstacleBetween(_enemyPosition)) { + actor->asyncWalkToXYZ(enemy->getXYZ(), 16, false, 0); + } else { + if (actor->isWalking()) { + actor->stopWalking(false); + } + faceEnemy(); + } +} + +void ActorCombat::aim() { + Actor *actor = _vm->_actors[_actorId]; + + if (actor->isObstacleBetween(_enemyPosition)) { + if (actor->getAnimationMode() != kAnimationModeCombatIdle) { + actor->changeAnimationMode(kAnimationModeCombatIdle, false); + } + } else { + faceEnemy(); + if (actor->getAnimationMode() != kAnimationModeCombatAim) { + actor->changeAnimationMode(kAnimationModeCombatAim, false); + } + } +} + +void ActorCombat::rangedAttack() { + Actor *actor = _vm->_actors[_actorId]; + + if (actor->isObstacleBetween(_enemyPosition) || (actor->distanceFromActor(_enemyId) > _range)) { + _state = kActorCombatStateApproachRangedAttack; + } else { + faceEnemy(); + if (actor->getAnimationMode() != kAnimationModeCombatAttack) { + if (_enemyId != kActorMcCoy || _vm->playerHasControl() || _unstoppable) { + actor->changeAnimationMode(kAnimationModeCombatAttack, false); + } + } + } +} + +void ActorCombat::closeAttack() { + Actor *actor = _vm->_actors[_actorId]; + + if (actor->isObstacleBetween(_enemyPosition) || (actor->distanceFromActor(_enemyId) > 36.0f)) { + _state = kActorCombatStateApproachCloseAttack; + } else { + faceEnemy(); + if (actor->getAnimationMode() != kAnimationModeCombatAttack) { + if (_enemyId != kActorMcCoy || _vm->playerHasControl() || _unstoppable) { + actor->changeAnimationMode(kAnimationModeCombatAttack, false); + } + } + } +} + +void ActorCombat::flee() { + Actor *actor = _vm->_actors[_actorId]; + + if (_fleeingTowards != -1 && actor->isWalking()) { + Vector3 fleeWaypointPosition = _vm->_combat->_fleeWaypoints[_fleeingTowards].position; + if (distance(_actorPosition, fleeWaypointPosition) <= 12.0f) { + _vm->_aiScripts->fledCombat(_actorId/*, _enemyId*/); + actor->setSetId(kSetFreeSlotG); + actor->combatModeOff(); + _fleeingTowards = -1; + } + } else { + int fleeWaypointId = _vm->_combat->findFleeWaypoint(actor->getSetId(), _enemyId, _actorPosition); + if (fleeWaypointId == -1) { + _state = kActorCombatStateIdle; + } else { + Vector3 fleeWaypointPosition = _vm->_combat->_fleeWaypoints[fleeWaypointId].position; + actor->asyncWalkToXYZ(fleeWaypointPosition, 0, true, 0); + _fleeingTowards = fleeWaypointId; + } + } +} + +void ActorCombat::faceEnemy() { + _vm->_actors[_actorId]->setFacing(angle_1024(_actorPosition.x, _actorPosition.z, _enemyPosition.x, _enemyPosition.z), false); +} + +int ActorCombat::getaggressivenessCloseAttack() const{ + Actor *actor = _vm->_actors[_actorId]; + Actor *enemy = _vm->_actors[_enemyId]; + + float distance = actor->distanceFromActor(_enemyId); + + if (distance > 36.0f) { + return 0; + } + + int aggressiveness = 0; + if (enemy->isRunning()) { + aggressiveness = 11; + } else if (enemy->isMoving()) { + aggressiveness = 22; + } else { + aggressiveness = 33; + } + + aggressiveness += actor->getCombatAggressiveness() / 3; + + int angle = abs(actor->angleTo(_enemyPosition)); + + if (angle > 128) { + return false; + } + + return aggressiveness + (abs(angle - 128) / 3.7f); +} + +int ActorCombat::getaggressivenessRangedAttack() const { + Actor *actor = _vm->_actors[_actorId]; + Actor *enemy = _vm->_actors[_enemyId]; + + if (actor->isObstacleBetween(_enemyPosition)) { + return 0; + } + + int distance = MIN(actor->distanceFromActor(_enemyId), 900.0f); + + int aggressiveness = 0; + if (enemy->isRunning()) { + aggressiveness = 10; + } else if (enemy->isMoving()) { + aggressiveness = 20; + } else { + aggressiveness = 30; + } + + aggressiveness += actor->getCombatAggressiveness() / 5; + return aggressiveness + abs((distance / 30) - 30) + actor->getIntelligence() / 5; +} + +int ActorCombat::getDamageCloseAttack(int min, int max) const { + if (_enemyId == kActorMcCoy && _vm->_settings->getDifficulty() == 0) { + return _damage / 2; + } + if (_enemyId == kActorMcCoy && _vm->_settings->getDifficulty() == 2) { + return _damage; + } + return ((MIN(max - min, 30) * 100.0f / 60.0f) + 50) * _damage / 100; +} + +int ActorCombat::getDamageRangedAttack(int min, int max) const { + if (_enemyId == kActorMcCoy && _vm->_settings->getDifficulty() == 0) { + return _damage / 2; + } + if (_enemyId == kActorMcCoy && _vm->_settings->getDifficulty() == 2) { + return _damage; + } + return ((MIN(max - min, 30) * 100.0f / 60.0f) + 50) * _damage / 100; +} + +int ActorCombat::calculateActionRatio() const { + Actor *actor = _vm->_actors[_actorId]; + Actor *enemy = _vm->_actors[_enemyId]; + + int aggressivenessFactor = actor->getCombatAggressiveness(); + int actorHpFactor = actor->getCurrentHP(); + int enemyHpFactor = 100 - enemy->getCurrentHP(); + int combatFactor = enemy->inCombat() ? 0 : 100; + int angleFactor = (100 * abs(enemy->angleTo(_actorPosition))) / 512; + int distanceFactor = 2 * (50 - MAX(actor->distanceFromActor(_enemyId) / 12.0f, 50.0f)); + + if (_rangedAttack) { + return + angleFactor * 0.25f + + combatFactor * 0.05f + + enemyHpFactor * 0.20f + + actorHpFactor * 0.10f + + aggressivenessFactor * 0.40f; + } else { + return + distanceFactor * 0.20f + + angleFactor * 0.10f + + combatFactor * 0.10f + + enemyHpFactor * 0.15f + + actorHpFactor * 0.15f + + aggressivenessFactor * 0.30f; + } +} + +int ActorCombat::calculateCoverRatio() const { + if (_coversWaypointCount == 0) { + return 0; + } + + Actor *actor = _vm->_actors[_actorId]; + Actor *enemy = _vm->_actors[_enemyId]; + + int angleFactor = 100 - (100 * abs(enemy->angleTo(_actorPosition))) / 512; + int actorHpFactor = 100 - actor->getCurrentHP(); + int enemyHpFactor = enemy->getCurrentHP(); + int aggressivenessFactor = 100 - actor->getCombatAggressiveness(); + int distanceFactor = 2 * MAX(actor->distanceFromActor(_enemyId) / 12.0f, 50.0f); + + if (_rangedAttack) { + return + angleFactor * 0.40f + + enemyHpFactor * 0.05f + + actorHpFactor * 0.15f + + aggressivenessFactor * 0.50f; + } else { + return + distanceFactor * 0.25f + + angleFactor * 0.20f + + enemyHpFactor * 0.05f + + actorHpFactor * 0.10f + + aggressivenessFactor * 0.50f; + } +} + +int ActorCombat::calculateFleeRatio() const { + if (_fleeWaypointsCount == 0) { + return 0; + } + + Actor *actor = _vm->_actors[_actorId]; + Actor *enemy = _vm->_actors[_enemyId]; + + int aggressivenessFactor = 100 - actor->getCombatAggressiveness(); + int actorHpFactor = 100 - actor->getCurrentHP(); + int combatFactor = enemy->inCombat() ? 100 : 0; + + return + combatFactor * 0.2f + + actorHpFactor * 0.4f + + aggressivenessFactor * 0.4f; +} + +bool ActorCombat::findClosestPositionToEnemy(Vector3 &output) const { + output = Vector3(); + + Vector3 offsets[] = { + Vector3( 0.0f, 0.0f, -28.0f), + Vector3( 28.0f, 0.0f, 0.0f), + Vector3( 0.0f, 0.0f, 28.0f), + Vector3(-28.0f, 0.0f, 0.0f) + }; + + float min = -1.0f; + + for (int i = 0; i < 4; ++i) { + Vector3 test = _enemyPosition + offsets[i]; + float dist = distance(_actorPosition, test); + if ( min == -1.0f || dist < min) { + if (!_vm->_sceneObjects->existsOnXZ(_actorId, test.x, test.z, true, true) && _vm->_scene->_set->findWalkbox(test.x, test.z) >= 0) { + output = test; + min = dist; + } + } + } + + return min >= 0.0f; } } // End of namespace BladeRunner diff --git a/engines/bladerunner/actor_combat.h b/engines/bladerunner/actor_combat.h index b7ec93533d..8f36445008 100644 --- a/engines/bladerunner/actor_combat.h +++ b/engines/bladerunner/actor_combat.h @@ -28,31 +28,33 @@ namespace BladeRunner { class BladeRunnerEngine; +class SaveFileReadStream; +class SaveFileWriteStream; class ActorCombat { BladeRunnerEngine *_vm; -// int _actorId; -// int _combatOn; -// int _field2; -// int _field3; -// int _otherActorId; -// int _field5; -// int _field6; -// int _field7; -// int _field8; -// int _field9; -// int _field10; -// int _field11; -// int _field12; -// int _actorHp; -// int _field14; -// int _field15; - Vector3 actorPosition; - Vector3 otherActorPosition; -// int _availableCoversCount; -// int _availableFleeWaypointsCount; -// int _field24; + int _actorId; + bool _active; + int _state; + bool _rangedAttack; + int _enemyId; + int _waypointType; + int _damage; + int _fleeRatio; + int _coverRatio; + int _actionRatio; + int _fleeRatioConst; + int _coverRatioConst; + int _actionRatioConst; + int _actorHp; + int _range; + bool _unstoppable; + Vector3 _actorPosition; + Vector3 _enemyPosition; + int _coversWaypointCount; + int _fleeWaypointsCount; + int _fleeingTowards; public: ActorCombat(BladeRunnerEngine *vm); @@ -60,10 +62,41 @@ public: void setup(); + void combatOn(int actorId, int initialState, bool rangedAttack, int enemyId, int waypointType, int fleeRatio, int coverRatio, int actionRatio, int damage, int range, bool unstoppable); + void combatOff(); + + void tick(); + void hitAttempt(); - void combatOn(int actorId, int a3, int a4, int otherActorId, int a6, int a7, int a8, int a9, int a10, int a11, int a12); - void combatOff(); + void save(SaveFileWriteStream &f); + void load(SaveFileReadStream &f); + +private: + void reset(); + + void cover(); + void approachToCloseAttack(); + void approachToRangedAttack(); + void uncover(); + void aim(); + void rangedAttack(); + void closeAttack(); + void flee(); + + void faceEnemy(); + + int getaggressivenessCloseAttack() const; + int getaggressivenessRangedAttack() const; + + int getDamageCloseAttack(int min, int max) const; + int getDamageRangedAttack(int min, int max) const; + + int calculateActionRatio() const; + int calculateCoverRatio() const; + int calculateFleeRatio() const; + + bool findClosestPositionToEnemy(Vector3 &output) const; }; } // End of namespace BladeRunner diff --git a/engines/bladerunner/actor_dialogue_queue.cpp b/engines/bladerunner/actor_dialogue_queue.cpp index dbf5598a52..48fcf8a6ab 100644 --- a/engines/bladerunner/actor_dialogue_queue.cpp +++ b/engines/bladerunner/actor_dialogue_queue.cpp @@ -26,6 +26,7 @@ #include "bladerunner/actor.h" #include "bladerunner/audio_speech.h" +#include "bladerunner/savefile.h" #include "bladerunner/scene.h" #include "bladerunner/script/scene_script.h" @@ -53,7 +54,7 @@ void ActorDialogueQueue::add(int actorId, int sentenceId, int animationMode) { if (actorId == 0 || actorId == BladeRunnerEngine::kActorVoiceOver) { animationMode = -1; } - if (_entries.size() < 25) { + if (_entries.size() < kMaxEntries) { Entry entry; entry.isNotPause = true; entry.isPause = false; @@ -67,7 +68,7 @@ void ActorDialogueQueue::add(int actorId, int sentenceId, int animationMode) { } void ActorDialogueQueue::addPause(int delay) { - if (_entries.size() < 25) { + if (_entries.size() < kMaxEntries) { Entry entry; entry.isNotPause = false; entry.isPause = true; @@ -159,6 +160,58 @@ void ActorDialogueQueue::tick() { } } +void ActorDialogueQueue::save(SaveFileWriteStream &f) { + int count = (int)_entries.size(); + f.writeInt(count); + for (int i = 0; i < count; ++i) { + Entry &e = _entries[i]; + f.writeBool(e.isNotPause); + f.writeBool(e.isPause); + f.writeInt(e.actorId); + f.writeInt(e.sentenceId); + f.writeInt(e.animationMode); + f.writeInt(e.delay); + } + f.padBytes((kMaxEntries - count) * 24); + + f.writeBool(_isNotPause); + f.writeInt(_actorId); + f.writeInt(_sentenceId); + f.writeInt(_animationMode); + f.writeInt(_animationModePrevious); + f.writeBool(_isPause); + f.writeInt(_delay); + // f.write(_timeLast); +} + +void ActorDialogueQueue::load(SaveFileReadStream &f) { + _entries.clear(); + int count = f.readInt(); + assert(count <= kMaxEntries); + _entries.resize(count); + for (int i = 0; i < count; ++i) { + Entry &e = _entries[i]; + e.isNotPause = f.readBool(); + e.isPause = f.readBool(); + e.actorId = f.readInt(); + e.sentenceId = f.readInt(); + e.animationMode = f.readInt(); + e.delay = f.readInt(); + } + + f.skip((kMaxEntries - count) * 24); + + _isNotPause = f.readBool(); + _actorId = f.readInt(); + _sentenceId = f.readInt(); + _animationMode = f.readInt(); + _animationModePrevious = f.readInt(); + _isPause = f.readBool(); + _delay = f.readInt(); + + _timeLast = 0; +} + void ActorDialogueQueue::clear() { _entries.clear(); _isNotPause = false; @@ -170,4 +223,5 @@ void ActorDialogueQueue::clear() { _delay = 0; _timeLast = 0; } + } // End of namespace BladeRunner diff --git a/engines/bladerunner/actor_dialogue_queue.h b/engines/bladerunner/actor_dialogue_queue.h index 841ca8a48e..832bcc9dab 100644 --- a/engines/bladerunner/actor_dialogue_queue.h +++ b/engines/bladerunner/actor_dialogue_queue.h @@ -22,13 +22,18 @@ #ifndef BLADERUNNER_ACTOR_DIALOGUE_QUEUE_H #define BLADERUNNER_ACTOR_DIALOGUE_QUEUE_H + #include "common/array.h" namespace BladeRunner { class BladeRunnerEngine; +class SaveFileReadStream; +class SaveFileWriteStream; class ActorDialogueQueue { + static const int kMaxEntries = 25; + struct Entry { bool isNotPause; bool isPause; @@ -61,6 +66,9 @@ public: void flush(int a1, bool callScript); void tick(); + void save(SaveFileWriteStream &f); + void load(SaveFileReadStream &f); + private: void clear(); }; diff --git a/engines/bladerunner/actor_walk.cpp b/engines/bladerunner/actor_walk.cpp index 16009aa65b..b4587333a1 100644 --- a/engines/bladerunner/actor_walk.cpp +++ b/engines/bladerunner/actor_walk.cpp @@ -28,6 +28,7 @@ #include "bladerunner/game_constants.h" #include "bladerunner/game_info.h" #include "bladerunner/obstacles.h" +#include "bladerunner/savefile.h" #include "bladerunner/scene.h" #include "bladerunner/scene_objects.h" #include "bladerunner/set.h" @@ -238,6 +239,53 @@ void ActorWalk::run(int actorId) { _vm->_actors[actorId]->changeAnimationMode(animationMode, false); } +void ActorWalk::save(SaveFileWriteStream &f) { + f.writeInt(_walking); + f.writeInt(_running); + f.writeVector3(_destination); + // _originalDestination is not saved + f.writeVector3(_current); + f.writeVector3(_next); + f.writeInt(_facing); + + assert(_nearActors.size() <= 20); + for (Common::HashMap<int, bool>::const_iterator it = _nearActors.begin(); it != _nearActors.end(); ++it) { + f.writeInt(it->_key); + f.writeBool(it->_value); + } + f.padBytes(8 * (20 - _nearActors.size())); + f.writeInt(_nearActors.size()); + + f.writeInt(0); // _notUsed + f.writeInt(_status); +} + +void ActorWalk::load(SaveFileReadStream &f) { + _walking = f.readInt(); + _running = f.readInt(); + _destination = f.readVector3(); + // _originalDestination is not saved + _current = f.readVector3(); + _next = f.readVector3(); + _facing = f.readInt(); + + int actorId[20]; + bool isNear[20]; + + for (int i = 0; i < 20; ++i) { + actorId[i] = f.readInt(); + isNear[i] = f.readBool(); + } + + int count = f.readInt(); + for (int i = 0; i < count; ++i) { + _nearActors.setVal(actorId[i], isNear[i]); + } + + f.skip(4); // _notUsed + _status = f.readInt(); +} + bool ActorWalk::isXYZEmpty(float x, float y, float z, int actorId) const { if (_vm->_scene->_set->findWalkbox(x, z) == -1) { return true; diff --git a/engines/bladerunner/actor_walk.h b/engines/bladerunner/actor_walk.h index f4caa65453..45298e2794 100644 --- a/engines/bladerunner/actor_walk.h +++ b/engines/bladerunner/actor_walk.h @@ -29,6 +29,8 @@ namespace BladeRunner { class BladeRunnerEngine; +class SaveFileReadStream; +class SaveFileWriteStream; class ActorWalk { BladeRunnerEngine *_vm; @@ -60,6 +62,9 @@ public: void stop(int actorId, bool immediately, int combatAnimationMode, int animationMode); void run(int actorId); + void save(SaveFileWriteStream &f); + void load(SaveFileReadStream &f); + private: int nextOnPath(int actorId, const Vector3 &from, const Vector3 &to, Vector3 &next) const; diff --git a/engines/bladerunner/ambient_sounds.cpp b/engines/bladerunner/ambient_sounds.cpp index 64a85bc46c..81b6174ab9 100644 --- a/engines/bladerunner/ambient_sounds.cpp +++ b/engines/bladerunner/ambient_sounds.cpp @@ -25,6 +25,7 @@ #include "bladerunner/audio_player.h" #include "bladerunner/bladerunner.h" #include "bladerunner/game_info.h" +#include "bladerunner/savefile.h" #include "common/debug.h" #include "common/system.h" @@ -68,25 +69,23 @@ void AmbientSounds::addSound( int panStartMin, int panStartMax, int panEndMin, int panEndMax, int priority, int unk) { - const char *name = _vm->_gameInfo->getSfxTrack(sfxId); sort(volumeMin, volumeMax); sort(panStartMin, panStartMax); sort(panEndMin, panEndMax); addSoundByName( - name, - timeMin, timeMax, - volumeMin, volumeMax, - panStartMin, panStartMax, - panEndMin, panEndMax, - priority, unk + _vm->_gameInfo->getSfxTrack(sfxId), + timeMin, timeMax, + volumeMin, volumeMax, + panStartMin, panStartMax, + panEndMin, panEndMax, + priority, unk ); } void AmbientSounds::removeNonLoopingSound(int sfxId, bool stopPlaying) { - const char *name = _vm->_gameInfo->getSfxTrack(sfxId); - int32 hash = mix_id(name); + int32 hash = MIXArchive::getHash(_vm->_gameInfo->getSfxTrack(sfxId)); int index = findNonLoopingTrackByHash(hash); if (index >= 0) { removeNonLoopingSoundByIndex(index, stopPlaying); @@ -104,8 +103,7 @@ void AmbientSounds::addSpeech(int actorId, int sentenceId, int timeMin, int time sort(panStartMin, panStartMax); sort(panEndMin, panEndMax); - char name[13]; - sprintf(name, "%02d-%04d%s.AUD", actorId, sentenceId, _vm->_languageCode); + Common::String name = Common::String::format( "%02d-%04d%s.AUD", actorId, sentenceId, _vm->_languageCode.c_str()); addSoundByName(name, timeMin, timeMax, volumeMin, volumeMax, @@ -115,15 +113,12 @@ void AmbientSounds::addSpeech(int actorId, int sentenceId, int timeMin, int time } void AmbientSounds::playSound(int sfxId, int volume, int panStart, int panEnd, int priority) { - const char *name = _vm->_gameInfo->getSfxTrack(sfxId); - - _vm->_audioPlayer->playAud(name, volume * _ambientVolume / 100, panStart, panEnd, priority, kAudioPlayerOverrideVolume); + _vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(sfxId), volume * _ambientVolume / 100, panStart, panEnd, priority, kAudioPlayerOverrideVolume); } void AmbientSounds::addLoopingSound(int sfxId, int volume, int pan, int delay) { - const char *name = _vm->_gameInfo->getSfxTrack(sfxId); - - int32 hash = mix_id(name); + const Common::String &name = _vm->_gameInfo->getSfxTrack(sfxId); + int32 hash = MIXArchive::getHash(name); if (findLoopingTrackByHash(hash) >= 0) { return; @@ -136,8 +131,7 @@ void AmbientSounds::addLoopingSound(int sfxId, int volume, int pan, int delay) { LoopingSound &track = _loopingSounds[i]; track.isActive = true; - strncpy(track.name, name, sizeof(track.name)); - track.name[sizeof(track.name) - 1] = 0; + track.name = name; track.hash = hash; track.pan = pan; track.volume = volume; @@ -161,8 +155,7 @@ void AmbientSounds::addLoopingSound(int sfxId, int volume, int pan, int delay) { } void AmbientSounds::adjustLoopingSound(int sfxId, int volume, int pan, int delay) { - const char *name = _vm->_gameInfo->getSfxTrack(sfxId); - int32 hash = mix_id(name); + int32 hash = MIXArchive::getHash(_vm->_gameInfo->getSfxTrack(sfxId)); int index = findLoopingTrackByHash(hash); if (index >= 0 && _loopingSounds[index].audioPlayerTrack != -1 && _vm->_audioPlayer->isActive(_loopingSounds[index].audioPlayerTrack)) { @@ -178,8 +171,7 @@ void AmbientSounds::adjustLoopingSound(int sfxId, int volume, int pan, int delay } void AmbientSounds::removeLoopingSound(int sfxId, int delay) { - const char *name = _vm->_gameInfo->getSfxTrack(sfxId); - int32 hash = mix_id(name); + int32 hash = MIXArchive::getHash(_vm->_gameInfo->getSfxTrack(sfxId)); int index = findLoopingTrackByHash(hash); if (index >= 0) { removeLoopingSoundByIndex(index, delay); @@ -297,15 +289,12 @@ int AmbientSounds::findLoopingTrackByHash(int32 hash) const { } void AmbientSounds::addSoundByName( - const char *name, + const Common::String &name, int timeMin, int timeMax, int volumeMin, int volumeMax, int panStartMin, int panStartMax, int panEndMin, int panEndMax, int priority, int unk) { - if (strlen(name) > 12) { - error("AmbientSounds::addSoundByName: Overlong name '%s'", name); - } int i = findAvailableNonLoopingTrack(); if (i < 0) { @@ -317,9 +306,8 @@ void AmbientSounds::addSoundByName( uint32 now = _vm->getTotalPlayTime(); track.isActive = true; - strncpy(track.name, name, sizeof(track.name)); - track.name[sizeof(track.name) - 1] = 0; - track.hash = mix_id(name); + track.name = name; + track.hash = MIXArchive::getHash(name); track.timeMin = 1000 * timeMin; track.timeMax = 1000 * timeMax; track.nextPlayTime = now + _vm->_rnd.getRandomNumberRng(track.timeMin, track.timeMax); @@ -355,11 +343,83 @@ void AmbientSounds::removeLoopingSoundByIndex(int index, int delay) { } } track.isActive = false; - track.name[0] = 0; + track.name.clear(); track.hash = 0; track.audioPlayerTrack = -1; track.volume = 0; track.pan = 0; } +void AmbientSounds::save(SaveFileWriteStream &f) { + f.writeBool(false); // TODO: _isDisabled + + for (int i = 0; i != kNonLoopingSounds; ++i) { + // 73 bytes per non-looping sound + NonLoopingSound &s = _nonLoopingSounds[i]; + f.writeBool(s.isActive); + f.writeStringSz(s.name, 13); + f.writeSint32LE(s.hash); + f.writeInt(s.audioPlayerTrack); + f.writeInt(s.timeMin); + f.writeInt(s.timeMax); + f.writeUint32LE(s.nextPlayTime); + f.writeInt(s.volumeMin); + f.writeInt(s.volumeMax); + f.writeInt(s.volume); + f.writeInt(s.panStartMin); + f.writeInt(s.panStartMax); + f.writeInt(s.panEndMin); + f.writeInt(s.panEndMax); + f.writeInt(s.priority); + f.padBytes(4); // field_45 + } + + for (int i = 0; i != kLoopingSounds; ++i) { + // 33 bytes per looping sound + LoopingSound &s = _loopingSounds[i]; + f.writeBool(s.isActive); + f.writeStringSz(s.name, 13); + f.writeSint32LE(s.hash); + f.writeInt(s.audioPlayerTrack); + f.writeInt(s.volume); + f.writeInt(s.pan); + } +} + +void AmbientSounds::load(SaveFileReadStream &f) { + f.skip(4); // TODO: _isDisabled + + for (int i = 0; i != kNonLoopingSounds; ++i) { + // 73 bytes per non-looping sound + NonLoopingSound &s = _nonLoopingSounds[i]; + s.isActive = f.readBool(); + s.name = f.readStringSz(13); + s.hash = f.readSint32LE(); + s.audioPlayerTrack = f.readInt(); + s.timeMin = f.readInt(); + s.timeMax = f.readInt(); + s.nextPlayTime = f.readUint32LE(); + s.volumeMin = f.readInt(); + s.volumeMax = f.readInt(); + s.volume = f.readInt(); + s.panStartMin = f.readInt(); + s.panStartMax = f.readInt(); + s.panEndMin = f.readInt(); + s.panEndMax = f.readInt(); + s.priority = f.readInt(); + f.skip(4); // field_45 + } + + for (int i = 0; i != kLoopingSounds; ++i) { + // 33 bytes per looping sound + LoopingSound &s = _loopingSounds[i]; + s.isActive = f.readBool(); + s.name = f.readStringSz(13); + s.hash = f.readSint32LE(); + s.audioPlayerTrack = f.readInt(); + s.volume = f.readInt(); + s.pan = f.readInt(); + } +} + } // End of namespace BladeRunner diff --git a/engines/bladerunner/ambient_sounds.h b/engines/bladerunner/ambient_sounds.h index e06726b183..de08965a11 100644 --- a/engines/bladerunner/ambient_sounds.h +++ b/engines/bladerunner/ambient_sounds.h @@ -25,39 +25,43 @@ #include "audio/audiostream.h" +#include "common/str.h" + namespace BladeRunner { class BladeRunnerEngine; +class SaveFileReadStream; +class SaveFileWriteStream; class AmbientSounds { static const int kNonLoopingSounds = 25; static const int kLoopingSounds = 3; struct NonLoopingSound { - bool isActive; - char name[13]; - int32 hash; - int32 audioPlayerTrack; - int32 timeMin; - int32 timeMax; - uint32 nextPlayTime; - int32 volumeMin; - int32 volumeMax; - int32 volume; - int32 panStartMin; - int32 panStartMax; - int32 panEndMin; - int32 panEndMax; - int32 priority; + bool isActive; + Common::String name; + int32 hash; + int audioPlayerTrack; + int timeMin; + int timeMax; + uint32 nextPlayTime; + int volumeMin; + int volumeMax; + int volume; + int panStartMin; + int panStartMax; + int panEndMin; + int panEndMax; + int priority; }; struct LoopingSound { - bool isActive; - char name[13]; - int32 hash; - int audioPlayerTrack; - int32 volume; - int pan; + bool isActive; + Common::String name; + int32 hash; + int audioPlayerTrack; + int volume; + int pan; }; BladeRunnerEngine *_vm; @@ -103,6 +107,9 @@ public: int getVolume() const; void playSample(); + void save(SaveFileWriteStream &f); + void load(SaveFileReadStream &f); + private: int findAvailableNonLoopingTrack() const; int findNonLoopingTrackByHash(int32 hash) const; @@ -119,7 +126,7 @@ private: // playVolumeAdjustSound void addSoundByName( - const char *name, + const Common::String &name, int timeMin, int timeMax, int volumeMin, int volumeMax, int panStartMin, int panStartMax, diff --git a/engines/bladerunner/archive.cpp b/engines/bladerunner/archive.cpp index 8088e1ac67..c116eeb94f 100644 --- a/engines/bladerunner/archive.cpp +++ b/engines/bladerunner/archive.cpp @@ -51,7 +51,7 @@ bool MIXArchive::open(const Common::String &filename) { _entries.resize(_entryCount); for (uint16 i = 0; i != _entryCount; ++i) { - _entries[i].id = _fd.readSint32LE(); + _entries[i].hash = _fd.readUint32LE(); _entries[i].offset = _fd.readUint32LE(); _entries[i].length = _fd.readUint32LE(); @@ -61,7 +61,7 @@ bool MIXArchive::open(const Common::String &filename) { // Verify that the entries are sorted by id. Note that id is signed. if (i > 0) { - assert(_entries[i].id > _entries[i - 1].id); + assert(_entries[i].hash > _entries[i - 1].hash); } } @@ -86,7 +86,7 @@ bool MIXArchive::isOpen() const { #define ROL(n) ((n << 1) | ((n >> 31) & 1)) -int32 mix_id(const Common::String &name) { +int32 MIXArchive::getHash(const Common::String &name) { char buffer[12] = { 0 }; for (uint i = 0; i != name.size() && i < 12u; ++i) { @@ -103,11 +103,10 @@ int32 mix_id(const Common::String &name) { id = ROL(id) + t; } - return reinterpret_cast<int32&>(id); + return id; } -static -int32 tlk_id(const Common::String &name) { +static uint32 tlk_id(const Common::String &name) { char buffer[12] = { 0 }; for (uint i = 0; i != name.size() && i < 12u; ++i) @@ -124,15 +123,15 @@ int32 tlk_id(const Common::String &name) { return 10000 * actor_id + speech_id; } -uint32 MIXArchive::indexForId(int32 id) const { +uint32 MIXArchive::indexForHash(int32 hash) const { uint32 lo = 0, hi = _entryCount; while (lo < hi) { uint32 mid = lo + (hi - lo) / 2; - if (id > _entries[mid].id) { + if (hash > _entries[mid].hash) { lo = mid + 1; - } else if (id < _entries[mid].id) { + } else if (hash < _entries[mid].hash) { hi = mid; } else { return mid; @@ -142,14 +141,15 @@ uint32 MIXArchive::indexForId(int32 id) const { } Common::SeekableReadStream *MIXArchive::createReadStreamForMember(const Common::String &name) { - int32 id; + int32 hash; - if (_isTLK) - id = tlk_id(name); - else - id = mix_id(name); + if (_isTLK) { + hash = tlk_id(name); + } else { + hash = MIXArchive::getHash(name); + } - uint32 i = indexForId(id); + uint32 i = indexForHash(hash); if (i == _entryCount) { return nullptr; diff --git a/engines/bladerunner/archive.h b/engines/bladerunner/archive.h index 7bec41f97a..9f7c67920d 100644 --- a/engines/bladerunner/archive.h +++ b/engines/bladerunner/archive.h @@ -34,6 +34,8 @@ public: MIXArchive(); ~MIXArchive(); + static int32 getHash(const Common::String &name); + bool open(const Common::String &filename); void close(); bool isOpen() const; @@ -50,17 +52,16 @@ private: uint32 _size; struct ArchiveEntry { - int32 id; + int32 hash; uint32 offset; uint32 length; }; Common::Array<ArchiveEntry> _entries; - uint32 indexForId(int32 id) const; + uint32 indexForHash(int32 hash) const; }; -int32 mix_id(const Common::String &name); } // End of namespace BladeRunner diff --git a/engines/bladerunner/aud_stream.cpp b/engines/bladerunner/aud_stream.cpp index 0d4434cbbf..3d14f948d2 100644 --- a/engines/bladerunner/aud_stream.cpp +++ b/engines/bladerunner/aud_stream.cpp @@ -133,7 +133,12 @@ int AudStream::getLength() const { if (_flags & 2) { // stereo bytesPerSecond *= 2; } - return (1000 * _sizeDecompressed) / bytesPerSecond; + + // since everything is 44100, we easily get overflows with ints + // thus we must use doubles + double res = (double)_sizeDecompressed * 1000.0 / (double)bytesPerSecond; + + return (int32)res; } } // End of namespace BladeRunner diff --git a/engines/bladerunner/audio_player.cpp b/engines/bladerunner/audio_player.cpp index e1ef0263b6..5186888dd2 100644 --- a/engines/bladerunner/audio_player.cpp +++ b/engines/bladerunner/audio_player.cpp @@ -253,7 +253,7 @@ int AudioPlayer::playAud(const Common::String &name, int volume, int panFrom, in } /* Load audio resource and store in cache. Playback will happen directly from there. */ - int32 hash = mix_id(name); + int32 hash = MIXArchive::getHash(name); if (!_cache->findByHash(hash)) { Common::SeekableReadStream *r = _vm->getResourceStream(name); if (!r) { diff --git a/engines/bladerunner/audio_speech.cpp b/engines/bladerunner/audio_speech.cpp index 69883cbce2..5ffde9db6c 100644 --- a/engines/bladerunner/audio_speech.cpp +++ b/engines/bladerunner/audio_speech.cpp @@ -58,12 +58,12 @@ AudioSpeech::~AudioSpeech() { delete[] _data; } -bool AudioSpeech::playSpeech(const char *name, int pan) { +bool AudioSpeech::playSpeech(const Common::String &name, int pan) { // debug("AudioSpeech::playSpeech(\"%s\")", name); Common::ScopedPtr<Common::SeekableReadStream> r(_vm->getResourceStream(name)); if (!r) { - warning("AudioSpeech::playSpeech: AUD resource \"%s\" not found", name); + warning("AudioSpeech::playSpeech: AUD resource \"%s\" not found", name.c_str()); return false; } @@ -78,7 +78,7 @@ bool AudioSpeech::playSpeech(const char *name, int pan) { r->read(_data, r->size()); if (r->err()) { - warning("AudioSpeech::playSpeech: Error reading resource \"%s\"", name); + warning("AudioSpeech::playSpeech: Error reading resource \"%s\"", name.c_str()); return false; } @@ -110,14 +110,14 @@ void AudioSpeech::stopSpeech() { bool AudioSpeech::isPlaying() const { if (_channel == -1) { - return false; + return false; } return _isActive; } bool AudioSpeech::playSpeechLine(int actorId, int sentenceId, int volume, int a4, int priority) { int balance = _vm->_actors[actorId]->soundBalance(); - Common::String name = Common::String::format("%02d-%04d%s.AUD", actorId, sentenceId, _vm->_languageCode); + Common::String name = Common::String::format("%02d-%04d%s.AUD", actorId, sentenceId, _vm->_languageCode.c_str()); return _vm->_audioPlayer->playAud(name, _speechVolume * volume / 100, balance, balance, priority, kAudioPlayerOverrideVolume); } diff --git a/engines/bladerunner/audio_speech.h b/engines/bladerunner/audio_speech.h index 010499f480..5406f1d8e5 100644 --- a/engines/bladerunner/audio_speech.h +++ b/engines/bladerunner/audio_speech.h @@ -23,6 +23,7 @@ #ifndef BLADERUNNER_AUDIO_SPEECH_H #define BLADERUNNER_AUDIO_SPEECH_H +#include "common/str.h" #include "common/types.h" namespace BladeRunner { @@ -44,7 +45,7 @@ public: AudioSpeech(BladeRunnerEngine *vm); ~AudioSpeech(); - bool playSpeech(const char *name, int balance = 0); + bool playSpeech(const Common::String &name, int balance = 0); void stopSpeech(); bool isPlaying() const; diff --git a/engines/bladerunner/bladerunner.cpp b/engines/bladerunner/bladerunner.cpp index ea663b53bc..fab708cbb8 100644 --- a/engines/bladerunner/bladerunner.cpp +++ b/engines/bladerunner/bladerunner.cpp @@ -46,6 +46,7 @@ #include "bladerunner/obstacles.h" #include "bladerunner/overlays.h" #include "bladerunner/regions.h" +#include "bladerunner/savefile.h" #include "bladerunner/scene.h" #include "bladerunner/scene_objects.h" #include "bladerunner/screen_effects.h" @@ -53,6 +54,7 @@ #include "bladerunner/script/ai_script.h" #include "bladerunner/script/init_script.h" #include "bladerunner/script/kia_script.h" +#include "bladerunner/script/police_maze.h" #include "bladerunner/script/scene_script.h" #include "bladerunner/settings.h" #include "bladerunner/shape.h" @@ -60,9 +62,12 @@ #include "bladerunner/slice_renderer.h" #include "bladerunner/suspects_database.h" #include "bladerunner/text_resource.h" +#include "bladerunner/time.h" #include "bladerunner/ui/elevator.h" +#include "bladerunner/ui/end_credits.h" #include "bladerunner/ui/esper.h" #include "bladerunner/ui/kia.h" +#include "bladerunner/ui/scores.h" #include "bladerunner/ui/spinner.h" #include "bladerunner/ui/vk.h" #include "bladerunner/vqa_decoder.h" @@ -70,8 +75,10 @@ #include "bladerunner/zbuffer.h" #include "common/array.h" +#include "common/config-manager.h" #include "common/error.h" #include "common/events.h" +#include "common/savefile.h" #include "common/system.h" #include "engines/util.h" @@ -88,6 +95,9 @@ BladeRunnerEngine::BladeRunnerEngine(OSystem *syst, const ADGameDescription *des _windowIsActive = true; _gameIsRunning = true; + _vqaIsPlaying = false; + _vqaStopIsRequested = false; + _playerLosesControlCounter = 0; _playerActorIdle = false; @@ -139,6 +149,7 @@ BladeRunnerEngine::BladeRunnerEngine(OSystem *syst, const ADGameDescription *des _lights = nullptr; _obstacles = nullptr; _sceneScript = nullptr; + _gameTime = nullptr; _gameInfo = nullptr; _waypoints = nullptr; _gameVars = nullptr; @@ -165,11 +176,14 @@ BladeRunnerEngine::BladeRunnerEngine(OSystem *syst, const ADGameDescription *des _dialogueMenu = nullptr; _suspectsDatabase = nullptr; _kia = nullptr; + _endCredits = nullptr; _spinner = nullptr; + _scores = nullptr; _elevator = nullptr; _mainFont = nullptr; _esper = nullptr; _vk = nullptr; + _policeMaze = nullptr; _mouse = nullptr; _sliceAnimations = nullptr; _sliceRenderer = nullptr; @@ -212,6 +226,13 @@ Common::Error BladeRunnerEngine::run() { /* TODO: Check for save games and enter KIA */ gameLoop(); + + _mouse->disable(); + + if (_gameOver) { + // autoSaveGame(4, 1); // TODO + _endCredits->show(); + } } shutdown(); @@ -224,9 +245,7 @@ bool BladeRunnerEngine::startup(bool hasSavegames) { _screenEffects = new ScreenEffects(this, 0x8000); - _combat = new Combat(this); - - // TODO: end credits + _endCredits = new EndCredits(this); _actorDialogueQueue = new ActorDialogueQueue(this); @@ -238,7 +257,7 @@ bool BladeRunnerEngine::startup(bool hasSavegames) { // TODO: outtake player - but this is done bit differently - // TODO: police maze + _policeMaze = new PoliceMaze(this); _obstacles = new Obstacles(this); @@ -254,19 +273,22 @@ bool BladeRunnerEngine::startup(bool hasSavegames) { _surfaceBack.create(640, 480, createRGB555()); _surface4.create(640, 480, createRGB555()); + _gameTime = new Time(this); + r = openArchive("STARTUP.MIX"); if (!r) return false; - // TODO: Timer - _gameInfo = new GameInfo(this); if (!_gameInfo) return false; r = _gameInfo->open("GAMEINFO.DAT"); - if (!r) + if (!r) { return false; + } + + _combat = new Combat(this); // TODO: Create datetime - not used @@ -286,7 +308,7 @@ bool BladeRunnerEngine::startup(bool hasSavegames) { // TODO: Flee waypoints - _gameVars = new int[_gameInfo->getGlobalVarCount()]; + _gameVars = new int[_gameInfo->getGlobalVarCount()](); // TODO: Actor AI DLL init @@ -348,7 +370,7 @@ bool BladeRunnerEngine::startup(bool hasSavegames) { // TODO: Set actor ids (redundant?) - // TODO: Police Maze + _policeMaze = new PoliceMaze(this); _textActorNames = new TextResource(this); if (!_textActorNames->open("ACTORS")) @@ -390,7 +412,7 @@ bool BladeRunnerEngine::startup(bool hasSavegames) { _elevator = new Elevator(this); - // TODO: Scores + _scores = new Scores(this); _mainFont = new Font(this); _mainFont->open("KIA6PT.FON", 640, 480, -1, 0, 0x252D); @@ -415,13 +437,6 @@ bool BladeRunnerEngine::startup(bool hasSavegames) { if (!r) return false; - // TODO: Support cdframes - - r = _sliceAnimations->openHDFrames(); - if (!r) { - return false; - } - r = _sliceAnimations->openCoreAnim(); if (!r) { return false; @@ -461,8 +476,20 @@ void BladeRunnerEngine::initChapterAndScene() { _actors[i]->movementTrackNext(true); } - _settings->setChapter(1); - _settings->setNewSetAndScene(_gameInfo->getInitialSetId(), _gameInfo->getInitialSceneId()); + if (ConfMan.hasKey("boot_param")) { + int param = ConfMan.getInt("boot_param"); // CTTTSSS + int chapter = param / 1000000; + param -= chapter * 1000000; + int set = param / 1000; + param -= set * 1000; + int scene = param; + + _settings->setChapter(chapter); + _settings->setNewSetAndScene(set, scene); + } else { + _settings->setChapter(1); + _settings->setNewSetAndScene(_gameInfo->getInitialSetId(), _gameInfo->getInitialSceneId()); + } } void BladeRunnerEngine::shutdown() { @@ -590,7 +617,11 @@ void BladeRunnerEngine::shutdown() { // TODO: Delete Flee waypoints - // TODO: Delete Scores + delete _scores; + _scores = nullptr; + + delete _endCredits; + _endCredits = nullptr; delete _elevator; _elevator = nullptr; @@ -630,8 +661,8 @@ void BladeRunnerEngine::shutdown() { // TODO: Delete MIXArchives here - // TODO: Delete Timer - + delete _gameTime; + _gameTime = nullptr; // These are static objects in original game @@ -644,6 +675,9 @@ void BladeRunnerEngine::shutdown() { delete _itemPickup; _itemPickup = nullptr; + delete _policeMaze; + _policeMaze = nullptr; + delete _obstacles; _obstacles = nullptr; @@ -744,17 +778,25 @@ void BladeRunnerEngine::gameTick() { return; } - // TODO: Scores + if (_scores->isOpen()) { + _scores->tick(); + _ambientSounds->tick(); + return; + } _actorDialogueQueue->tick(); if (_scene->didPlayerWalkIn()) { _sceneScript->playerWalkedIn(); } bool inDialogueMenu = _dialogueMenu->isVisible(); - if (!inDialogueMenu) { - // TODO: actors combat-tick + if (!inDialogueMenu) { + for (int i = 0; i < (int)_gameInfo->getActorCount(); ++i) { + _actors[i]->tickCombat(); + } } + _policeMaze->tick(); + // TODO: Gun range announcements _zbuffer->clean(); @@ -769,9 +811,6 @@ void BladeRunnerEngine::gameTick() { (void)backgroundChanged; blit(_surfaceBack, _surfaceFront); - // TODO: remove zbuffer draw - // _surfaceFront.copyRectToSurface(_zbuffer->getData(), 1280, 0, 0, 640, 480); - _overlays->tick(); if (!inDialogueMenu) { @@ -814,14 +853,16 @@ void BladeRunnerEngine::gameTick() { // TODO: Process AUD if (_walkSoundId >= 0) { - const char *name = _gameInfo->getSfxTrack(_walkSoundId); - _audioPlayer->playAud(name, _walkSoundVolume, _walkSoundBalance, _walkSoundBalance, 50, 0); + _audioPlayer->playAud(_gameInfo->getSfxTrack(_walkSoundId), _walkSoundVolume, _walkSoundBalance, _walkSoundBalance, 50, 0); _walkSoundId = -1; } if (_debugger->_viewSceneObjects) { _debugger->drawSceneObjects(); } + if (_debugger->_viewObstacles) { + _obstacles->draw(); + } blitToScreen(_surfaceFront); _system->delayMillis(10); @@ -908,6 +949,13 @@ void BladeRunnerEngine::handleKeyUp(Common::Event &event) { _speechSkipped = true; } + if (_vqaIsPlaying) { + _vqaStopIsRequested = true; + _vqaIsPlaying = false; + + return; + } + // TODO: if (!playerHasControl() /*|| ActorInWalkingLoop*/) { return; @@ -938,7 +986,10 @@ void BladeRunnerEngine::handleKeyUp(Common::Event &event) { return; } - //TODO: scores + if (_scores->isOpen()) { + return; + } + switch (event.kbd.keycode) { case Common::KEYCODE_TAB: _kia->openLastOpened(); @@ -986,7 +1037,10 @@ void BladeRunnerEngine::handleKeyDown(Common::Event &event) { return; } - //TODO: scores + if (_scores->isOpen()) { + _scores->handleKeyDown(event.kbd); + return; + } switch (event.kbd.keycode) { case Common::KEYCODE_F1: @@ -1075,6 +1129,15 @@ void BladeRunnerEngine::handleMouseAction(int x, int y, bool mainButton, bool bu return; } + if (_scores->isOpen()) { + if (buttonDown) { + _scores->handleMouseDown(x, y); + } else { + _scores->handleMouseUp(x, y); + } + return; + } + if (_dialogueMenu->waitingForInput()) { if (mainButton && !buttonDown) { _dialogueMenu->mouseUp(); @@ -1093,7 +1156,7 @@ void BladeRunnerEngine::handleMouseAction(int x, int y, bool mainButton, bool bu int exitIndex = _scene->_exits->getRegionAtXY(x, y); int regionIndex = _scene->_regions->getRegionAtXY(x, y); - if ((sceneObjectId < kSceneObjectOffsetActors || sceneObjectId >= kSceneObjectOffsetActors) && exitIndex >= 0) { + if ((sceneObjectId < kSceneObjectOffsetActors || sceneObjectId >= kSceneObjectOffsetItems) && exitIndex >= 0) { handleMouseClickExit(exitIndex, x, y, buttonDown); } else if (regionIndex >= 0) { handleMouseClickRegion(regionIndex, x, y, buttonDown); @@ -1183,8 +1246,8 @@ void BladeRunnerEngine::handleMouseClickRegion(int regionId, int x, int y, bool } void BladeRunnerEngine::handleMouseClick3DObject(int objectId, bool buttonDown, bool isClickable, bool isTarget) { - const char *objectName = _scene->objectGetName(objectId); - debug("Clicked on object %s", objectName); + const Common::String &objectName = _scene->objectGetName(objectId); + debug("Clicked on object %s", objectName.c_str()); if (_isWalkingInterruptible && objectId != _walkingToObjectId) { _isWalkingInterruptible = false; @@ -1217,7 +1280,7 @@ void BladeRunnerEngine::handleMouseClick3DObject(int objectId, bool buttonDown, _walkingToActorId = -1; _isInsideScriptObject = true; - _sceneScript->clickedOn3DObject(objectName, false); + _sceneScript->clickedOn3DObject(objectName.c_str(), false); _isInsideScriptObject = false; } } else { @@ -1226,14 +1289,14 @@ void BladeRunnerEngine::handleMouseClick3DObject(int objectId, bool buttonDown, } _playerActor->stopWalking(false); _playerActor->faceObject(objectName, false); - _playerActor->changeAnimationMode(kAnimationModeCombatShoot, false); + _playerActor->changeAnimationMode(kAnimationModeCombatAttack, false); _settings->decreaseAmmo(); _audioPlayer->playAud(_gameInfo->getSfxTrack(_combat->getHitSound()), 100, 0, 0, 90, 0); - //TODO mouse::randomize(Mouse); + _mouse->setMouseJitterUp(); _isInsideScriptObject = true; - _sceneScript->clickedOn3DObject(objectName, true); + _sceneScript->clickedOn3DObject(objectName.c_str(), true); _isInsideScriptObject = false; } } @@ -1267,11 +1330,11 @@ void BladeRunnerEngine::handleMouseClickEmpty(int x, int y, Vector3 &scenePositi } else { _playerActor->faceItem(itemId, false); } - _playerActor->changeAnimationMode(kAnimationModeCombatShoot, false); + _playerActor->changeAnimationMode(kAnimationModeCombatAttack, false); _settings->decreaseAmmo(); _audioPlayer->playAud(_gameInfo->getSfxTrack(_combat->getMissSound()), 100, 0, 0, 90, 0); - //TODO mouse::randomize(Mouse); + _mouse->setMouseJitterUp(); if (actorId > 0) { _aiScripts->shotAtAndMissed(actorId); @@ -1362,11 +1425,12 @@ void BladeRunnerEngine::handleMouseClickItem(int itemId, bool buttonDown) { _playerActor->stopWalking(false); _playerActor->faceItem(itemId, false); - _playerActor->changeAnimationMode(kAnimationModeCombatShoot, false); + _playerActor->changeAnimationMode(kAnimationModeCombatAttack, false); _settings->decreaseAmmo(); _audioPlayer->playAud(_gameInfo->getSfxTrack(_combat->getHitSound()), 100, 0, 0, 90, 0); - //TODO mouse::randomize(Mouse); + _mouse->setMouseJitterUp(); + _isInsideScriptItem = true; _sceneScript->clickedOnItem(itemId, true); _isInsideScriptItem = false; @@ -1422,22 +1486,21 @@ void BladeRunnerEngine::handleMouseClickActor(int actorId, bool mainButton, bool } } } else { - if (!_combat->isActive() || actorId == kActorMcCoy || !_actors[actorId]->isTarget() || _actors[actorId]->isRetired() /*|| _mouse->isRandomized()*/) { + Actor *actor = _actors[actorId]; + + if (!_combat->isActive() || actorId == kActorMcCoy || !actor->isTarget() || actor->isRetired() /*|| _mouse->isRandomized()*/) { return; } _playerActor->stopWalking(false); _playerActor->faceActor(actorId, false); - _playerActor->changeAnimationMode(kAnimationModeCombatShoot, false); + _playerActor->changeAnimationMode(kAnimationModeCombatAttack, false); _settings->decreaseAmmo(); - float targetX = _actors[actorId]->getX(); - float targetZ = _actors[actorId]->getZ(); - - bool missed = _playerActor->isObstacleBetween(targetX, targetZ); + bool missed = _playerActor->isObstacleBetween(actor->getXYZ()); _audioPlayer->playAud(_gameInfo->getSfxTrack(missed ? _combat->getMissSound() : _combat->getHitSound()), 100, 0, 0, 90, 0); - //TODO mouse::randomize(Mouse); + _mouse->setMouseJitterUp(); if (missed) { _aiScripts->shotAtAndMissed(actorId); @@ -1501,7 +1564,6 @@ bool BladeRunnerEngine::openArchive(const Common::String &name) { * archive when it runs out of slots. */ error("openArchive: No more archive slots"); - return false; } _archives[i].open(name); @@ -1534,9 +1596,11 @@ Common::SeekableReadStream *BladeRunnerEngine::getResourceStream(const Common::S if (!_archives[i].isOpen()) { continue; } + if (false) { debug("getResource: Searching archive %s for %s.", _archives[i].getName().c_str(), name.c_str()); } + Common::SeekableReadStream *stream = _archives[i].createReadStreamForMember(name); if (stream) { return stream; @@ -1573,8 +1637,144 @@ void BladeRunnerEngine::playerGainsControl() { } } -void BladeRunnerEngine::ISez(const char *str) { - debug("\t%s", str); +bool BladeRunnerEngine::saveGame(const Common::String &filename, byte *thumbnail) { + warning("BladeRunnerEngine::saveGame not finished"); + + if (!playerHasControl() || _sceneScript->isInsideScript() || _aiScripts->isInsideScript()) { + return false; + } + + Common::OutSaveFile *commonSaveFile = getSaveFileManager()->openForSaving(filename, false); + if (commonSaveFile->err()) { + return false; + } + + SaveFileWriteStream s; + + s.padBytes(9600); // TODO: thumbnail + s.writeFloat(-1.0f); + _settings->save(s); + _scene->save(s); + _scene->_exits->save(s); + _scene->_regions->save(s); + _scene->_set->save(s); + for (uint i = 0; i != _gameInfo->getGlobalVarCount(); ++i) { + s.writeInt(_gameVars[i]); + } + _music->save(s); + // _audioPlayer->save(s) // zero func + // _audioSpeech->save(s) // zero func + _combat->save(s); + _gameFlags->save(s); + _items->save(s); + _sceneObjects->save(s); + _ambientSounds->save(s); + _overlays->save(s); + _spinner->save(s); + _scores->save(s); + _dialogueMenu->save(s); + _obstacles->save(s); + _actorDialogueQueue->save(s); + _waypoints->save(s); + + for (uint i = 0; i != _gameInfo->getActorCount(); ++i) { + _actors[i]->save(s); + + int animationState, animationFrame, animationStateNext, nextAnimation; + _aiScripts->queryAnimationState(i, &animationState, &animationFrame, &animationStateNext, &nextAnimation); + s.writeInt(animationState); + s.writeInt(animationFrame); + s.writeInt(animationStateNext); + s.writeInt(nextAnimation); + } + _actors[kActorVoiceOver]->save(s); + + _policeMaze->save(s); + _crimesDatabase->save(s); + + s.finalize(); + assert(0 && "ok"); + + commonSaveFile->writeUint32LE(s.size() + 4); + commonSaveFile->write(s.getData(), s.size()); + + return !commonSaveFile->err(); +} + +void BladeRunnerEngine::loadGame(const Common::String &filename, byte *thumbnail) { + warning("BladeRunnerEngine::loadGame not finished"); + + if (!playerHasControl() || _sceneScript->isInsideScript() || _aiScripts->isInsideScript()) { + return; + } + + Common::InSaveFile *commonSaveFile = getSaveFileManager()->openForLoading(filename); + if (commonSaveFile->err()) { + return; + } + + void *buf = malloc(commonSaveFile->size()); + int dataSize = commonSaveFile->read(buf, commonSaveFile->size()); + + SaveFileReadStream s((const byte*)buf, dataSize); + + _ambientSounds->removeAllNonLoopingSounds(true); + _ambientSounds->removeAllLoopingSounds(1); + _music->stop(2); + _audioSpeech->stopSpeech(); + _actorDialogueQueue->flush(true, false); + + int size = s.readInt(); + + if (size != dataSize) { + return; + } + + s.skip(9600); // thumbnail + _settings->load(s); + _scene->load(s); + _scene->_exits->load(s); + _scene->_regions->load(s); + _scene->_set->load(s); + for (uint i = 0; i != _gameInfo->getGlobalVarCount(); ++i) { + _gameVars[i] = s.readInt(); + } + _music->load(s); + // _audioPlayer->load(s) // zero func + // _audioSpeech->load(s) // zero func + _combat->load(s); + _gameFlags->load(s); + _items->load(s); + _sceneObjects->load(s); + _ambientSounds->load(s); + _overlays->load(s); + _spinner->load(s); + _scores->load(s); + _dialogueMenu->load(s); + _obstacles->load(s); + _actorDialogueQueue->load(s); + _waypoints->load(s); + + for (uint i = 0; i != _gameInfo->getActorCount(); ++i) { + _actors[i]->load(s); + + int animationState = s.readInt(); + int animationFrame = s.readInt(); + int animationStateNext = s.readInt(); + int nextAnimation = s.readInt(); + _aiScripts->setAnimationState(i, animationState, animationFrame, animationStateNext, nextAnimation); + } + _actors[kActorVoiceOver]->load(s); + + _policeMaze->load(s); + _crimesDatabase->load(s); + + _settings->setNewSetAndScene(_settings->getSet(), _settings->getScene()); + _settings->setChapter(_settings->getChapter()); +} + +void BladeRunnerEngine::ISez(const Common::String &str) { + debug("\t%s", str.c_str()); } void BladeRunnerEngine::blitToScreen(const Graphics::Surface &src) { diff --git a/engines/bladerunner/bladerunner.h b/engines/bladerunner/bladerunner.h index 066e6837cc..fc0abf4e0a 100644 --- a/engines/bladerunner/bladerunner.h +++ b/engines/bladerunner/bladerunner.h @@ -63,6 +63,7 @@ class Combat; class Debugger; class DialogueMenu; class Elevator; +class EndCredits; class ESPER; class Font; class GameFlags; @@ -75,9 +76,11 @@ class Mouse; class Music; class Obstacles; class Overlays; +class PoliceMaze; class Scene; class SceneObjects; class SceneScript; +class Scores; class Settings; class Shape; class SliceAnimations; @@ -85,6 +88,7 @@ class SliceRenderer; class Spinner; class SuspectsDatabase; class TextResource; +class Time; class KIAShapes; class Vector3; class View; @@ -102,10 +106,10 @@ public: static const int kActorCount = 100; static const int kActorVoiceOver = kActorCount - 1; - bool _gameIsRunning; - bool _windowIsActive; - int _playerLosesControlCounter; - const char *_languageCode; + bool _gameIsRunning; + bool _windowIsActive; + int _playerLosesControlCounter; + Common::String _languageCode; ActorDialogueQueue *_actorDialogueQueue; ScreenEffects *_screenEffects; @@ -119,6 +123,7 @@ public: Combat *_combat; DialogueMenu *_dialogueMenu; Elevator *_elevator; + EndCredits *_endCredits; ESPER *_esper; GameFlags *_gameFlags; GameInfo *_gameInfo; @@ -131,14 +136,17 @@ public: Music *_music; Obstacles *_obstacles; Overlays *_overlays; + PoliceMaze *_policeMaze; Scene *_scene; SceneObjects *_sceneObjects; SceneScript *_sceneScript; + Scores *_scores; Settings *_settings; SliceAnimations *_sliceAnimations; SliceRenderer *_sliceRenderer; Spinner *_spinner; SuspectsDatabase *_suspectsDatabase; + Time *_gameTime; View *_view; VK *_vk; Waypoints *_waypoints; @@ -176,6 +184,8 @@ public: int _gameAutoSave; bool _gameIsLoading; bool _sceneIsLoading; + bool _vqaIsPlaying; + bool _vqaStopIsRequested; int _walkSoundId; int _walkSoundVolume; @@ -256,7 +266,12 @@ public: void playerLosesControl(); void playerGainsControl(); - void ISez(const char *str); + bool saveGame(const Common::String &filename, byte *thumbnail); + void loadGame(const Common::String &filename, byte *thumbnail); + void newGame(); + void autoSaveGame(); + + void ISez(const Common::String &str); void blitToScreen(const Graphics::Surface &src); diff --git a/engines/bladerunner/boundingbox.cpp b/engines/bladerunner/boundingbox.cpp index a1c79a17e7..40b7d4285a 100644 --- a/engines/bladerunner/boundingbox.cpp +++ b/engines/bladerunner/boundingbox.cpp @@ -22,6 +22,8 @@ #include "bladerunner/boundingbox.h" +#include "bladerunner/savefile.h" + namespace BladeRunner { BoundingBox::BoundingBox(float x0, float y0, float z0, float x1, float y1, float z1) { diff --git a/engines/bladerunner/boundingbox.h b/engines/bladerunner/boundingbox.h index 11922cbd14..0206f0bbdc 100644 --- a/engines/bladerunner/boundingbox.h +++ b/engines/bladerunner/boundingbox.h @@ -27,6 +27,8 @@ namespace BladeRunner { +class SaveFileWriteStream; + class BoundingBox { Vector3 _vertices[2]; diff --git a/engines/bladerunner/chapters.cpp b/engines/bladerunner/chapters.cpp index e7404c3c97..16d084b970 100644 --- a/engines/bladerunner/chapters.cpp +++ b/engines/bladerunner/chapters.cpp @@ -23,12 +23,15 @@ #include "bladerunner/chapters.h" #include "bladerunner/bladerunner.h" +#include "bladerunner/slice_animations.h" namespace BladeRunner { bool Chapters::enterChapter(int chapter) { int id = _resourceIds[chapter]; + _vm->_sliceAnimations->openFrames(id); + if (!_vm->openArchive("A.TLK")) return false; diff --git a/engines/bladerunner/combat.cpp b/engines/bladerunner/combat.cpp index c371f7bedd..e1dc6b1044 100644 --- a/engines/bladerunner/combat.cpp +++ b/engines/bladerunner/combat.cpp @@ -22,10 +22,14 @@ #include "bladerunner/combat.h" - #include "bladerunner/actor.h" +#include "bladerunner/audio_speech.h" #include "bladerunner/bladerunner.h" #include "bladerunner/game_constants.h" +#include "bladerunner/game_info.h" +#include "bladerunner/movement_track.h" +#include "bladerunner/savefile.h" +#include "bladerunner/scene_objects.h" #include "bladerunner/settings.h" namespace BladeRunner { @@ -33,6 +37,9 @@ namespace BladeRunner { Combat::Combat(BladeRunnerEngine *vm) { _vm = vm; + _coverWaypoints.resize(_vm->_gameInfo->getCoverWaypointCount()); + _fleeWaypoints.resize(_vm->_gameInfo->getFleeWaypointCount()); + reset(); } @@ -55,7 +62,7 @@ void Combat::reset() { void Combat::activate() { if(_enabled) { - _vm->_playerActor->combatModeOn(-1, -1, -1, -1, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, -1, -1, -1, _vm->_combat->_ammoDamage[_vm->_settings->getAmmoType()], 0, 0); + _vm->_playerActor->combatModeOn(-1, true, -1, -1, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, -1, -1, -1, _vm->_combat->_ammoDamage[_vm->_settings->getAmmoType()], 0, false); _active = true; } } @@ -97,16 +104,127 @@ void Combat::setMissSound(int ammoType, int column, int soundId) { _missSoundId[ammoType * 3 + column] = soundId; } -int Combat::getHitSound() { +int Combat::getHitSound() const { return _hitSoundId[3 * _vm->_settings->getAmmoType() + _vm->_rnd.getRandomNumber(2)]; } -int Combat::getMissSound() { +int Combat::getMissSound() const { return _hitSoundId[3 * _vm->_settings->getAmmoType() + _vm->_rnd.getRandomNumber(2)]; } void Combat::shoot(int actorId, Vector3 &to, int screenX) { + Actor *actor = _vm->_actors[actorId]; + + if (actor->isRetired()) { + return; + } + + int sentenceId = -1; + + /* + Distance from center as a percentage: + screenX - abs(right + left) / 2 + distanceFromCenter = 100 * ------------------------------- + abs(right - left) / 2 + */ + const Common::Rect &rect = actor->getScreenRectangle(); + int distanceFromCenter = CLIP(100 * (screenX - abs((rect.right + rect.left) / 2)) / abs((rect.right - rect.left) / 2), 0, 100); + + int damage = (100 - distanceFromCenter) * _ammoDamage[_vm->_settings->getAmmoType()] / 100; + + int hp = MAX(actor->getCurrentHP() - damage, 0); + + actor->setCurrentHP(hp); + + bool setDamageAnimation = true; + if (actor->isWalking() == 1 && !actor->getFlagDamageAnimIfMoving()) { + setDamageAnimation = false; + } + if (actor->_movementTrack->hasNext() && !actor->_movementTrack->isPaused()) { + setDamageAnimation = false; + } + if (setDamageAnimation) { + if (actor->isWalking()) { + actor->stopWalking(false); + } + if (actor->getAnimationMode() != kAnimationModeHit && actor->getAnimationMode() != kAnimationModeCombatHit) { + actor->changeAnimationMode(kAnimationModeHit, false); + sentenceId = _vm->_rnd.getRandomNumberRng(0, 1) ? 9000 : 9005; + } + } + + if (hp <= 0) { + actor->setTarget(false); + if (actor->inCombat()) { + actor->combatModeOff(); + } + actor->stopWalking(false); + actor->changeAnimationMode(48, false); + actor->retire(true, 72, 36, kActorMcCoy); + actor->setAtXYZ(actor->getXYZ(), actor->getFacing(), true, false, true); + _vm->_sceneObjects->setRetired(actorId + kSceneObjectOffsetActors, true); + sentenceId = 9020; + } + if (sentenceId >= 0 && actor->inCombat()) { + _vm->_audioSpeech->playSpeechLine(actorId, sentenceId, 75, 0, 99); + } +} + +int Combat::findFleeWaypoint(int setId, int enemyId, const Vector3& position) const { + float min = -1.0f; + int result = -1; + for (int i = 0; i < (int)_fleeWaypoints.size(); ++i) { + if (setId == _fleeWaypoints[i].setId) { + float dist = distance(position, _fleeWaypoints[i].position); + if (result == -1 || dist < min) { + result = i; + min = dist; + } + } + } + return result; +} + +int Combat::findCoverWaypoint(int waypointType, int actorId, int enemyId) const { + Actor *actor = _vm->_actors[actorId]; + Actor *enemy = _vm->_actors[enemyId]; + int result = -1; + float min = -1.0f; + for (int i = 0; i < (int)_coverWaypoints.size(); ++i) { + if (waypointType == _coverWaypoints[i].type && actor->getSetId() == _coverWaypoints[i].setId) { + if (_vm->_sceneObjects->isObstacleBetween(_coverWaypoints[i].position, enemy->getXYZ(), enemyId)) { + float dist = distance(_coverWaypoints[i].position, actor->getXYZ()); + if (result == -1 || dist < min) { + result = i; + min = dist; + } + } + } + } + return result; +} + +void Combat::save(SaveFileWriteStream &f) { + f.writeBool(_active); + f.writeBool(_enabled); + for (int i = 0; i != kSoundCount; ++i) { + f.writeInt(_hitSoundId[i]); + } + for (int i = 0; i != kSoundCount; ++i) { + f.writeInt(_missSoundId[i]); + } +} + +void Combat::load(SaveFileReadStream &f) { + _active = f.readBool(); + _enabled = f.readBool(); + for (int i = 0; i != kSoundCount; ++i) { + _hitSoundId[i] = f.readInt(); + } + for (int i = 0; i != kSoundCount; ++i) { + _missSoundId[i] = f.readInt(); + } } } // End of namespace BladeRunner diff --git a/engines/bladerunner/combat.h b/engines/bladerunner/combat.h index 21989dac52..e0038d677e 100644 --- a/engines/bladerunner/combat.h +++ b/engines/bladerunner/combat.h @@ -23,11 +23,16 @@ #ifndef BLADERUNNER_COMBAT_H #define BLADERUNNER_COMBAT_H -namespace BladeRunner { +#include "bladerunner/vector.h" -class Vector3; +#include "common/array.h" + +namespace BladeRunner { class BladeRunnerEngine; +class SaveFileReadStream; +class SaveFileWriteStream; +class Vector3; class Combat { static const int kSoundCount = 9; @@ -36,14 +41,32 @@ class Combat { bool _active; bool _enabled; - int _hitSoundId[kSoundCount]; - int _missSoundId[kSoundCount]; -// int _random1; -// int _random2; + int _hitSoundId[kSoundCount]; + int _missSoundId[kSoundCount]; + // int _random1; + // int _random2; public: int _ammoDamage[3]; + struct CoverWaypoint { + int type; + int setId; + int sceneId; + Vector3 position; + }; + + struct FleeWaypoint { + int type; + int setId; + int sceneId; + Vector3 position; + int field7; + }; + + Common::Array<CoverWaypoint> _coverWaypoints; + Common::Array<FleeWaypoint> _fleeWaypoints; + public: Combat(BladeRunnerEngine *vm); ~Combat(); @@ -60,10 +83,16 @@ public: void setHitSound(int ammoType, int column, int soundId); void setMissSound(int ammoType, int column, int soundId); - int getHitSound(); - int getMissSound(); + int getHitSound() const; + int getMissSound() const; void shoot(int actorId, Vector3 &to, int screenX); + + int findFleeWaypoint(int setId, int enemyId, const Vector3& position) const; + int findCoverWaypoint(int waypointType, int actorId, int enemyId) const; + + void save(SaveFileWriteStream &f); + void load(SaveFileReadStream &f); }; } // End of namespace BladeRunner diff --git a/engines/bladerunner/crimes_database.cpp b/engines/bladerunner/crimes_database.cpp index febe408cd9..e9faa22b41 100644 --- a/engines/bladerunner/crimes_database.cpp +++ b/engines/bladerunner/crimes_database.cpp @@ -24,11 +24,12 @@ #include "bladerunner/bladerunner.h" +#include "bladerunner/savefile.h" #include "bladerunner/text_resource.h" namespace BladeRunner { -CrimesDatabase::CrimesDatabase(BladeRunnerEngine *vm, const char *cluesResource, int crimeCount) { +CrimesDatabase::CrimesDatabase(BladeRunnerEngine *vm, const Common::String &cluesResource, int crimeCount) { _crimeCount = crimeCount; _crimes.resize(_crimeCount); @@ -70,4 +71,17 @@ const char *CrimesDatabase::getClueText(int clueId) const { return _cluesText->getText(clueId); } +void CrimesDatabase::save(SaveFileWriteStream &f) { + for (int i = 0; i < _crimeCount; ++i) { + uint8 c = _crimes[i]; + f.writeByte(c); + } +} + +void CrimesDatabase::load(SaveFileReadStream &f) { + for (int i = 0; i < _crimeCount; ++i) { + _crimes[i] = f.readByte(); + } +} + } // End of namespace BladeRunner diff --git a/engines/bladerunner/crimes_database.h b/engines/bladerunner/crimes_database.h index 40e46cb356..9bb83f148f 100644 --- a/engines/bladerunner/crimes_database.h +++ b/engines/bladerunner/crimes_database.h @@ -28,6 +28,8 @@ namespace BladeRunner { class BladeRunnerEngine; +class SaveFileReadStream; +class SaveFileWriteStream; class TextResource; class CrimesDatabase { @@ -37,7 +39,7 @@ class CrimesDatabase { TextResource *_cluesText; public: - CrimesDatabase(BladeRunnerEngine *vm, const char *cluesResource, int crimeCount); + CrimesDatabase(BladeRunnerEngine *vm, const Common::String &cluesResource, int crimeCount); ~CrimesDatabase(); void setCrime(int clueId, int crimeId); @@ -47,6 +49,9 @@ public: int getAssetType(int clueId) const; const char *getClueText(int clueId) const; + + void save(SaveFileWriteStream &f); + void load(SaveFileReadStream &f); }; } // End of namespace BladeRunner diff --git a/engines/bladerunner/debugger.cpp b/engines/bladerunner/debugger.cpp index 5539082187..0355f9d0f5 100644 --- a/engines/bladerunner/debugger.cpp +++ b/engines/bladerunner/debugger.cpp @@ -25,6 +25,7 @@ #include "bladerunner/actor.h" #include "bladerunner/bladerunner.h" #include "bladerunner/boundingbox.h" +#include "bladerunner/combat.h" #include "bladerunner/font.h" #include "bladerunner/game_constants.h" #include "bladerunner/game_flags.h" @@ -55,6 +56,8 @@ Debugger::Debugger(BladeRunnerEngine *vm) : GUI::Debugger() { _vm = vm; _viewSceneObjects = false; + _viewActorsOnly = false; + _viewObstacles = false; _viewUI = false; _viewZBuffer = false; @@ -105,8 +108,8 @@ bool Debugger::cmdAnimation(int argc, const char **argv) { bool Debugger::cmdDraw(int argc, const char **argv) { if (argc != 2) { - debugPrintf("Enables debug rendering of scene objects, ui elements, zbuffer or disables debug rendering.\n"); - debugPrintf("Usage: %s (obj | ui | zbuf | reset)\n", argv[0]); + debugPrintf("Enables debug rendering of scene objects, obstacles, ui elements, zbuffer or disables debug rendering.\n"); + debugPrintf("Usage: %s (obj | actors | obstacles | ui | zbuf | reset)\n", argv[0]); return true; } @@ -114,6 +117,13 @@ bool Debugger::cmdDraw(int argc, const char **argv) { if (arg == "obj") { _viewSceneObjects = !_viewSceneObjects; debugPrintf("Drawing scene objects = %i\n", _viewSceneObjects); + } else if (arg == "actors") { + _viewSceneObjects = !_viewSceneObjects; + _viewActorsOnly = _viewSceneObjects; + debugPrintf("Drawing scene actors = %i\n", _viewSceneObjects); + } else if (arg == "obstacles") { + _viewObstacles = !_viewObstacles; + debugPrintf("Drawing obstacles = %i\n", _viewObstacles); } else if (arg == "ui") { _viewUI = !_viewUI; debugPrintf("Drawing UI elements = %i\n", _viewUI); @@ -256,7 +266,6 @@ bool Debugger::cmdPosition(int argc, const char **argv) { debugPrintf("actorY(%i) = %f\n", actorId, actor->getY()); debugPrintf("actorZ(%i) = %f\n", actorId, actor->getZ()); debugPrintf("actorFacing(%i) = %i\n", actorId, actor->getFacing()); - return true; } if (argc == 3) { @@ -271,11 +280,9 @@ bool Debugger::cmdPosition(int argc, const char **argv) { return true; } - Vector3 position; - otherActor->getXYZ(&position.x, &position.y, &position.z); + Vector3 position = otherActor->getXYZ(); actor->setSetId(otherActor->getSetId()); actor->setAtXYZ(position, otherActor->getFacing()); - return true; } if (argc == 7) { @@ -285,7 +292,6 @@ bool Debugger::cmdPosition(int argc, const char **argv) { actor->setSetId(setId); actor->setAtXYZ(position, facing); - return true; } return true; } @@ -386,19 +392,22 @@ void Debugger::drawSceneObjects() { for (int i = 0; i < count; i++) { SceneObjects::SceneObject *sceneObject = &_vm->_sceneObjects->_sceneObjects[_vm->_sceneObjects->_sceneObjectsSortedByDistance[i]]; - const BoundingBox *bbox = sceneObject->boundingBox; + const BoundingBox &bbox = sceneObject->boundingBox; Vector3 a, b; - bbox->getXYZ(&a.x, &a.y, &a.z, &b.x, &b.y, &b.z); + bbox.getXYZ(&a.x, &a.y, &a.z, &b.x, &b.y, &b.z); Vector3 pos = _vm->_view->calculateScreenPosition(0.5 * (a + b)); int color; + if (_viewActorsOnly && sceneObject->type != kSceneObjectTypeActor) + continue; + switch (sceneObject->type) { case kSceneObjectTypeUnknown: break; case kSceneObjectTypeActor: color = 0x7C00; // 11111 00000 00000; drawBBox(a, b, _vm->_view, &_vm->_surfaceFront, color); - _vm->_surfaceFront.frameRect(*sceneObject->screenRectangle, color); + _vm->_surfaceFront.frameRect(sceneObject->screenRectangle, color); _vm->_mainFont->drawColor(_vm->_textActorNames->getText(sceneObject->id - kSceneObjectOffsetActors), _vm->_surfaceFront, pos.x, pos.y, color); break; case kSceneObjectTypeItem: @@ -406,7 +415,7 @@ void Debugger::drawSceneObjects() { char itemText[40]; drawBBox(a, b, _vm->_view, &_vm->_surfaceFront, color); sprintf(itemText, "item %i", sceneObject->id - kSceneObjectOffsetItems); - _vm->_surfaceFront.frameRect(*sceneObject->screenRectangle, color); + _vm->_surfaceFront.frameRect(sceneObject->screenRectangle, color); _vm->_mainFont->drawColor(itemText, _vm->_surfaceFront, pos.x, pos.y, color); break; case kSceneObjectTypeObject: @@ -417,13 +426,16 @@ void Debugger::drawSceneObjects() { color = 0x03E0; // 00000 11111 00000; } drawBBox(a, b, _vm->_view, &_vm->_surfaceFront, color); - _vm->_surfaceFront.frameRect(*sceneObject->screenRectangle, color); + _vm->_surfaceFront.frameRect(sceneObject->screenRectangle, color); _vm->_mainFont->drawColor(_vm->_scene->objectGetName(sceneObject->id - kSceneObjectOffsetObjects), _vm->_surfaceFront, pos.x, pos.y, color); break; } } } + if (_viewActorsOnly) + return; + //draw regions for (int i = 0; i < 10; i++) { Regions::Region *region = &_vm->_scene->_regions->_regions[i]; @@ -481,12 +493,13 @@ void Debugger::drawSceneObjects() { } //draw waypoints - for(int i = 0; i < _vm->_waypoints->_count; i++) { + for (int i = 0; i < _vm->_waypoints->_count; i++) { Waypoints::Waypoint *waypoint = &_vm->_waypoints->_waypoints[i]; - if(waypoint->setId != _vm->_scene->getSetId()) + if(waypoint->setId != _vm->_scene->getSetId()) { continue; + } Vector3 pos = waypoint->position; - Vector3 size = Vector3(5.0f, 5.0f, 5.0f); + Vector3 size = Vector3(3.0f, 3.0f, 3.0f); int color = 0x7FFF; // 11111 11111 11111 drawBBox(pos - size, pos + size, _vm->_view, &_vm->_surfaceFront, color); Vector3 spos = _vm->_view->calculateScreenPosition(pos); @@ -495,6 +508,38 @@ void Debugger::drawSceneObjects() { _vm->_mainFont->drawColor(waypointText, _vm->_surfaceFront, spos.x, spos.y, color); } + //draw combat cover waypoints + for (int i = 0; i < (int)_vm->_combat->_coverWaypoints.size(); i++) { + Combat::CoverWaypoint *cover = &_vm->_combat->_coverWaypoints[i]; + if (cover->setId != _vm->_scene->getSetId()) { + continue; + } + Vector3 pos = cover->position; + Vector3 size = Vector3(3.0f, 3.0f, 3.0f); + int color = 0x7C1F; // 11111 00000 11111 + drawBBox(pos - size, pos + size, _vm->_view, &_vm->_surfaceFront, color); + Vector3 spos = _vm->_view->calculateScreenPosition(pos); + char coverText[40]; + sprintf(coverText, "cover %i", i); + _vm->_mainFont->drawColor(coverText, _vm->_surfaceFront, spos.x, spos.y, color); + } + + //draw combat flee waypoints + for (int i = 0; i < (int)_vm->_combat->_fleeWaypoints.size(); i++) { + Combat::FleeWaypoint *flee = &_vm->_combat->_fleeWaypoints[i]; + if (flee->setId != _vm->_scene->getSetId()) { + continue; + } + Vector3 pos = flee->position; + Vector3 size = Vector3(3.0f, 3.0f, 3.0f); + int color = 0x03FF; // 00000 11111 11111 + drawBBox(pos - size, pos + size, _vm->_view, &_vm->_surfaceFront, color); + Vector3 spos = _vm->_view->calculateScreenPosition(pos); + char fleeText[40]; + sprintf(fleeText, "flee %i", i); + _vm->_mainFont->drawColor(fleeText, _vm->_surfaceFront, spos.x, spos.y, color); + } + #if 0 //draw aesc for (uint i = 0; i < _screenEffects->_entries.size(); i++) { diff --git a/engines/bladerunner/debugger.h b/engines/bladerunner/debugger.h index bfe4e49b9b..64e312ae20 100644 --- a/engines/bladerunner/debugger.h +++ b/engines/bladerunner/debugger.h @@ -41,6 +41,8 @@ class Debugger : public GUI::Debugger{ public: bool _viewSceneObjects; + bool _viewActorsOnly; + bool _viewObstacles; bool _viewUI; bool _viewZBuffer; diff --git a/engines/bladerunner/detection_tables.h b/engines/bladerunner/detection_tables.h index 52627890ea..3c509f4314 100644 --- a/engines/bladerunner/detection_tables.h +++ b/engines/bladerunner/detection_tables.h @@ -30,24 +30,18 @@ static const ADGameDescription gameDescriptions[] = { { "bladerunner", 0, - { - {"STARTUP.MIX", 0, "5643b53306ca7764cf1ec7b79c9630a3", 2312374}, - AD_LISTEND - }, + AD_ENTRY1s("STARTUP.MIX", "5643b53306ca7764cf1ec7b79c9630a3", 2312374), Common::EN_ANY, Common::kPlatformWindows, ADGF_NO_FLAGS, GUIO0() }, - + // BladeRunner (German) { "bladerunner", 0, - { - {"STARTUP.MIX", 0, "57d674ed860148a530b7f4957cbe65ec", 2314301}, - AD_LISTEND - }, + AD_ENTRY1s("STARTUP.MIX", "57d674ed860148a530b7f4957cbe65ec", 2314301), Common::DE_DEU, Common::kPlatformWindows, ADGF_NO_FLAGS, @@ -58,10 +52,7 @@ static const ADGameDescription gameDescriptions[] = { { "bladerunner", 0, - { - {"STARTUP.MIX", 0, "39d1901df50935d58aee252707134952", 2314526}, - AD_LISTEND - }, + AD_ENTRY1s("STARTUP.MIX", "39d1901df50935d58aee252707134952", 2314526), Common::FR_FRA, Common::kPlatformWindows, ADGF_NO_FLAGS, @@ -72,10 +63,7 @@ static const ADGameDescription gameDescriptions[] = { { "bladerunner", 0, - { - {"STARTUP.MIX", 0, "c7ceb9c691223d25e78516aa519ff504", 2314461}, - AD_LISTEND - }, + AD_ENTRY1s("STARTUP.MIX", "c7ceb9c691223d25e78516aa519ff504", 2314461), Common::IT_ITA, Common::kPlatformWindows, ADGF_NO_FLAGS, @@ -86,10 +74,7 @@ static const ADGameDescription gameDescriptions[] = { { "bladerunner", 0, - { - {"STARTUP.MIX", 0, "c198b54a5366b88b1734bbca21d3b192", 2678672}, - AD_LISTEND - }, + AD_ENTRY1s("STARTUP.MIX", "c198b54a5366b88b1734bbca21d3b192", 2678672), Common::RU_RUS, Common::kPlatformWindows, ADGF_NO_FLAGS, @@ -100,10 +85,7 @@ static const ADGameDescription gameDescriptions[] = { { "bladerunner", 0, - { - {"STARTUP.MIX", 0, "54cad53da9e4ae03a85648834ac6765d", 2312976}, - AD_LISTEND - }, + AD_ENTRY1s("STARTUP.MIX", "54cad53da9e4ae03a85648834ac6765d", 2312976), Common::ES_ESP, Common::kPlatformWindows, ADGF_NO_FLAGS, diff --git a/engines/bladerunner/dialogue_menu.cpp b/engines/bladerunner/dialogue_menu.cpp index 685c8459e0..d9a7c62866 100644 --- a/engines/bladerunner/dialogue_menu.cpp +++ b/engines/bladerunner/dialogue_menu.cpp @@ -25,6 +25,7 @@ #include "bladerunner/bladerunner.h" #include "bladerunner/font.h" #include "bladerunner/mouse.h" +#include "bladerunner/savefile.h" #include "bladerunner/settings.h" #include "bladerunner/shape.h" #include "bladerunner/text_resource.h" @@ -158,7 +159,7 @@ int DialogueMenu::queryInput() { if (_items[0].isDone) { _selectedItemIndex = 1; answer = _items[0].answerValue; - } else if (_items[0].isDone) { + } else if (_items[1].isDone) { _selectedItemIndex = 0; answer = _items[1].answerValue; } @@ -364,6 +365,54 @@ bool DialogueMenu::waitingForInput() const { return _waitingForInput; } +void DialogueMenu::save(SaveFileWriteStream &f) { + f.writeBool(_isVisible); + f.writeBool(_waitingForInput); + f.writeInt(_selectedItemIndex); + f.writeInt(_listSize); + + f.writeInt(_neverRepeatListSize); + for (int i = 0; i < 100; ++i) { + f.writeInt(_neverRepeatValues[i]); + } + for (int i = 0; i < 100; ++i) { + f.writeBool(_neverRepeatWasSelected[i]); + } + for (int i = 0; i < 10; ++i) { + f.writeStringSz(_items[i].text, 50); + f.writeInt(_items[i].answerValue); + f.writeInt(_items[i].colorIntensity); + f.writeInt(_items[i].priorityPolite); + f.writeInt(_items[i].priorityNormal); + f.writeInt(_items[i].prioritySurly); + f.writeInt(_items[i].isDone); + } +} + +void DialogueMenu::load(SaveFileReadStream &f) { + _isVisible = f.readBool(); + _waitingForInput = f.readBool(); + _selectedItemIndex = f.readInt(); + _listSize = f.readInt(); + + _neverRepeatListSize = f.readInt(); + for (int i = 0; i < 100; ++i) { + _neverRepeatValues[i] = f.readInt(); + } + for (int i = 0; i < 100; ++i) { + _neverRepeatWasSelected[i] = f.readBool(); + } + for (int i = 0; i < 10; ++i) { + _items[i].text = f.readStringSz(50); + _items[i].answerValue = f.readInt(); + _items[i].colorIntensity = f.readInt(); + _items[i].priorityPolite = f.readInt(); + _items[i].priorityNormal = f.readInt(); + _items[i].prioritySurly = f.readInt(); + _items[i].isDone = f.readInt(); + } +} + void DialogueMenu::clear() { _isVisible = false; _waitingForInput = false; diff --git a/engines/bladerunner/dialogue_menu.h b/engines/bladerunner/dialogue_menu.h index 63e23d8ada..6dfd3efdc6 100644 --- a/engines/bladerunner/dialogue_menu.h +++ b/engines/bladerunner/dialogue_menu.h @@ -33,6 +33,8 @@ namespace BladeRunner { class BladeRunnerEngine; +class SaveFileReadStream; +class SaveFileWriteStream; class TextResource; class DialogueMenu { @@ -96,12 +98,14 @@ public: void mouseUp(); bool waitingForInput() const; + void save(SaveFileWriteStream &f); + void load(SaveFileReadStream &f); + private: bool showAt(int x, int y); int getAnswerIndex(int answer) const; const char *getText(int id) const; void calculatePosition(int unusedX = 0, int unusedY = 0); - void clear(); void reset(); diff --git a/engines/bladerunner/fog.cpp b/engines/bladerunner/fog.cpp index fff27b0558..cb8235ba64 100644 --- a/engines/bladerunner/fog.cpp +++ b/engines/bladerunner/fog.cpp @@ -27,7 +27,6 @@ namespace BladeRunner { Fog::Fog() { - _name[0] = 0; _frameCount = 0; _animatedParameters = 0; _fogDensity = 0.0f; @@ -55,7 +54,9 @@ Fog::~Fog() { int Fog::readCommon(Common::ReadStream *stream) { int offset = stream->readUint32LE(); - stream->read(_name, 20); + char buf[20]; + stream->read(buf, sizeof(buf)); + _name = buf; _fogColor.r = stream->readFloatLE(); _fogColor.g = stream->readFloatLE(); _fogColor.b = stream->readFloatLE(); @@ -65,7 +66,7 @@ int Fog::readCommon(Common::ReadStream *stream) { void Fog::readAnimationData(Common::ReadStream *stream, int size) { _animatedParameters = stream->readUint32LE(); - + int floatCount = size / 4; _animationData = new float[floatCount]; for (int i = 0; i < floatCount; i++) { diff --git a/engines/bladerunner/fog.h b/engines/bladerunner/fog.h index 95ac550fd1..e952d24165 100644 --- a/engines/bladerunner/fog.h +++ b/engines/bladerunner/fog.h @@ -38,7 +38,8 @@ class Fog { friend class SetEffects; protected: - char _name[20]; + Common::String _name; + int _frameCount; int _animatedParameters; Matrix4x3 _matrix; diff --git a/engines/bladerunner/font.cpp b/engines/bladerunner/font.cpp index 5ae1c8eedc..d4b0e16515 100644 --- a/engines/bladerunner/font.cpp +++ b/engines/bladerunner/font.cpp @@ -120,6 +120,14 @@ void Font::drawColor(const Common::String &text, Graphics::Surface &surface, int draw(text, surface, x, y); } +void Font::drawNumber(int num, Graphics::Surface &surface, int x, int y) const { + char buffer[20]; + + snprintf(buffer, 20, "%d", num); + + draw(buffer, surface, x, y); +} + int Font::getTextWidth(const Common::String &text) const { const uint8 *character = (const uint8 *)text.c_str(); diff --git a/engines/bladerunner/font.h b/engines/bladerunner/font.h index 9302520153..4af25468c6 100644 --- a/engines/bladerunner/font.h +++ b/engines/bladerunner/font.h @@ -69,6 +69,7 @@ public: void draw(const Common::String &text, Graphics::Surface &surface, int x, int y) const; void drawColor(const Common::String &text, Graphics::Surface &surface, int x, int y, uint16 color); + void drawNumber(int num, Graphics::Surface &surface, int x, int y) const; int getTextWidth(const Common::String &text) const; int getTextHeight(const Common::String &text) const; diff --git a/engines/bladerunner/game_constants.h b/engines/bladerunner/game_constants.h index af4728aaad..79381975f7 100644 --- a/engines/bladerunner/game_constants.h +++ b/engines/bladerunner/game_constants.h @@ -535,6 +535,8 @@ enum Flags { enum Variables { kVariableChapter = 1, kVariableChinyen = 2, + kVariablePoliceMazeScore = 9, + kVariablePoliceMazePS10TargetCounter = 10, kVariableGenericWalkerAModel = 32, kVariableGenericWalkerBModel = 33, kVariableGenericWalkerCModel = 34, @@ -597,14 +599,17 @@ enum AnimationModes { kAnimationModeTalk = 3, kAnimationModeCombatIdle = 4, kAnimationModeCombatAim = 5, - kAnimationModeCombatShoot = 6, + kAnimationModeCombatAttack = 6, kAnimationModeCombatWalk = 7, kAnimationModeCombatRun = 8, + kAnimationModeHit = 21, + kAnimationModeCombatHit = 22, kAnimationModeWalkUp = 44, kAnimationModeWalkDown = 45, kAnimationModeCombatWalkUp = 46, kAnimationModeCombatWalkDown = 47, - kAnimationModeDie = 48, // TODO: check + kAnimationModeDie = 48, + kAnimationModeCombatDie = 49, kAnimationModeFeeding = 52, kAnimationModeSit = 53, // TODO: check kAnimationModeClimbUp = 64, @@ -842,6 +847,15 @@ enum Sets { }; enum GameItems { + kItemPoliceMazeTarget1 = 0, + kItemPoliceMazeTarget2 = 1, + kItemPoliceMazeTarget3 = 2, + kItemPoliceMazeTarget4 = 3, + kItemPoliceMazeTarget5 = 4, + kItemPoliceMazeTarget6 = 5, + kItemPoliceMazeTarget7 = 6, + kItemPoliceMazeTarget8 = 7, + kItemPoliceMazeTarget9 = 8, kItemChromeDebris = 66, kItemCandy = 79, kItemChopstickWrapper = 82, @@ -871,6 +885,48 @@ enum SceneObjectOffset { kSceneObjectOffsetObjects = 198 }; +enum ActorCombatStates { + kActorCombatStateIdle = 0, + kActorCombatStateCover = 1, + kActorCombatStateApproachCloseAttack = 2, + kActorCombatStateUncover = 3, + kActorCombatStateAim = 4, + kActorCombatStateRangedAttack = 5, + kActorCombatStateCloseAttack = 6, + kActorCombatStateFlee = 7, + kActorCombatStateApproachRangedAttack = 8 +}; + +enum PoliceMazeTrackInstruction { + kPMTIActivate = -26, + kPMTILeave = -25, + kPMTIShoot = -24, + kPMTIEnemyReset = -23, + kPMTIEnemySet = -22, + kPMTIFlagReset = -21, + kPMTIFlagSet = -20, + kPMTIVariableDec = -19, + kPMTIVariableInc = -18, + kPMTIVariableReset = -17, + kPMTIVariableSet = -16, + kPMTITargetSet = -15, + kPMTI12 = -14, + kPMTI13 = -13, + kPMTIPausedSet = -12, + kPMTIPausedReset = -11, + kPMTIPlaySound = -10, + kPMTIObstacleReset = -9, + kPMTIObstacleSet = -8, + kPMTIWaitRandom = -7, + kPMTIRotate = -6, + kPMTIFacing = -5, + kPMTIRestart = -4, + kPMTIWait = -3, + kPMTIMove = -2, + kPMTIPosition = -1, + kPMTI26 = 0 +}; + } // End of namespace BladeRunner #endif diff --git a/engines/bladerunner/game_flags.cpp b/engines/bladerunner/game_flags.cpp index 81fe6a0a4f..6fdcb89363 100644 --- a/engines/bladerunner/game_flags.cpp +++ b/engines/bladerunner/game_flags.cpp @@ -22,6 +22,8 @@ #include "bladerunner/game_flags.h" +#include "bladerunner/savefile.h" + #include "common/debug.h" namespace BladeRunner { @@ -38,7 +40,7 @@ void GameFlags::setFlagCount(int count) { assert(count > 0); _flagCount = count; - _flags = new uint32[count / 32 + 1]; + _flags = new uint32[count / 32 + 1](); for (int i = 0; i <= _flagCount; ++i) reset(i); @@ -71,4 +73,16 @@ bool GameFlags::query(int flag) const { return !!(_flags[flag / 32] & (1 << (flag % 32))); } +void GameFlags::save(SaveFileWriteStream &f) { + for (int i = 0; i != _flagCount / 32 + 1; ++i) { + f.writeUint32LE(_flags[i]); + } +} + +void GameFlags::load(SaveFileReadStream &f) { + for (int i = 0; i != _flagCount / 32 + 1; ++i) { + _flags[i] = f.readUint32LE(); + } +} + } // End of namespace BladeRunner diff --git a/engines/bladerunner/game_flags.h b/engines/bladerunner/game_flags.h index 83cbdbc086..837537f689 100644 --- a/engines/bladerunner/game_flags.h +++ b/engines/bladerunner/game_flags.h @@ -27,6 +27,9 @@ namespace BladeRunner { +class SaveFileReadStream; +class SaveFileWriteStream; + class GameFlags { uint32 *_flags; int _flagCount; @@ -40,6 +43,9 @@ public: void set(int flag); void reset(int flag); bool query(int flag) const; + + void save(SaveFileWriteStream &f); + void load(SaveFileReadStream &f); }; } // End of namespace BladeRunner diff --git a/engines/bladerunner/game_info.cpp b/engines/bladerunner/game_info.cpp index db664c0f2d..9dc261bc31 100644 --- a/engines/bladerunner/game_info.cpp +++ b/engines/bladerunner/game_info.cpp @@ -31,10 +31,6 @@ namespace BladeRunner { GameInfo::GameInfo(BladeRunnerEngine *vm) { _vm = vm; - _sceneNames = nullptr; - _sfxTracks = nullptr; - _musicTracks = nullptr; - _outtakes = nullptr; _actorCount = 0; _playerId = 0; _flagCount = 0; @@ -53,18 +49,12 @@ GameInfo::GameInfo(BladeRunnerEngine *vm) { _fleeWaypointCount = 0; } -GameInfo::~GameInfo() { - delete[] _sceneNames; - delete[] _sfxTracks; - delete[] _musicTracks; - delete[] _outtakes; -} - bool GameInfo::open(const Common::String &name) { Common::SeekableReadStream *s = _vm->getResourceStream(name); - if (!s) + if (!s) { return false; + } uint32 unk; _actorCount = s->readUint32LE(); /* 00 */ @@ -88,25 +78,33 @@ bool GameInfo::open(const Common::String &name) { (void)unk; - _sceneNames = new char[_sceneNamesCount][5]; - for (uint32 i = 0; i != _sceneNamesCount; ++i) - s->read(_sceneNames[i], 5); + char buf[9]; + + _sceneNames.resize(_sceneNamesCount); + for (uint32 i = 0; i != _sceneNamesCount; ++i) { + s->read(buf, 5); + _sceneNames[i] = buf; + } - _sfxTracks = new char[_sfxTrackCount][13]; + _sfxTracks.resize(_sfxTrackCount); for (uint32 i = 0; i != _sfxTrackCount; ++i) { - s->read(_sfxTracks[i], 9); - strcat(_sfxTracks[i], ".AUD"); + s->read(buf, 9); + _sfxTracks[i] = buf; + _sfxTracks[i] += ".AUD"; } - _musicTracks = new char[_musicTrackCount][13]; + _musicTracks.resize(_musicTrackCount); for (uint32 i = 0; i != _musicTrackCount; ++i) { - s->read(_musicTracks[i], 9); - strcat(_musicTracks[i], ".AUD"); + s->read(buf, 9); + _musicTracks[i] = buf; + _musicTracks[i] += ".AUD"; } - _outtakes = new char[_outtakeCount][13]; - for (uint32 i = 0; i != _outtakeCount; ++i) - s->read(_outtakes[i], 9); + _outtakes.resize(_outtakeCount); + for (uint32 i = 0; i != _outtakeCount; ++i) { + s->read(buf, 9); + _outtakes[i] = buf; + } #if BLADERUNNER_DEBUG_CONSOLE debug("\nScene names\n----------------"); @@ -135,34 +133,38 @@ bool GameInfo::open(const Common::String &name) { return !err; } -const char *GameInfo::getSceneName(int i) const { +const Common::String &GameInfo::getSceneName(int i) const { if (i < 0 || i >= (int)_sceneNamesCount) { warning("GameInfo::getSceneName: unknown id \"%i\"", i); - return nullptr; + static Common::String str("UNKNOWN_SCENE"); + return str; } return _sceneNames[i]; } -const char *GameInfo::getSfxTrack(int i) const { +const Common::String &GameInfo::getSfxTrack(int i) const { if (i < 0 || i >= (int)_sfxTrackCount) { warning("GameInfo::getSfxTrack: unknown id \"%i\"", i); - return nullptr; + static Common::String str("UNKNOWN_SFX_TRACK"); + return str; } return _sfxTracks[i]; } -const char *GameInfo::getMusicTrack(int i) const { +const Common::String &GameInfo::getMusicTrack(int i) const { if (i < 0 || i >= (int)_musicTrackCount) { warning("GameInfo::getMusicTrack: unknown id \"%i\"", i); - return nullptr; + static Common::String str("UNKNOWN_MUSIC_TRACK"); + return str; } return _musicTracks[i]; } -const char *GameInfo::getOuttake(int i) const { +const Common::String &GameInfo::getOuttake(int i) const { if (i < 0 || i >= (int)_outtakeCount) { warning("GameInfo::getOuttake: unknown id \"%i\"", i); - return nullptr; + static Common::String str("UNKNOWN_OUTTAKE"); + return str; } return _outtakes[i]; } diff --git a/engines/bladerunner/game_info.h b/engines/bladerunner/game_info.h index bc7fc1eeec..857efa9e0b 100644 --- a/engines/bladerunner/game_info.h +++ b/engines/bladerunner/game_info.h @@ -23,6 +23,7 @@ #ifndef BLADERUNNER_GAME_INFO_H #define BLADERUNNER_GAME_INFO_H +#include "common/array.h" #include "common/str.h" namespace BladeRunner { @@ -49,14 +50,13 @@ class GameInfo { uint32 _coverWaypointCount; uint32 _fleeWaypointCount; - char (*_sceneNames)[5]; - char (*_sfxTracks)[13]; - char (*_musicTracks)[13]; - char (*_outtakes)[13]; + Common::Array<Common::String> _sceneNames; + Common::Array<Common::String> _sfxTracks; + Common::Array<Common::String> _musicTracks; + Common::Array<Common::String> _outtakes; public: GameInfo(BladeRunnerEngine *vm); - ~GameInfo(); bool open(const Common::String &name); @@ -77,10 +77,10 @@ public: uint32 getCoverWaypointCount() const { return _coverWaypointCount; } uint32 getFleeWaypointCount() const { return _fleeWaypointCount; } - const char *getSceneName(int i) const; - const char *getSfxTrack(int i) const; - const char *getMusicTrack(int i) const; - const char *getOuttake(int i) const; + const Common::String &getSceneName(int i) const; + const Common::String &getSfxTrack(int i) const; + const Common::String &getMusicTrack(int i) const; + const Common::String &getOuttake(int i) const; }; } // End of namespace BladeRunner diff --git a/engines/bladerunner/item.cpp b/engines/bladerunner/item.cpp index af7b2ef4c2..2f82fc0b35 100644 --- a/engines/bladerunner/item.cpp +++ b/engines/bladerunner/item.cpp @@ -24,6 +24,7 @@ #include "bladerunner/bladerunner.h" +#include "bladerunner/savefile.h" #include "bladerunner/slice_renderer.h" #include "bladerunner/zbuffer.h" @@ -73,13 +74,17 @@ bool Item::isTarget() const { return _isTarget; } +bool Item::isPoliceMazeEnemy() const { + return _isPoliceMazeEnemy; +} + bool Item::tick(Common::Rect *screenRect, bool special) { if (!_isVisible) { *screenRect = Common::Rect(); return false; } - bool isVisible = false; + bool isVisibleFlag = false; Vector3 position(_position.x, -_position.z, _position.y); int animationId = _animationId + (special ? 1 : 0); @@ -88,7 +93,7 @@ bool Item::tick(Common::Rect *screenRect, bool special) { if (!_screenRectangle.isEmpty()) { *screenRect = _screenRectangle; - isVisible = true; + isVisibleFlag = true; } else { *screenRect = Common::Rect(); } @@ -120,7 +125,7 @@ bool Item::tick(Common::Rect *screenRect, bool special) { } } - return isVisible; + return isVisibleFlag; } void Item::setXYZ(Vector3 position) { @@ -134,7 +139,7 @@ void Item::setXYZ(Vector3 position) { _depth = screenPosition.z * 25.5f; } -void Item::setup(int itemId, int setId, int animationId, Vector3 position, int facing, int height, int width, bool isTargetFlag, bool isVisible, bool isPoliceMazeEnemy) { +void Item::setup(int itemId, int setId, int animationId, Vector3 position, int facing, int height, int width, bool isTargetFlag, bool isVisibleFlag, bool isPoliceMazeEnemyFlag) { _itemId = itemId; _setId = setId; _animationId = animationId; @@ -143,8 +148,8 @@ void Item::setup(int itemId, int setId, int animationId, Vector3 position, int f _width = width; _height = height; _isTarget = isTargetFlag; - _isVisible = isVisible; - _isPoliceMazeEnemy = isPoliceMazeEnemy; + _isVisible = isVisibleFlag; + _isPoliceMazeEnemy = isPoliceMazeEnemyFlag; setXYZ(position); _screenRectangle.bottom = -1; _screenRectangle.right = -1; @@ -152,6 +157,15 @@ void Item::setup(int itemId, int setId, int animationId, Vector3 position, int f _screenRectangle.left = -1; } +void Item::spinInWorld() { + _isSpinning = true; + if (_vm->_rnd.getRandomNumberRng(1, 2) == 1) { + _facingChange = -340; + } else { + _facingChange = 340; + } +} + bool Item::isUnderMouse(int mouseX, int mouseY) const { return _isVisible && mouseX >= _screenRectangle.left - 10 @@ -160,4 +174,48 @@ bool Item::isUnderMouse(int mouseX, int mouseY) const { && mouseY <= _screenRectangle.bottom + 10; } +void Item::save(SaveFileWriteStream &f) { + f.writeInt(_setId); + f.writeInt(_itemId); + f.writeBoundingBox(_boundingBox); + f.writeRect(_screenRectangle); + f.writeInt(_animationId); + f.writeVector3(_position); + f.writeInt(_facing); + f.writeFloat(_angle); + f.writeInt(_width); + f.writeInt(_height); + f.writeInt(_screenX); + f.writeInt(_screenY); + f.writeFloat(_depth); + f.writeBool(_isTarget); + f.writeBool(_isSpinning); + f.writeInt(_facingChange); + f.writeFloat(0.0f); // _viewAngle + f.writeBool(_isVisible); + f.writeBool(_isPoliceMazeEnemy); +} + +void Item::load(SaveFileReadStream &f) { + _setId = f.readInt(); + _itemId = f.readInt(); + _boundingBox = f.readBoundingBox(); + _screenRectangle = f.readRect(); + _animationId = f.readInt(); + _position = f.readVector3(); + _facing = f.readInt(); + _angle = f.readFloat(); + _width = f.readInt(); + _height = f.readInt(); + _screenX = f.readInt(); + _screenY = f.readInt(); + _depth = f.readFloat(); + _isTarget = f.readBool(); + _isSpinning = f.readBool(); + _facingChange = f.readInt(); + f.skip(4); + _isVisible = f.readBool(); + _isPoliceMazeEnemy = f.readBool(); +} + } // End of namespace BladeRunner diff --git a/engines/bladerunner/item.h b/engines/bladerunner/item.h index 89bec5590b..a6420d1857 100644 --- a/engines/bladerunner/item.h +++ b/engines/bladerunner/item.h @@ -32,6 +32,8 @@ namespace BladeRunner { class BladeRunnerEngine; class Items; +class SaveFileReadStream; +class SaveFileWriteStream; class Item { friend class Items; @@ -65,12 +67,28 @@ public: void setXYZ(Vector3 position); void getWidthHeight(int *width, int *height) const; + const BoundingBox &getBoundingBox() { return _boundingBox; } + const Common::Rect &getScreenRectangle() { return _screenRectangle; } + int getFacing() const { return _facing; } + void setFacing(int facing) { _facing = facing; } + + void setIsTarget(bool val) { _isTarget = val; } + bool isTarget() const; + bool isSpinning() const { return _isSpinning; } + bool isVisible() const { return _isVisible; } + void setVisible(bool val) { _isVisible = val; } + bool isPoliceMazeEnemy() const; + void setPoliceMazeEnemy(bool val) { _isPoliceMazeEnemy = val; } + void spinInWorld(); bool tick(Common::Rect *screenRect, bool special); - void setup(int itemId, int setId, int animationId, Vector3 position, int facing, int height, int width, bool isTargetFlag, bool isVisible, bool isPoliceMazeEnemy); + void setup(int itemId, int setId, int animationId, Vector3 position, int facing, int height, int width, bool isTargetFlag, bool isVisibleFlag, bool isPoliceMazeEnemyFlag); bool isUnderMouse(int mouseX, int mouseY) const; + + void save(SaveFileWriteStream &f); + void load(SaveFileReadStream &f); }; } diff --git a/engines/bladerunner/item_pickup.h b/engines/bladerunner/item_pickup.h index 97d98ead36..0328bf428e 100644 --- a/engines/bladerunner/item_pickup.h +++ b/engines/bladerunner/item_pickup.h @@ -43,7 +43,7 @@ class ItemPickup { int _timeLeft; int _timeLast; Common::Rect _screenRect; - + public: ItemPickup(BladeRunnerEngine *vm); ~ItemPickup(); diff --git a/engines/bladerunner/items.cpp b/engines/bladerunner/items.cpp index 9d9efd6a38..a2cb9da194 100644 --- a/engines/bladerunner/items.cpp +++ b/engines/bladerunner/items.cpp @@ -23,6 +23,7 @@ #include "bladerunner/items.h" #include "bladerunner/game_constants.h" +#include "bladerunner/savefile.h" #include "bladerunner/scene.h" #include "bladerunner/scene_objects.h" #include "bladerunner/zbuffer.h" @@ -46,6 +47,13 @@ void Items::getXYZ(int itemId, float *x, float *y, float *z) const { _items[itemIndex]->getXYZ(x, y, z); } +void Items::setXYZ(int itemId, Vector3 position) { + int itemIndex = findItem(itemId); + assert(itemIndex != -1); + + _items[itemIndex]->setXYZ(position); +} + void Items::getWidthHeight(int itemId, int *width, int *height) const { int itemIndex = findItem(itemId); assert(itemIndex != -1); @@ -67,7 +75,7 @@ void Items::tick() { } } -bool Items::addToWorld(int itemId, int animationId, int setId, Vector3 position, int facing, int height, int width, bool isTargetFlag, bool isVisible, bool isPoliceMazeEnemy, bool addToSetFlag) { +bool Items::addToWorld(int itemId, int animationId, int setId, Vector3 position, int facing, int height, int width, bool isTargetFlag, bool isVisibleFlag, bool isPoliceMazeEnemyFlag, bool addToSetFlag) { if (_items.size() >= 100) { return false; } @@ -78,10 +86,10 @@ bool Items::addToWorld(int itemId, int animationId, int setId, Vector3 position, } Item *item = _items[itemIndex]; - item->setup(itemId, setId, animationId, position, facing, height, width, isTargetFlag, isVisible, isPoliceMazeEnemy); + item->setup(itemId, setId, animationId, position, facing, height, width, isTargetFlag, isVisibleFlag, isPoliceMazeEnemyFlag); if (addToSetFlag && setId == _vm->_scene->getSetId()) { - return _vm->_sceneObjects->addItem(itemId + kSceneObjectOffsetItems, &item->_boundingBox, &item->_screenRectangle, isTargetFlag, isVisible); + return _vm->_sceneObjects->addItem(itemId + kSceneObjectOffsetItems, item->_boundingBox, item->_screenRectangle, isTargetFlag, isVisibleFlag); } return true; } @@ -94,7 +102,7 @@ bool Items::addToSet(int setId) { for (int i = 0; i < itemCount; i++) { Item *item = _items[i]; if (item->_setId == setId) { - _vm->_sceneObjects->addItem(item->_itemId + kSceneObjectOffsetItems, &item->_boundingBox, &item->_screenRectangle, item->isTarget(), item->_isVisible); + _vm->_sceneObjects->addItem(item->_itemId + kSceneObjectOffsetItems, item->_boundingBox, item->_screenRectangle, item->isTarget(), item->_isVisible); } } return true; @@ -118,6 +126,15 @@ bool Items::remove(int itemId) { return true; } +void Items::setIsTarget(int itemId, bool val) { + int itemIndex = findItem(itemId); + if (itemIndex == -1) { + return; + } + _items[itemIndex]->setIsTarget(val); + _vm->_sceneObjects->setIsTarget(itemId + kSceneObjectOffsetItems, val); +} + bool Items::isTarget(int itemId) const { int itemIndex = findItem(itemId); if (itemIndex == -1) { @@ -126,6 +143,87 @@ bool Items::isTarget(int itemId) const { return _items[itemIndex]->isTarget(); } +bool Items::isSpinning(int itemId) const { + int itemIndex = findItem(itemId); + if (itemIndex == -1) { + return false; + } + return _items[itemIndex]->isSpinning(); +} + +bool Items::isVisible(int itemId) const { + int itemIndex = findItem(itemId); + if (itemIndex == -1) { + return false; + } + return _items[itemIndex]->isVisible(); +} + +bool Items::isPoliceMazeEnemy(int itemId) const { + int itemIndex = findItem(itemId); + if (itemIndex == -1) { + return false; + } + return _items[itemIndex]->isTarget(); +} + +void Items::setPoliceMazeEnemy(int itemId, bool val) { + int itemIndex = findItem(itemId); + if (itemIndex == -1) { + return; + } + _items[itemIndex]->setPoliceMazeEnemy(val); +} + +void Items::setIsObstacle(int itemId, bool val) { + int itemIndex = findItem(itemId); + if (itemIndex == -1) { + return; + } + _items[itemIndex]->setVisible(val); + _vm->_sceneObjects->setIsClickable(itemId + kSceneObjectOffsetItems, val); +} + +const BoundingBox &Items::getBoundingBox(int itemId) { + int itemIndex = findItem(itemId); + // if (itemIndex == -1) { + // return nullptr; + // } + return _items[itemIndex]->getBoundingBox(); +} + +const Common::Rect &Items::getScreenRectangle(int itemId) { + int itemIndex = findItem(itemId); + // if (itemIndex == -1) { + // return nullptr; + // } + return _items[itemIndex]->getScreenRectangle(); +} + +int Items::getFacing(int itemId) const { + int itemIndex = findItem(itemId); + if (itemIndex == -1) { + return 0; + } + return _items[itemIndex]->getFacing(); +} + +void Items::setFacing(int itemId, int facing) { + int itemIndex = findItem(itemId); + if (itemIndex == -1) { + return; + } + _items[itemIndex]->setFacing(facing); +} + +void Items::spinInWorld(int itemId) { + int itemIndex = findItem(itemId); + if (itemIndex == -1) { + return; + } + _items[itemIndex]->spinInWorld(); +} + int Items::findTargetUnderMouse(int mouseX, int mouseY) const { int setId = _vm->_scene->getSetId(); for (int i = 0 ; i < (int)_items.size(); ++i) { @@ -145,4 +243,39 @@ int Items::findItem(int itemId) const { return -1; } +void Items::save(SaveFileWriteStream &f) { + int size = (int)_items.size(); + + f.writeInt(size); + int i; + for (i = 0; i != size; ++i) { + _items[i]->save(f); + } + + // Always write out 100 items + for (; i != 100; ++i) { + f.padBytes(0x174); // bbox + rect + 18 float fields + } +} + +void Items::load(SaveFileReadStream &f) { + for (int i = _items.size() - 1; i >= 0; i--) { + delete _items.remove_at(i); + } + _items.resize(f.readInt()); + + int size = (int)_items.size(); + + int i; + for (i = 0; i != size; ++i) { + _items[i] = new Item(_vm); + _items[i]->load(f); + } + + // Always read out 100 items + for (; i != 100; ++i) { + f.skip(0x174); // bbox + rect + 18 float fields + } +} + } // End of namespace BladeRunner diff --git a/engines/bladerunner/items.h b/engines/bladerunner/items.h index 1aac82e8da..ce29a77787 100644 --- a/engines/bladerunner/items.h +++ b/engines/bladerunner/items.h @@ -30,6 +30,9 @@ namespace BladeRunner { +class SaveFileReadStream; +class SaveFileWriteStream; + class Items { BladeRunnerEngine *_vm; @@ -40,16 +43,33 @@ public: ~Items(); void getXYZ(int itemId, float *x, float *y, float *z) const; + void setXYZ(int itemId, Vector3 position); void getWidthHeight(int itemId, int *width, int *height) const; void tick(); - bool addToWorld(int itemId, int animationId, int setId, Vector3 position, int facing, int height, int width, bool isTargetFlag, bool isVisible, bool isPoliceMazeEnemy, bool addToSetFlag); + bool addToWorld(int itemId, int animationId, int setId, Vector3 position, int facing, int height, int width, bool isTargetFlag, bool isVisibleFlag, bool isPoliceMazeEnemyFlag, bool addToSetFlag); bool addToSet(int itemId); bool remove(int itemId); + void setIsTarget(int itemId, bool val); bool isTarget(int itemId) const; + bool isSpinning(int itemId) const; + bool isPoliceMazeEnemy(int itemId) const; + void setPoliceMazeEnemy(int itemId, bool val); + void setIsObstacle(int itemId, bool val); + bool isVisible(int itemId) const; int findTargetUnderMouse(int mouseX, int mouseY) const; + const BoundingBox &getBoundingBox(int itemId); + const Common::Rect &getScreenRectangle(int itemId); + int getFacing(int itemId) const; + void setFacing(int itemId, int facing); + + void spinInWorld(int itemId); + + void save(SaveFileWriteStream &f); + void load(SaveFileReadStream &f); + private: int findItem(int itemId) const; }; diff --git a/engines/bladerunner/light.cpp b/engines/bladerunner/light.cpp index 615958281a..2231e0460e 100644 --- a/engines/bladerunner/light.cpp +++ b/engines/bladerunner/light.cpp @@ -69,7 +69,9 @@ void Light::read(Common::ReadStream *stream, int frameCount, int frame, int anim int size = stream->readUint32LE(); size = size - 32; - stream->read(_name, 20); + char buf[20]; + stream->read(buf, sizeof(buf)); + _name = buf; _animatedParameters = stream->readUint32LE(); diff --git a/engines/bladerunner/light.h b/engines/bladerunner/light.h index 1ef9f3082c..31e40e0b45 100644 --- a/engines/bladerunner/light.h +++ b/engines/bladerunner/light.h @@ -42,7 +42,8 @@ class Light { friend class SliceRenderer; protected: - char _name[20]; + Common::String _name; + int _frameCount; int _animated; int _animatedParameters; diff --git a/engines/bladerunner/matrix.h b/engines/bladerunner/matrix.h index 5343eb6b8e..d5922b403f 100644 --- a/engines/bladerunner/matrix.h +++ b/engines/bladerunner/matrix.h @@ -57,8 +57,8 @@ inline Matrix3x2 operator*(const Matrix3x2 &a, const Matrix3x2 &b) { inline Matrix3x2 operator+(const Matrix3x2 &a, Vector2 b) { Matrix3x2 t(a); - t(0,2) += b.x; - t(1,2) += b.y; + t(0, 2) += b.x; + t(1, 2) += b.y; return t; } diff --git a/engines/bladerunner/module.mk b/engines/bladerunner/module.mk index 67827030cd..b0bae4f7bd 100644 --- a/engines/bladerunner/module.mk +++ b/engines/bladerunner/module.mk @@ -41,6 +41,7 @@ MODULE_OBJS = \ outtake.o \ overlays.o \ regions.o \ + savefile.o \ scene.o \ scene_objects.o \ screen_effects.o \ @@ -49,19 +50,24 @@ MODULE_OBJS = \ script/kia_script.o \ script/vk_script.o \ script/esper_script.o \ + script/police_maze.o \ script/ai_script.o \ script/ai/answering_machine.o \ script/ai/baker.o \ script/ai/blimp_guy.o \ script/ai/bryant.o \ + script/ai/bullet_bob.o \ script/ai/chew.o \ script/ai/clovis.o \ script/ai/crazylegs.o \ script/ai/dektora.o \ script/ai/desk_clerk.o \ script/ai/dispatcher.o \ + script/ai/early_q.o \ script/ai/early_q_bartender.o \ script/ai/fish_dealer.o \ + script/ai/free_slot_a.o \ + script/ai/free_slot_b.o \ script/ai/gaff.o \ script/ai/general_doll.o \ script/ai/generic_walker_a.o \ @@ -71,6 +77,8 @@ MODULE_OBJS = \ script/ai/governor_kolvig.o \ script/ai/grigorian.o \ script/ai/guzza.o \ + script/ai/hanoi.o \ + script/ai/hasan.o \ script/ai/hawkers_barkeep.o \ script/ai/hawkers_parrot.o \ script/ai/holloway.o \ @@ -86,6 +94,7 @@ MODULE_OBJS = \ script/ai/leon.o \ script/ai/lockup_guard.o \ script/ai/lucy.o \ + script/ai/luther.o \ script/ai/maggie.o \ script/ai/male_announcer.o \ script/ai/marcus.o \ @@ -97,11 +106,13 @@ MODULE_OBJS = \ script/ai/mutant2.o \ script/ai/mutant3.o \ script/ai/newscaster.o \ + script/ai/officer_grayford.o \ script/ai/officer_leary.o \ script/ai/photographer.o \ script/ai/rachael.o \ script/ai/rajif.o \ script/ai/runciter.o \ + script/ai/sadik.o \ script/ai/sebastian.o \ script/ai/sergeant_walls.o \ script/ai/shoeshine_man.o \ @@ -235,7 +246,9 @@ MODULE_OBJS = \ slice_renderer.o \ suspects_database.o \ text_resource.o \ + time.o \ ui/elevator.o \ + ui/end_credits.o \ ui/esper.o \ ui/kia.o \ ui/kia_log.o \ @@ -248,6 +261,7 @@ MODULE_OBJS = \ ui/kia_section_settings.o \ ui/kia_section_suspects.o \ ui/kia_shapes.o \ + ui/scores.o \ ui/spinner.o \ ui/ui_check_box.o \ ui/ui_container.o \ diff --git a/engines/bladerunner/mouse.cpp b/engines/bladerunner/mouse.cpp index 7d67e98de5..093c73fcff 100644 --- a/engines/bladerunner/mouse.cpp +++ b/engines/bladerunner/mouse.cpp @@ -51,6 +51,11 @@ Mouse::Mouse(BladeRunnerEngine *vm) { _disabledCounter = 0; _lastFrameTime = 0; _animCounter = 0; + + _randomCountdownX = 0; + _randomCountdownY = 0; + _randomX = 0; + _randomY = 0; } Mouse::~Mouse() { @@ -161,8 +166,55 @@ void Mouse::getXY(int *x, int *y) const { *y = _y; } +void Mouse::setMouseJitterUp() { + switch (_vm->_settings->getDifficulty()) { + case 0: + _randomCountdownX = 2; + _randomX = _vm->_rnd.getRandomNumberRng(0, 6) - 3; + _randomY = _vm->_rnd.getRandomNumberRng(0, 10) - 20; + break; + + case 1: + _randomCountdownX = 3; + _randomX = _vm->_rnd.getRandomNumberRng(0, 8) - 4; + _randomY = _vm->_rnd.getRandomNumberRng(0, 10) - 25; + break; + + case 2: + _randomCountdownX = 4; + _randomX = _vm->_rnd.getRandomNumberRng(0, 10) - 5; + _randomY = _vm->_rnd.getRandomNumberRng(0, 10) - 30; + break; + } +} + +void Mouse::setMouseJitterDown() { + switch (_vm->_settings->getDifficulty()) { + case 0: + _randomCountdownY = 2; + _randomX = _vm->_rnd.getRandomNumberRng(0, 6) - 3; + _randomY = _vm->_rnd.getRandomNumberRng(10, 20); + break; + + case 1: + _randomCountdownY = 3; + _randomX = _vm->_rnd.getRandomNumberRng(0, 8) - 4; + _randomY = _vm->_rnd.getRandomNumberRng(15, 25); + break; + + case 2: + _randomCountdownY = 4; + _randomX = _vm->_rnd.getRandomNumberRng(0, 10) - 5; + _randomY = _vm->_rnd.getRandomNumberRng(20, 30); + break; + } +} + void Mouse::disable() { ++_disabledCounter; + + _randomCountdownX = 0; + _randomCountdownY = 0; } void Mouse::enable() { @@ -177,9 +229,24 @@ bool Mouse::isDisabled() const { void Mouse::draw(Graphics::Surface &surface, int x, int y) { if (_disabledCounter) { + _randomCountdownX = 0; + _randomCountdownY = 0; return; } + if (_randomCountdownX > 0) { + _randomCountdownX--; + x += _randomX; + y += _randomY; + + if (!_randomCountdownX) + setMouseJitterDown(); + } else if (_randomCountdownY > 0){ + _randomCountdownY--; + x += _randomX; + y += _randomY; + } + _x = CLIP(x, 0, surface.w - 1); _y = CLIP(y, 0, surface.h - 1); diff --git a/engines/bladerunner/mouse.h b/engines/bladerunner/mouse.h index 2f33d72583..31ba17a6c3 100644 --- a/engines/bladerunner/mouse.h +++ b/engines/bladerunner/mouse.h @@ -46,6 +46,11 @@ class Mouse { int _lastFrameTime; int _animCounter; + int _randomCountdownX; + int _randomCountdownY; + int _randomX; + int _randomY; + public: Mouse(BladeRunnerEngine *vm); ~Mouse(); @@ -53,6 +58,8 @@ public: void setCursor(int cursor); void getXY(int *x, int *y) const; + void setMouseJitterUp(); + void setMouseJitterDown(); void disable(); void enable(); diff --git a/engines/bladerunner/movement_track.cpp b/engines/bladerunner/movement_track.cpp index 47eb56a098..322d92ef31 100644 --- a/engines/bladerunner/movement_track.cpp +++ b/engines/bladerunner/movement_track.cpp @@ -22,6 +22,8 @@ #include "bladerunner/movement_track.h" +#include "bladerunner/savefile.h" + namespace BladeRunner { MovementTrack::MovementTrack() { @@ -107,4 +109,32 @@ bool MovementTrack::next(int *waypointId, int *delay, int *angle, bool *run) { } } +void MovementTrack::save(SaveFileWriteStream &f) { + f.writeInt(_currentIndex); + f.writeInt(_lastIndex); + f.writeBool(_hasNext); + f.writeBool(_paused); + for (int i = 0; i < kSize; ++i) { + Entry &e = _entries[i]; + f.writeInt(e.waypointId); + f.writeInt(e.delay); + f.writeInt(e.angle); + f.writeBool(e.run); + } +} + +void MovementTrack::load(SaveFileReadStream &f) { + _currentIndex = f.readInt(); + _lastIndex = f.readInt(); + _hasNext = f.readBool(); + _paused = f.readBool(); + for (int i = 0; i < kSize; ++i) { + Entry &e = _entries[i]; + e.waypointId = f.readInt(); + e.delay = f.readInt(); + e.angle = f.readInt(); + e.run = f.readBool(); + } +} + } // End of namespace BladeRunner diff --git a/engines/bladerunner/movement_track.h b/engines/bladerunner/movement_track.h index cba9b690ff..2eab4cdedb 100644 --- a/engines/bladerunner/movement_track.h +++ b/engines/bladerunner/movement_track.h @@ -29,6 +29,8 @@ namespace BladeRunner { class BladeRunnerEngine; class BoundingBox; +class SaveFileReadStream; +class SaveFileWriteStream; class MovementTrack { static const int kSize = 100; @@ -59,7 +61,8 @@ public: bool hasNext() const; bool next(int *waypointId, int *delay, int *angle, bool *run); - //int saveGame(); + void save(SaveFileWriteStream &f); + void load(SaveFileReadStream &f); private: void reset(); diff --git a/engines/bladerunner/music.cpp b/engines/bladerunner/music.cpp index 818d412ba9..05412dd8e0 100644 --- a/engines/bladerunner/music.cpp +++ b/engines/bladerunner/music.cpp @@ -26,6 +26,7 @@ #include "bladerunner/aud_stream.h" #include "bladerunner/bladerunner.h" #include "bladerunner/game_info.h" +#include "bladerunner/savefile.h" #include "common/timer.h" @@ -167,6 +168,68 @@ void Music::playSample() { } } +void Music::save(SaveFileWriteStream &f) { + f.writeBool(_isNextPresent); + f.writeBool(_isPlaying); + f.writeBool(_isPaused); + f.writeStringSz(_current.name, 13); + f.writeInt(_current.volume); + f.writeInt(_current.pan); + f.writeInt(_current.timeFadeIn); + f.writeInt(_current.timePlay); + f.writeInt(_current.loop); + f.writeInt(_current.timeFadeOut); + f.writeStringSz(_next.name, 13); + f.writeInt(_next.volume); + f.writeInt(_next.pan); + f.writeInt(_next.timeFadeIn); + f.writeInt(_next.timePlay); + f.writeInt(_next.loop); + f.writeInt(_next.timeFadeOut); +} + +void Music::load(SaveFileReadStream &f) { + _isNextPresent = f.readBool(); + _isPlaying = f.readBool(); + _isPaused = f.readBool(); + _current.name = f.readStringSz(13); + _current.volume = f.readInt(); + _current.pan = f.readInt(); + _current.timeFadeIn = f.readInt(); + _current.timePlay = f.readInt(); + _current.loop = f.readInt(); + _current.timeFadeOut = f.readInt(); + _next.name = f.readStringSz(13); + _next.volume = f.readInt(); + _next.pan = f.readInt(); + _next.timeFadeIn = f.readInt(); + _next.timePlay = f.readInt(); + _next.loop = f.readInt(); + _next.timeFadeOut = f.readInt(); + + stop(2); + if (_isPlaying) { + if (_channel == -1) { + play(_current.name, + _current.volume, + _current.pan, + _current.timeFadeIn, + _current.timePlay, + _current.loop, + _current.timeFadeOut); + } else { + _isNextPresent = true; + _next.name = _current.name; + _next.volume = _current.volume; + _next.pan = _current.pan; + _next.timeFadeIn = _current.timeFadeIn; + _next.timePlay = _current.timePlay; + _next.loop = _current.loop; + _next.timeFadeOut = _current.timeFadeOut; + } + } +} + void Music::adjustVolume(int volume, int delay) { if (_channel >= 0) { _vm->_audioMixer->adjustVolume(_channel, volume, delay); diff --git a/engines/bladerunner/music.h b/engines/bladerunner/music.h index de19942a20..b4dc284def 100644 --- a/engines/bladerunner/music.h +++ b/engines/bladerunner/music.h @@ -30,6 +30,8 @@ namespace BladeRunner { class AudStream; class BladeRunnerEngine; +class SaveFileReadStream; +class SaveFileWriteStream; class Music { struct Track { @@ -47,9 +49,9 @@ class Music { Common::Mutex _mutex; int _musicVolume; int _channel; - int _isNextPresent; - int _isPlaying; - int _isPaused; + bool _isNextPresent; + bool _isPlaying; + bool _isPaused; Track _current; Track _next; byte *_data; @@ -68,6 +70,9 @@ public: int getVolume(); void playSample(); + void save(SaveFileWriteStream &f); + void load(SaveFileReadStream &f); + private: void adjustVolume(int volume, int delay); void adjustPan(int pan, int delay); diff --git a/engines/bladerunner/obstacles.cpp b/engines/bladerunner/obstacles.cpp index 8061e782f2..06c19ad4af 100644 --- a/engines/bladerunner/obstacles.cpp +++ b/engines/bladerunner/obstacles.cpp @@ -24,23 +24,42 @@ #include "bladerunner/bladerunner.h" +#include "bladerunner/savefile.h" +#include "bladerunner/scene.h" // for debug +#include "bladerunner/view.h" + +#include "common/debug.h" + +#define WITHIN_TOLERANCE(a, b) (((a) - 0.009) < (b) && ((a) + 0.009) > (b)) + namespace BladeRunner { Obstacles::Obstacles(BladeRunnerEngine *vm) { _vm = vm; - _vertices = new Vector2[150]; + _polygons = new Polygon[kPolygonCount]; + _polygonsBackup = new Polygon[kPolygonCount]; + _vertices = new Vector2[kVertexCount]; clear(); } Obstacles::~Obstacles() { + clear(); + + delete[] _polygons; + _polygons = nullptr; + + delete[] _polygonsBackup; + _polygonsBackup = nullptr; + delete[] _vertices; + _vertices = nullptr; } void Obstacles::clear() { for (int i = 0; i < kPolygonCount; i++) { _polygons[i].isPresent = false; _polygons[i].verticeCount = 0; - for (int j = 0; j < kVertexCount; j++) { + for (int j = 0; j < kPolygonVertexCount; j++) { _polygons[i].vertices[j].x = 0.0f; _polygons[i].vertices[j].y = 0.0f; } @@ -50,7 +69,250 @@ void Obstacles::clear() { _count = 0; } -void Obstacles::add(float x0, float z0, float x1, float z1) { +#define IN_RANGE(v, start, end) ((start) <= (v) && (v) <= (end)) + +/* + * This function is limited to finding intersections between + * horizontal and vertical lines! + * + * The original implementation is more general but obstacle + * polygons only consists of horizontal and vertical lines, + * and this is more numerically stable. + */ +bool Obstacles::lineLineIntersection(LineSegment a, LineSegment b, Vector2 *intersection) { + assert(a.start.x == a.end.x || a.start.y == a.end.y); + assert(b.start.x == b.end.x || b.start.y == b.end.y); + + if (a.start.x > a.end.x) SWAP(a.start.x, a.end.x); + if (a.start.y > a.end.y) SWAP(a.start.y, a.end.y); + if (b.start.x > b.end.x) SWAP(b.start.x, b.end.x); + if (b.start.y > b.end.y) SWAP(b.start.y, b.end.y); + + if (a.start.x == a.end.x && b.start.y == b.end.y && IN_RANGE(a.start.x, b.start.x, b.end.x) && IN_RANGE(b.start.y, a.start.y, a.end.y)) { + // A is vertical, B is horizontal + *intersection = Vector2(a.start.x, b.start.y); + return true; + } + + if (a.start.y == a.end.y && b.start.x == b.end.x && IN_RANGE(a.start.y, b.start.y, b.end.y) && IN_RANGE(b.start.x, a.start.x, a.end.x)) { + // A is horizontal, B is vertical + *intersection = Vector2(b.start.x, a.start.y); + return true; + } + + return false; +} + +bool Obstacles::linePolygonIntersection(LineSegment lineA, VertexType lineAType, Polygon *polyB, Vector2 *intersectionPoint, int *intersectionIndex) { + bool hasIntersection = false; + float nearestIntersectionDistance = 0.0f; + + for (int i = 0; i != polyB->verticeCount; ++i) { + LineSegment lineB; + lineB.start = polyB->vertices[i]; + lineB.end = polyB->vertices[(i+1) % polyB->verticeCount]; + + VertexType lineBType = polyB->vertexType[i]; + + Vector2 newIntersectionPoint; + + if (lineLineIntersection(lineA, lineB, &newIntersectionPoint)) { + if ((lineAType == TOP_RIGHT && lineBType == TOP_LEFT) + || (lineAType == BOTTOM_RIGHT && lineBType == TOP_RIGHT) + || (lineAType == BOTTOM_LEFT && lineBType == BOTTOM_RIGHT) + || (lineAType == TOP_LEFT && lineBType == BOTTOM_LEFT) + ) { + if (!WITHIN_TOLERANCE(lineB.end.x, intersectionPoint->x) + || !WITHIN_TOLERANCE(lineB.end.y, intersectionPoint->y)) { + if (newIntersectionPoint != *intersectionPoint) { + float newIntersectionDistance = getLength(lineA.start.x, lineA.start.y, newIntersectionPoint.x, newIntersectionPoint.y); + if (!hasIntersection || newIntersectionDistance < nearestIntersectionDistance) { + hasIntersection = true; + nearestIntersectionDistance = newIntersectionDistance; + *intersectionPoint = newIntersectionPoint; + *intersectionIndex = i; + } + } + } + } + } + } + + return hasIntersection; +} + +/* + * Polygons vertices are defined in clock-wise order + * starting at the top-most, right-most corner. + * + * When merging two polygons, we start at the top-most, right-most vertex. + * The polygon with this vertex starts is the primary polygon. + * We follow the edges until we find an intersection with the secondary polygon, + * in which case we switch primary and secondary and continue following the new edges. + * + * Luckily the first two polygons added in RC01 (A, then B) are laid as as below, + * making an ideal test case. + * + * Merge order: (B0,B1) (B1,B2) (B2,J) (J,A2) (A2,A3) (A3,A0) (A0,I) (I,B0) + * + * 0,0 ---> x + * | + * | primary + * | B 0 ----- 1 + * | | | + * | A 0 --I-- 1 | + * | | | | | + * | | 3 --J-- 2 + * | | | + * | 3 ----- 2 + * | secondary + * v y + */ + +bool Obstacles::mergePolygons(Polygon &polyA, Polygon &polyB) { + bool flagDidMergePolygons = false; + Polygon polyMerged; + polyMerged.rect = merge(polyA.rect, polyB.rect); + + Polygon *polyPrimary, *polySecondary; + if (polyA.rect.y0 < polyB.rect.y0 || (polyA.rect.y0 == polyB.rect.y0 && polyA.rect.x0 < polyB.rect.x0)) { + polyPrimary = &polyA; + polySecondary = &polyB; + } else { + polyPrimary = &polyB; + polySecondary = &polyA; + } + + Vector2 intersectionPoint; + LineSegment polyLine; + bool flagAddVertexToVertexList = true; + bool flagDidFindIntersection = false; + int vertIndex = 0; + + Polygon *startingPolygon = polyPrimary; + int flagDone = false; + while (!flagDone) { + VertexType polyPrimaryType; + + polyLine.start = flagDidFindIntersection ? intersectionPoint : polyPrimary->vertices[vertIndex]; + polyLine.end = polyPrimary->vertices[(vertIndex + 1) % polyPrimary->verticeCount]; + + // TODO(madmoose): How does this work when adding a new intersection point? + polyPrimaryType = polyPrimary->vertexType[vertIndex]; + + if (flagAddVertexToVertexList) { + assert(polyMerged.verticeCount < kPolygonVertexCount); + polyMerged.vertices[polyMerged.verticeCount] = polyLine.start; + polyMerged.vertexType[polyMerged.verticeCount] = polyPrimaryType; + polyMerged.verticeCount++; + } + + flagAddVertexToVertexList = true; + int polySecondaryIntersectionIndex = -1; + + if (linePolygonIntersection(polyLine, polyPrimaryType, polySecondary, &intersectionPoint, &polySecondaryIntersectionIndex)) { + if (WITHIN_TOLERANCE(intersectionPoint.x, polyLine.start.x) && WITHIN_TOLERANCE(intersectionPoint.y, polyLine.start.y)) { + warning("Set: %d Scene: %d", _vm->_scene->getSetId(), _vm->_scene->getSceneId()); + assert(0 && "Report instances of this to madmoose!"); + flagAddVertexToVertexList = false; + polyMerged.verticeCount--; // TODO(madmoose): How would this work? + } else { + // Obstacles::nop + } + vertIndex = polySecondaryIntersectionIndex; + flagDidFindIntersection = true; + + SWAP(polyPrimary, polySecondary); + + flagDidMergePolygons = true; + } else { + vertIndex = (vertIndex + 1) % polyPrimary->verticeCount; + flagDidFindIntersection = false; + } + if (polyPrimary->vertices[vertIndex] == startingPolygon->vertices[0]) { + flagDone = true; + } + } + + if (flagDidMergePolygons) { + *startingPolygon = polyMerged; + startingPolygon->isPresent = true; + if (startingPolygon == &polyA) { + polyB.isPresent = false; + } else { + polyA.isPresent = false; + } + } + + return flagDidMergePolygons; +} + +void Obstacles::add(Rect rect) { + int polygonIndex = findEmptyPolygon(); + if (polygonIndex < 0) { + return; + } + + rect.expand(12.0f); + rect.trunc_2_decimals(); + + Polygon &poly = _polygons[polygonIndex]; + + poly.rect = rect; + + poly.vertices[0] = Vector2(rect.x0, rect.y0); + poly.vertexType[0] = TOP_LEFT; + + poly.vertices[1] = Vector2(rect.x1, rect.y0); + poly.vertexType[1] = TOP_RIGHT; + + poly.vertices[2] = Vector2(rect.x1, rect.y1); + poly.vertexType[2] = BOTTOM_RIGHT; + + poly.vertices[3] = Vector2(rect.x0, rect.y1); + poly.vertexType[3] = BOTTOM_LEFT; + + poly.isPresent = true; + poly.verticeCount = 4; + +restart: + for (int i = 0; i < kPolygonCount; ++i) { + Polygon &polyA = _polygons[i]; + if (!polyA.isPresent) { + continue; + } + + for (int j = i+1; j < kPolygonCount; ++j) { + Polygon &polyB = _polygons[j]; + if (!polyB.isPresent) { + continue; + } + + if (!overlaps(polyA.rect, polyB.rect)) { + continue; + } + + if (mergePolygons(polyA, polyB)) { + goto restart; + } + } + } +} + +int Obstacles::findEmptyPolygon() const { + for (int i = 0; i < kPolygonCount; i++) { + if (!_polygons[i].isPresent) { + return i; + } + } + return -1; +} + +float Obstacles::getLength(float x0, float z0, float x1, float z1) { + if (x0 == x1) { + return fabs(z1 - z0); + } + return fabs(x1 - x0); } bool Obstacles::find(const Vector3 &from, const Vector3 &to, Vector3 *next) const { @@ -59,10 +321,242 @@ bool Obstacles::find(const Vector3 &from, const Vector3 &to, Vector3 *next) cons return true; } +bool Obstacles::findIntersectionNearest(int polygonIndex, Vector2 from, Vector2 to, + int *outVertexIndex, float *outDistance, Vector2 *out) const +{ + float minDistance = 0.0f; + Vector2 minIntersection; + int minVertexIndex = -1; + + bool hasDistance = false; + + for (int i = 0; i < _polygons[polygonIndex].verticeCount; ++i) { + int nextIndex = (i + 1) % _polygons[polygonIndex].verticeCount; + Vector2 *vertices = _polygons[polygonIndex].vertices; + Vector2 intersection; + bool intersects = lineIntersection(from, to, vertices[i], vertices[nextIndex], &intersection); + if (intersects) { + float distance2 = distance(from, intersection); + if (!hasDistance || distance2 < minDistance) { + minDistance = distance2; + minIntersection = intersection; + minVertexIndex = i; + hasDistance = true; + } + } + } + + *outDistance = minDistance; + *outVertexIndex = minVertexIndex; + *out = minIntersection; + + return minVertexIndex != -1; +} + +bool Obstacles::findIntersectionFarthest(int polygonIndex, Vector2 from, Vector2 to, + int *outVertexIndex, float *outDistance, Vector2 *out) const +{ + float maxDistance = 0.0f; + Vector2 maxIntersection; + int maxVertexIndex = -1; + + bool hasDistance = false; + + for (int i = 0; i < _polygons[polygonIndex].verticeCount; ++i) { + int nextIndex = (i + 1) % _polygons[polygonIndex].verticeCount; + Vector2 *vertices = _polygons[polygonIndex].vertices; + Vector2 intersection; + bool intersects = lineIntersection(from, to, vertices[i], vertices[nextIndex], &intersection); + if (intersects) { + float distance2 = distance(from, intersection); + if (!hasDistance || distance2 > maxDistance) { + maxDistance = distance2; + maxIntersection = intersection; + maxVertexIndex = i; + hasDistance = true; + } + } + } + + *outDistance = maxDistance; + *outVertexIndex = maxVertexIndex; + *out = maxIntersection; + + return maxVertexIndex != -1; +} + +bool Obstacles::findPolygonVerticeByXZ(int *polygonIndex, int *verticeIndex, int *verticeCount, float x, float z) const { + *polygonIndex = -1; + *verticeIndex = -1; + *verticeCount = -1; + + for (int i = 0; i != kPolygonCount; ++i) { + if (!_polygons[i].isPresent || _polygons[i].verticeCount == 0) { + continue; + } + + for (int j = 0; j != kPolygonVertexCount; ++j) { + if (_polygons[i].vertices[j].x == x && _polygons[i].vertices[j].y == z) { + *polygonIndex = i; + *verticeIndex = j; + *verticeCount = _polygons[i].verticeCount; + return true; + } + } + } + + return false; +} + +bool Obstacles::findPolygonVerticeByXZWithinTolerance(float x, float z, int *polygonIndex, int *verticeIndex) const { + *polygonIndex = -1; + *verticeIndex = -1; + + for (int i = 0; i != kPolygonCount; ++i) { + if (!_polygons[i].isPresent || _polygons[i].verticeCount == 0) { + continue; + } + + for (int j = 0; j != kPolygonVertexCount; ++j) { + if (WITHIN_TOLERANCE(_polygons[i].vertices[j].x, x)) { + if (WITHIN_TOLERANCE(_polygons[i].vertices[j].y, z)) { + *polygonIndex = i; + *verticeIndex = j; + return true; + } + } + } + } + + return false; +} + +void Obstacles::clearVertices() { + _verticeCount = 0; +} + +void Obstacles::copyVerticesReverse() { + +} + +void Obstacles::copyVertices() { + +} + void Obstacles::backup() { + for (int i = 0; i != kPolygonCount; ++i) { + _polygonsBackup[i].isPresent = false; + } + + int count = 0; + for (int i = 0; i != kPolygonCount; ++i) { + if (_polygons[i].isPresent) { + _polygonsBackup[count] = _polygons[i]; + ++count; + } + } + + for (int i = 0; i != kPolygonCount; ++i) { + _polygons[i] = _polygonsBackup[count]; + } + + _count = count; + _backup = true; +} + +void Obstacles::restore() { + for (int i = 0; i != kPolygonCount; ++i) { + _polygons[i].isPresent = false; + } + for (int i = 0; i != kPolygonCount; ++i) { + _polygons[i] = _polygonsBackup[i]; + } } -void Obstacles::restore() {} +void Obstacles::save(SaveFileWriteStream &f) { + f.writeBool(_backup); + f.writeInt(_count); + for (int i = 0; i < _count; ++i) { + Polygon &p = _polygonsBackup[i]; + f.writeBool(p.isPresent); + f.writeInt(p.verticeCount); + f.writeFloat(p.rect.x0); + f.writeFloat(p.rect.y0); + f.writeFloat(p.rect.x1); + f.writeFloat(p.rect.y1); + for (int j = 0; j < kPolygonVertexCount; ++j) { + f.writeVector2(p.vertices[j]); + } + for (int j = 0; j < kPolygonVertexCount; ++j) { + f.writeInt(p.vertexType[j]); + } + } + for (int i = 0; i < kVertexCount; ++i) { + f.writeVector2(_vertices[i]); + } + f.writeInt(_verticeCount); +} +void Obstacles::load(SaveFileReadStream &f) { + for (int i = 0; i < kPolygonCount; ++i) { + _polygons[i].isPresent = false; + _polygons[i].verticeCount = 0; + _polygonsBackup[i].isPresent = false; + _polygonsBackup[i].verticeCount = 0; + } + + _backup = f.readBool(); + _count = f.readInt(); + for (int i = 0; i < _count; ++i) { + Polygon &p = _polygonsBackup[i]; + p.isPresent = f.readBool(); + p.verticeCount = f.readInt(); + p.rect.x0 = f.readFloat(); + p.rect.y0 = f.readFloat(); + p.rect.x1 = f.readFloat(); + p.rect.y1 = f.readFloat(); + for (int j = 0; j < kPolygonVertexCount; ++j) { + p.vertices[j] = f.readVector2(); + } + for (int j = 0; j < kPolygonVertexCount; ++j) { + p.vertexType[j] = (VertexType)f.readInt(); + } + } + + for (int i = 0; i < kPolygonCount; ++i) { + _polygons[i] = _polygonsBackup[i]; + } + + for (int i = 0; i < kVertexCount; ++i) { + _vertices[i] = f.readVector2(); + } + _verticeCount = f.readInt(); +} + +void Obstacles::draw() { + for (int i = 0; i != kPolygonCount; ++i) { + if (!_polygons[i].isPresent) { + continue; + } + + Vector3 p0 = _vm->_view->calculateScreenPosition(Vector3( + _polygons[i].vertices[_polygons[i].verticeCount - 1].x, + 0, + _polygons[i].vertices[_polygons[i].verticeCount - 1].y + )); + + for (int j = 0; j != _polygons[i].verticeCount; ++j) { + Vector3 p1 = _vm->_view->calculateScreenPosition(Vector3( + _polygons[i].vertices[j].x, + 0.0f, + _polygons[i].vertices[j].y + )); + + _vm->_surfaceFront.drawLine(p0.x, p0.y, p1.x, p1.y, 0x7FE0); + + p0 = p1; + } + } +} } // End of namespace BladeRunner diff --git a/engines/bladerunner/obstacles.h b/engines/bladerunner/obstacles.h index c2c84c3bfa..25124904ef 100644 --- a/engines/bladerunner/obstacles.h +++ b/engines/bladerunner/obstacles.h @@ -23,45 +23,87 @@ #ifndef BLADERUNNER_OBSTACLES_H #define BLADERUNNER_OBSTACLES_H +#include "bladerunner/rect.h" #include "bladerunner/vector.h" namespace BladeRunner { class BladeRunnerEngine; +class SaveFileReadStream; +class SaveFileWriteStream; class Obstacles { - static const int kPolygonCount = 50; - static const int kVertexCount = 160; + static const int kVertexCount = 150; + static const int kPolygonCount = 50; + static const int kPolygonVertexCount = 160; + + enum VertexType { + BOTTOM_LEFT, + TOP_LEFT, + TOP_RIGHT, + BOTTOM_RIGHT + }; + + struct LineSegment { + Vector2 start; + Vector2 end; + }; struct Polygon { - bool isPresent; - int verticeCount; - float left; - float bottom; - float right; - float top; - Vector2 vertices[kVertexCount]; - int vertexType[kVertexCount]; + bool isPresent; + int verticeCount; + Rect rect; + Vector2 vertices[kPolygonVertexCount]; + VertexType vertexType[kPolygonVertexCount]; + + Polygon() : isPresent(false), verticeCount(0) + {} }; BladeRunnerEngine *_vm; - Polygon _polygons[kPolygonCount]; - Polygon _polygonsBackup[kPolygonCount]; + Polygon *_polygons; + Polygon *_polygonsBackup; Vector2 *_vertices; int _verticeCount; int _count; bool _backup; + static bool lineLineIntersection(LineSegment a, LineSegment b, Vector2 *intersectionPoint); + static bool linePolygonIntersection(LineSegment lineA, VertexType lineAType, Polygon *polyB, Vector2 *intersectionPoint, int *intersectionIndex); + + bool mergePolygons(Polygon &polyA, Polygon &PolyB); + public: Obstacles(BladeRunnerEngine *vm); ~Obstacles(); void clear(); - void add(float x0, float z0, float x1, float z1); + void add(Rect rect); + void add(float x0, float z0, float x1, float z1) { add(Rect(x0, z0, x1, z1)); } + int findEmptyPolygon() const; + static float getLength(float x0, float z0, float x1, float z1); bool find(const Vector3 &from, const Vector3 &to, Vector3 *next) const; + + bool findIntersectionNearest(int polygonIndex, Vector2 from, Vector2 to, + int *outVertexIndex, float *outDistance, Vector2 *out) const; + bool findIntersectionFarthest(int polygonIndex, Vector2 from, Vector2 to, + int *outVertexIndex, float *outDistance, Vector2 *out) const; + + bool findPolygonVerticeByXZ(int *polygonIndex, int *verticeIndex, int *verticeCount, float x, float z) const; + bool findPolygonVerticeByXZWithinTolerance(float x, float z, int *polygonIndex, int *verticeIndex) const; + + void clearVertices(); + void copyVerticesReverse(); + void copyVertices(); + void backup(); void restore(); + void reset(); + + void draw(); + void save(SaveFileWriteStream &f); + void load(SaveFileReadStream &f); }; } // End of namespace BladeRunner diff --git a/engines/bladerunner/overlays.cpp b/engines/bladerunner/overlays.cpp index b5cb130678..65ba83f15d 100644 --- a/engines/bladerunner/overlays.cpp +++ b/engines/bladerunner/overlays.cpp @@ -25,6 +25,7 @@ #include "bladerunner/bladerunner.h" #include "bladerunner/archive.h" +#include "bladerunner/savefile.h" #include "bladerunner/vqa_player.h" #include "graphics/surface.h" @@ -56,19 +57,22 @@ Overlays::~Overlays() { } int Overlays::play(const Common::String &name, int loopId, bool loopForever, bool startNow, int a6) { - int id = mix_id(name); - int index = findById(id); + assert(name.size() <= 12); + + int32 hash = MIXArchive::getHash(name); + int index = findByHash(hash); if (index < 0) { index = findEmpty(); if (index < 0) { return index; } - _videos[index].id = id; + _videos[index].loaded = true; + _videos[index].name = name; + _videos[index].hash = hash; _videos[index].vqaPlayer = new VQAPlayer(_vm, &_vm->_surfaceFront); // repeat forever _videos[index].vqaPlayer->setBeginAndEndFrame(0, 0, -1, kLoopSetModeJustStart, nullptr, nullptr); - _videos[index].loaded = true; } Common::String resourceName = Common::String::format("%s.VQA", name.c_str()); @@ -83,8 +87,7 @@ int Overlays::play(const Common::String &name, int loopId, bool loopForever, boo } void Overlays::remove(const Common::String &name) { - int id = mix_id(name); - int index = findById(id); + int index = findByHash(MIXArchive::getHash(name)); if (index >= 0) { resetSingle(index); } @@ -109,9 +112,9 @@ void Overlays::tick() { } } -int Overlays::findById(int32 id) const { +int Overlays::findByHash(int32 hash) const { for (int i = 0; i < kOverlayVideos; ++i) { - if (_videos[i].loaded && _videos[i].id == id) { + if (_videos[i].loaded && _videos[i].hash == hash) { return i; } } @@ -134,12 +137,44 @@ void Overlays::resetSingle(int i) { _videos[i].vqaPlayer = nullptr; } _videos[i].loaded = false; - _videos[i].id = 0; + _videos[i].hash = 0; _videos[i].field2 = -1; + _videos[i].name.clear(); } void Overlays::reset() { _videos.clear(); } +void Overlays::save(SaveFileWriteStream &f) { + for (int i = 0; i < kOverlayVideos; ++i) { + // 37 bytes per overlay + Video &ov = _videos[i]; + + f.writeBool(ov.loaded); + f.writeInt(0); // vqaPlayer pointer + f.writeStringSz(ov.name, 13); + f.writeSint32LE(ov.hash); + f.writeInt(ov.field0); + f.writeInt(ov.field1); + f.writeInt(ov.field2); + } +} + +void Overlays::load(SaveFileReadStream &f) { + for (int i = 0; i < kOverlayVideos; ++i) { + // 37 bytes per overlay + Video &ov = _videos[i]; + + ov.loaded = f.readBool(); + f.skip(4); // vqaPlayer pointer + ov.vqaPlayer = nullptr; + ov.name = f.readStringSz(13); + ov.hash = f.readSint32LE(); + ov.field0 = f.readInt(); + ov.field1 = f.readInt(); + ov.field2 = f.readInt(); + } +} + } // End of namespace BladeRunner diff --git a/engines/bladerunner/overlays.h b/engines/bladerunner/overlays.h index 38edf7459b..405acbc264 100644 --- a/engines/bladerunner/overlays.h +++ b/engines/bladerunner/overlays.h @@ -33,20 +33,21 @@ struct Surface; namespace BladeRunner { class BladeRunnerEngine; +class SaveFileReadStream; +class SaveFileWriteStream; class VQAPlayer; - class Overlays { static const int kOverlayVideos = 5; struct Video { - bool loaded; - VQAPlayer *vqaPlayer; - // char name[13]; - int32 id; - int field0; - int field1; - int field2; + bool loaded; + VQAPlayer *vqaPlayer; + Common::String name; + int32 hash; + int field0; + int field1; + int field2; }; BladeRunnerEngine *_vm; @@ -62,8 +63,11 @@ public: void removeAll(); void tick(); + void save(SaveFileWriteStream &f); + void load(SaveFileReadStream &f); + private: - int findById(int32 id) const; + int findByHash(int32 hash) const; int findEmpty() const; void resetSingle(int i); diff --git a/engines/bladerunner/rect.h b/engines/bladerunner/rect.h new file mode 100644 index 0000000000..da01af389d --- /dev/null +++ b/engines/bladerunner/rect.h @@ -0,0 +1,75 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 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 BLADERUNNER_RECT_H +#define BLADERUNNER_RECT_H + +#include "common/debug.h" +#include "common/types.h" +#include "common/util.h" + +namespace BladeRunner { + +struct Rect { + float x0; + float y0; + float x1; + float y1; + + Rect() + : x0(0.0f), y0(0.0f), x1(0.0f), y1(0.0f) + {} + Rect(float x0, float y0, float x1, float y1) + : x0(x0), y0(y0), x1(x1), y1(y1) + {} + + void expand(float d) { + x0 -= d; + y0 -= d; + x1 += d; + y1 += d; + } + + void trunc_2_decimals() { + x0 = truncf(x0 * 100.0f) / 100.0f; + y0 = truncf(y0 * 100.0f) / 100.0f; + x1 = truncf(x1 * 100.0f) / 100.0f; + y1 = truncf(y1 * 100.0f) / 100.0f; + } +}; + +inline bool overlaps(const Rect &a, const Rect &b) { + return !(a.y1 < b.y0 || a.y0 > b.y1 || a.x0 > b.x1 || a.x1 < b.x0); +} + +inline Rect merge(const Rect &a, const Rect &b) { + Rect c; + c.x0 = MIN(a.x0, b.x0); + c.y0 = MIN(a.y0, b.y0); + c.x1 = MAX(a.x1, b.x1); + c.y1 = MAX(a.y1, b.y1); + return c; +} + +} // End of namespace BladeRunner + +#endif diff --git a/engines/bladerunner/regions.cpp b/engines/bladerunner/regions.cpp index 80dabf2989..f74186240b 100644 --- a/engines/bladerunner/regions.cpp +++ b/engines/bladerunner/regions.cpp @@ -22,6 +22,8 @@ #include "bladerunner/regions.h" +#include "bladerunner/savefile.h" + namespace BladeRunner { Regions::Regions() { @@ -99,4 +101,22 @@ void Regions::enable() { _enabled = true; } +void Regions::save(SaveFileWriteStream &f) { + f.writeBool(_enabled); + for (int i = 0; i != 10; ++i) { + f.writeRect(_regions[i].rectangle); + f.writeInt(_regions[i].type); + f.writeInt(_regions[i].present); + } +} + +void Regions::load(SaveFileReadStream &f) { + _enabled = f.readBool(); + for (int i = 0; i != 10; ++i) { + _regions[i].rectangle = f.readRect(); + _regions[i].type = f.readInt(); + _regions[i].present = f.readInt(); + } +} + } // End of namespace BladeRunner diff --git a/engines/bladerunner/regions.h b/engines/bladerunner/regions.h index 9868f46ac0..aae3627650 100644 --- a/engines/bladerunner/regions.h +++ b/engines/bladerunner/regions.h @@ -30,6 +30,9 @@ namespace BladeRunner { +class SaveFileReadStream; +class SaveFileWriteStream; + class Regions { friend class Debugger; @@ -54,6 +57,9 @@ public: void setEnabled(bool enabled); void enable(); + + void save(SaveFileWriteStream &f); + void load(SaveFileReadStream &f); }; } // End of namespace BladeRunner diff --git a/engines/bladerunner/savefile.cpp b/engines/bladerunner/savefile.cpp new file mode 100644 index 0000000000..3528a6bb82 --- /dev/null +++ b/engines/bladerunner/savefile.cpp @@ -0,0 +1,166 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 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 "bladerunner/savefile.h" + +#include "bladerunner/boundingbox.h" +#include "bladerunner/vector.h" + +#include "common/rect.h" +#include "common/savefile.h" + +namespace BladeRunner { + +SaveFileWriteStream::SaveFileWriteStream() + : MemoryWriteStreamDynamic(DisposeAfterUse::YES) { +} + +void SaveFileWriteStream::debug(char *p) { + write(p, strlen(p) + 1); +} + +void SaveFileWriteStream::padBytes(int count) { + for (int i = 0; i < count; ++i) { + writeByte(0); + } +} + +void SaveFileWriteStream::writeInt(int v) { + writeUint32LE(v); +} + +void SaveFileWriteStream::writeFloat(int v) { + writeFloatLE(v); +} + +void SaveFileWriteStream::writeBool(bool v) { + writeUint32LE(v); +} + +void SaveFileWriteStream::writeStringSz(const Common::String &s, int sz) { + assert(s.size() < (uint)sz); + write(s.begin(), s.size()); + padBytes((uint)sz - s.size()); +} + +void SaveFileWriteStream::writeVector2(const Vector2 &v) { + writeFloatLE(v.x); + writeFloatLE(v.y); +} + +void SaveFileWriteStream::writeVector3(const Vector3 &v) { + writeFloatLE(v.x); + writeFloatLE(v.y); + writeFloatLE(v.z); +} + +void SaveFileWriteStream::writeRect(const Common::Rect &v) { + writeUint32LE(v.left); + writeUint32LE(v.top); + writeUint32LE(v.right); + writeUint32LE(v.bottom); +} + +void SaveFileWriteStream::writeBoundingBox(const BoundingBox &v) { + float x0, y0, z0, x1, y1, z1; + + v.getXYZ(&x0, &y0, &z0, &x1, &y1, &z1); + writeFloatLE(x0); + writeFloatLE(y0); + writeFloatLE(z0); + writeFloatLE(x1); + writeFloatLE(y1); + writeFloatLE(z1); + + // Bounding boxes have a lot of extra data that's never actually used + for (int i = 0; i != 96; ++i) { + writeFloatLE(0.0f); + } +} + +SaveFileReadStream::SaveFileReadStream(const byte *dataPtr, uint32 dataSize) + : MemoryReadStream(dataPtr, dataSize, DisposeAfterUse::YES) { +} + +int SaveFileReadStream::readInt() { + return readUint32LE(); +} + +float SaveFileReadStream::readFloat() { + return readFloatLE(); +} + +bool SaveFileReadStream::readBool() { + return readUint32LE(); +} + +Common::String SaveFileReadStream::readStringSz(int sz) { + char *buf = (char *)malloc(sz); + read(buf, sz); + Common::String result = buf; + free(buf); + return result; +} + +Vector2 SaveFileReadStream::readVector2() { + Vector2 result; + result.x = readFloatLE(); + result.y = readFloatLE(); + return result; +} + +Vector3 SaveFileReadStream::readVector3() { + Vector3 result; + result.x = readFloatLE(); + result.y = readFloatLE(); + result.z = readFloatLE(); + return result; +} + +Common::Rect SaveFileReadStream::readRect() { + Common::Rect result; + result.left = readUint32LE(); + result.top = readUint32LE(); + result.right = readUint32LE(); + result.bottom = readUint32LE(); + return result; +} + +BoundingBox SaveFileReadStream::readBoundingBox() { + float x0, y0, z0, x1, y1, z1; + + x0 = readFloatLE(); + y0 = readFloatLE(); + z0 = readFloatLE(); + x1 = readFloatLE(); + y1 = readFloatLE(); + z1 = readFloatLE(); + + // Bounding boxes have a lot of extra data that's never actually used + skip(384); + + return BoundingBox(x0, y0, z0, x1, y1, z1); +} + + + +} // End of namespace BladeRunner diff --git a/engines/bladerunner/savefile.h b/engines/bladerunner/savefile.h new file mode 100644 index 0000000000..4dfdb20bd4 --- /dev/null +++ b/engines/bladerunner/savefile.h @@ -0,0 +1,75 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 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 BLADERUNNER_SAVEFILE_H +#define BLADERUNNER_SAVEFILE_H + +#include "common/memstream.h" +#include "common/types.h" + +namespace Common { +class OutSaveFile; +class String; +struct Rect; +} + +namespace BladeRunner { + +class Vector2; +class Vector3; +class BoundingBox; + +class SaveFileWriteStream : public Common::MemoryWriteStreamDynamic { +public: + SaveFileWriteStream(); + + void debug(char *p); + + void padBytes(int count); + + void writeInt(int v); + void writeFloat(int v); + void writeBool(bool v); + void writeStringSz(const Common::String &s, int sz); + void writeVector2(const Vector2 &v); + void writeVector3(const Vector3 &v); + void writeRect(const Common::Rect &v); + void writeBoundingBox(const BoundingBox &v); +}; + +class SaveFileReadStream : public Common::MemoryReadStream { +public: + SaveFileReadStream(const byte *dataPtr, uint32 dataSize); + + int readInt(); + float readFloat(); + bool readBool(); + Common::String readStringSz(int sz); + Vector2 readVector2(); + Vector3 readVector3(); + Common::Rect readRect(); + BoundingBox readBoundingBox(); +}; + +} // End of namespace BladeRunner + +#endif diff --git a/engines/bladerunner/scene.cpp b/engines/bladerunner/scene.cpp index 123134ed96..fe8dbc7b61 100644 --- a/engines/bladerunner/scene.cpp +++ b/engines/bladerunner/scene.cpp @@ -30,11 +30,13 @@ #include "bladerunner/items.h" #include "bladerunner/overlays.h" #include "bladerunner/regions.h" +#include "bladerunner/savefile.h" #include "bladerunner/scene_objects.h" #include "bladerunner/screen_effects.h" #include "bladerunner/set.h" #include "bladerunner/settings.h" #include "bladerunner/slice_renderer.h" +#include "bladerunner/script/police_maze.h" #include "bladerunner/script/scene_script.h" #include "bladerunner/ui/spinner.h" #include "bladerunner/vqa_player.h" @@ -187,7 +189,8 @@ bool Scene::close(bool isLoadingGame) { return true; } - //_vm->_policeMaze->clear(!isLoadingGame); + _vm->_policeMaze->clear(!isLoadingGame); + if (isLoadingGame) { _vm->_sceneScript->playerWalkedOut(); } @@ -328,7 +331,7 @@ void Scene::loopStartSpecial(int specialLoopMode, int loopId, bool immediately) } } -int Scene::findObject(const char *objectName) { +int Scene::findObject(const Common::String &objectName) { return _set->findObject(objectName); } @@ -374,7 +377,7 @@ void Scene::objectSetIsTarget(int objectId, bool isTarget, bool sceneLoaded) { } } -const char *Scene::objectGetName(int objectId) { +const Common::String &Scene::objectGetName(int objectId) { return _set->objectGetName(objectId); } @@ -410,4 +413,37 @@ void Scene::loopEnded(int frame, int loopId) { void Scene::loopEndedStatic(void *data, int frame, int loopId) { ((Scene *)data)->loopEnded(frame, loopId); } + +void Scene::save(SaveFileWriteStream &f) { + f.writeInt(_setId); + f.writeInt(_sceneId); + f.writeInt(_defaultLoop); + f.writeBool(_defaultLoopSet); + f.writeBool(_defaultLoopPreloadedSet); + f.writeInt(_specialLoopMode); + f.writeInt(_specialLoop); + f.writeInt(_nextSetId); + f.writeInt(_nextSceneId); + f.writeInt(_frame); + f.writeVector3(_actorStartPosition); + f.writeInt(_actorStartFacing); + f.writeBool(_playerWalkedIn); +} + +void Scene::load(SaveFileReadStream &f) { + _setId = f.readInt(); + _sceneId = f.readInt(); + _defaultLoop = f.readInt(); + _defaultLoopSet = f.readBool(); + _defaultLoopPreloadedSet = f.readBool(); + _specialLoopMode = f.readInt(); + _specialLoop = f.readInt(); + _nextSetId = f.readInt(); + _nextSceneId = f.readInt(); + _frame = f.readInt(); + _actorStartPosition = f.readVector3(); + _actorStartFacing = f.readInt(); + _playerWalkedIn = f.readBool(); +} + } // End of namespace BladeRunner diff --git a/engines/bladerunner/scene.h b/engines/bladerunner/scene.h index 91cd2ed604..0403cd331e 100644 --- a/engines/bladerunner/scene.h +++ b/engines/bladerunner/scene.h @@ -25,11 +25,15 @@ #include "bladerunner/vector.h" +#include "common/str.h" + namespace BladeRunner { class BladeRunnerEngine; class BoundingBox; class Regions; +class SaveFileReadStream; +class SaveFileWriteStream; class Set; class VQAPlayer; @@ -80,14 +84,17 @@ public: bool didPlayerWalkIn() { bool r = _playerWalkedIn; _playerWalkedIn = false; return r; } - int findObject(const char *objectName); + int findObject(const Common::String &objectName); bool objectSetHotMouse(int objectId); bool objectGetBoundingBox(int objectId, BoundingBox *boundingBox); void objectSetIsClickable(int objectId, bool isClickable, bool sceneLoaded); void objectSetIsObstacle(int objectId, bool isObstacle, bool sceneLoaded, bool updateWalkpath); void objectSetIsObstacleAll(bool isObstacle, bool sceneLoaded); void objectSetIsTarget(int objectId, bool isTarget, bool sceneLoaded); - const char *objectGetName(int objectId); + const Common::String &objectGetName(int objectId); + + void save(SaveFileWriteStream &f); + void load(SaveFileReadStream &f); private: void loopEnded(int frame, int loopId); diff --git a/engines/bladerunner/scene_objects.cpp b/engines/bladerunner/scene_objects.cpp index 87320a3fa4..30802a8d64 100644 --- a/engines/bladerunner/scene_objects.cpp +++ b/engines/bladerunner/scene_objects.cpp @@ -25,6 +25,7 @@ #include "bladerunner/bladerunner.h" #include "bladerunner/obstacles.h" +#include "bladerunner/savefile.h" #include "bladerunner/view.h" namespace BladeRunner { @@ -35,9 +36,7 @@ SceneObjects::SceneObjects(BladeRunnerEngine *vm, View *view) { _count = 0; - for (int i = 0; i < kSceneObjectCount; ++i) { - _sceneObjectsSortedByDistance[i] = -1; - } + clear(); } SceneObjects::~SceneObjects() { @@ -64,16 +63,16 @@ void SceneObjects::clear() { _count = 0; } -bool SceneObjects::addActor(int sceneObjectId, BoundingBox *boundingBox, Common::Rect *screenRectangle, bool isClickable, bool isMoving, bool isTarget, bool isRetired) { +bool SceneObjects::addActor(int sceneObjectId, const BoundingBox &boundingBox, const Common::Rect &screenRectangle, bool isClickable, bool isMoving, bool isTarget, bool isRetired) { return addSceneObject(sceneObjectId, kSceneObjectTypeActor, boundingBox, screenRectangle, isClickable, false, 0, isTarget, isMoving, isRetired); } -bool SceneObjects::addObject(int sceneObjectId, BoundingBox *boundingBox, bool isClickable, bool isObstacle, uint8 unknown1, bool isTarget) { +bool SceneObjects::addObject(int sceneObjectId, const BoundingBox &boundingBox, bool isClickable, bool isObstacle, uint8 unknown1, bool isTarget) { Common::Rect rect(-1, -1, -1, -1); - return addSceneObject(sceneObjectId, kSceneObjectTypeObject, boundingBox, &rect, isClickable, isObstacle, unknown1, isTarget, false, false); + return addSceneObject(sceneObjectId, kSceneObjectTypeObject, boundingBox, rect, isClickable, isObstacle, unknown1, isTarget, false, false); } -bool SceneObjects::addItem(int sceneObjectId, BoundingBox *boundingBox, Common::Rect *screenRectangle, bool isTarget, bool isObstacle) { +bool SceneObjects::addItem(int sceneObjectId, const BoundingBox &boundingBox, const Common::Rect &screenRectangle, bool isTarget, bool isObstacle) { return addSceneObject(sceneObjectId, kSceneObjectTypeItem, boundingBox, screenRectangle, isObstacle, 0, 0, isTarget, 0, 0); } @@ -110,7 +109,7 @@ int SceneObjects::findByXYZ(bool *isClickable, bool *isObstacle, bool *isTarget, if ((findClickables && sceneObject->isClickable) || (findObstacles && sceneObject->isObstacle) || (findTargets && sceneObject->isTarget)) { - BoundingBox boundingBox = *sceneObject->boundingBox; + BoundingBox boundingBox = sceneObject->boundingBox; if (sceneObject->type == kSceneObjectTypeActor) { boundingBox.expand(-4.0, 0.0, -4.0, 4.0, 0.0, 4.0); @@ -155,7 +154,7 @@ bool SceneObjects::existsOnXZ(int exceptSceneObjectId, float x, float z, bool mo if (isObstacle && sceneObject->id != exceptSceneObjectId) { float x1, y1, z1, x2, y2, z2; - sceneObject->boundingBox->getXYZ(&x1, &y1, &z1, &x2, &y2, &z2); + sceneObject->boundingBox.getXYZ(&x1, &y1, &z1, &x2, &y2, &z2); if (z1 <= zMax && z2 >= zMin && x1 <= xMax && x2 >= xMin) { return true; } @@ -176,7 +175,7 @@ int SceneObjects::findById(int sceneObjectId) const { return -1; } -bool SceneObjects::addSceneObject(int sceneObjectId, SceneObjectType sceneObjectType, BoundingBox *boundingBox, Common::Rect *screenRectangle, bool isClickable, bool isObstacle, uint8 unknown1, bool isTarget, bool isMoving, bool isRetired) { +bool SceneObjects::addSceneObject(int sceneObjectId, SceneObjectType sceneObjectType, const BoundingBox &boundingBox, const Common::Rect &screenRectangle, bool isClickable, bool isObstacle, uint8 unknown1, bool isTarget, bool isMoving, bool isRetired) { int index = findEmpty(); if (index == -1) { return false; @@ -194,7 +193,7 @@ bool SceneObjects::addSceneObject(int sceneObjectId, SceneObjectType sceneObject _sceneObjects[index].isMoving = isMoving; _sceneObjects[index].isRetired = isRetired; - float centerZ = (_sceneObjects[index].boundingBox->getZ0() + _sceneObjects[index].boundingBox->getZ1()) / 2.0f; + float centerZ = (_sceneObjects[index].boundingBox.getZ0() + _sceneObjects[index].boundingBox.getZ1()) / 2.0f; float distanceToCamera = fabs(-centerZ - _view->_cameraPosition.y); // y<->z is intentional, not a bug _sceneObjects[index].distanceToCamera = distanceToCamera; @@ -246,7 +245,7 @@ bool SceneObjects::isBetween(float sourceX, float sourceZ, float targetX, float } float objectX1, objectY1, objectZ1, objectX2, objectY2, objectZ2; - _sceneObjects[i].boundingBox->getXYZ(&objectX1, &objectY1, &objectZ1, &objectX2, &objectY2, &objectZ2); + _sceneObjects[i].boundingBox.getXYZ(&objectX1, &objectY1, &objectZ1, &objectX2, &objectY2, &objectZ2); Vector2 intersection; return lineIntersection(Vector2(sourceX, sourceZ), Vector2(targetX, targetZ), Vector2(objectX1, objectZ1), Vector2(objectX2, objectZ1), &intersection) @@ -255,7 +254,7 @@ bool SceneObjects::isBetween(float sourceX, float sourceZ, float targetX, float || lineIntersection(Vector2(sourceX, sourceZ), Vector2(targetX, targetZ), Vector2(objectX1, objectZ2), Vector2(objectX1, objectZ1), &intersection); } -bool SceneObjects::isObstacleBetween(float sourceX, float sourceZ, float targetX, float targetZ, float altitude, int exceptSceneObjectId) const { +bool SceneObjects::isObstacleBetween(const Vector3 &source, const Vector3 &target, int exceptSceneObjectId) const { for (int i = 0; i < _count; ++i) { const SceneObject *sceneObject = &_sceneObjects[_sceneObjectsSortedByDistance[i]]; @@ -264,9 +263,9 @@ bool SceneObjects::isObstacleBetween(float sourceX, float sourceZ, float targetX } float objectX1, objectY1, objectZ1, objectX2, objectY2, objectZ2; - _sceneObjects[i].boundingBox->getXYZ(&objectX1, &objectY1, &objectZ1, &objectX2, &objectY2, &objectZ2); + sceneObject->boundingBox.getXYZ(&objectX1, &objectY1, &objectZ1, &objectX2, &objectY2, &objectZ2); - if (84.0f <= objectY1 - altitude || 72.0f >= objectY2 - altitude) { + if (84.0f <= objectY1 - source.y || 72.0f >= objectY2 - source.y) { continue; } @@ -279,10 +278,10 @@ bool SceneObjects::isObstacleBetween(float sourceX, float sourceZ, float targetX objectZ2 = objectZ2 - zAdjustement; Vector2 intersection; - if (lineIntersection(Vector2(sourceX, sourceZ), Vector2(targetX, targetZ), Vector2(objectX1, objectZ1), Vector2(objectX2, objectZ1), &intersection) - || lineIntersection(Vector2(sourceX, sourceZ), Vector2(targetX, targetZ), Vector2(objectX2, objectZ1), Vector2(objectX2, objectZ2), &intersection) - || lineIntersection(Vector2(sourceX, sourceZ), Vector2(targetX, targetZ), Vector2(objectX2, objectZ2), Vector2(objectX1, objectZ2), &intersection) - || lineIntersection(Vector2(sourceX, sourceZ), Vector2(targetX, targetZ), Vector2(objectX1, objectZ2), Vector2(objectX1, objectZ1), &intersection)) { + if (lineIntersection(Vector2(source.x, source.z), Vector2(target.x, target.z), Vector2(objectX1, objectZ1), Vector2(objectX2, objectZ1), &intersection) + || lineIntersection(Vector2(source.x, source.z), Vector2(target.x, target.z), Vector2(objectX2, objectZ1), Vector2(objectX2, objectZ2), &intersection) + || lineIntersection(Vector2(source.x, source.z), Vector2(target.x, target.z), Vector2(objectX2, objectZ2), Vector2(objectX1, objectZ2), &intersection) + || lineIntersection(Vector2(source.x, source.z), Vector2(target.x, target.z), Vector2(objectX1, objectZ2), Vector2(objectX1, objectZ1), &intersection)) { return true; } } @@ -320,11 +319,53 @@ void SceneObjects::updateObstacles() { const SceneObject *sceneObject = &_sceneObjects[index]; if (sceneObject->isObstacle) { float x0, y0, z0, x1, y1, z1; - sceneObject->boundingBox->getXYZ(&x0, &y0, &z0, &x1, &y1, &z1); + sceneObject->boundingBox.getXYZ(&x0, &y0, &z0, &x1, &y1, &z1); _vm->_obstacles->add(x0, z0, x1, z1); } } _vm->_obstacles->backup(); } +void SceneObjects::save(SaveFileWriteStream &f) { + f.writeInt(_count); + for (int i = 0; i < kSceneObjectCount; ++i) { + f.writeInt(_sceneObjects[i].id); + f.writeInt(_sceneObjects[i].type); + f.writeBoundingBox(_sceneObjects[i].boundingBox); + f.writeRect(_sceneObjects[i].screenRectangle); + f.writeFloat(_sceneObjects[i].distanceToCamera); + f.writeBool(_sceneObjects[i].isPresent); + f.writeBool(_sceneObjects[i].isClickable); + f.writeBool(_sceneObjects[i].isObstacle); + f.writeInt(_sceneObjects[i].unknown1); + f.writeBool(_sceneObjects[i].isTarget); + f.writeBool(_sceneObjects[i].isMoving); + f.writeBool(_sceneObjects[i].isRetired); + } + for (int i = 0; i < kSceneObjectCount; ++i) { + f.writeInt(_sceneObjectsSortedByDistance[i]); + } +} + +void SceneObjects::load(SaveFileReadStream &f) { + _count = f.readInt(); + for (int i = 0; i < kSceneObjectCount; ++i) { + _sceneObjects[i].id = f.readInt(); + _sceneObjects[i].type = (SceneObjectType)f.readInt(); + _sceneObjects[i].boundingBox = f.readBoundingBox(); + _sceneObjects[i].screenRectangle = f.readRect(); + _sceneObjects[i].distanceToCamera = f.readFloat(); + _sceneObjects[i].isPresent = f.readBool(); + _sceneObjects[i].isClickable = f.readBool(); + _sceneObjects[i].isObstacle = f.readBool(); + _sceneObjects[i].unknown1 = f.readInt(); + _sceneObjects[i].isTarget = f.readBool(); + _sceneObjects[i].isMoving = f.readBool(); + _sceneObjects[i].isRetired = f.readBool(); + } + for (int i = 0; i < kSceneObjectCount; ++i) { + _sceneObjectsSortedByDistance[i] = f.readInt(); + } +} + } // End of namespace BladeRunner diff --git a/engines/bladerunner/scene_objects.h b/engines/bladerunner/scene_objects.h index dbd61b6dc5..a6d552017c 100644 --- a/engines/bladerunner/scene_objects.h +++ b/engines/bladerunner/scene_objects.h @@ -30,6 +30,8 @@ namespace BladeRunner { class BladeRunnerEngine; +class SaveFileReadStream; +class SaveFileWriteStream; class View; enum SceneObjectType { @@ -45,18 +47,18 @@ class SceneObjects { static const int kSceneObjectCount = 115; struct SceneObject { - int id; - SceneObjectType type; - const BoundingBox *boundingBox; - const Common::Rect *screenRectangle; - float distanceToCamera; - bool isPresent; - bool isClickable; - bool isObstacle; - int unknown1; - bool isTarget; - bool isMoving; - bool isRetired; + int id; + SceneObjectType type; + BoundingBox boundingBox; + Common::Rect screenRectangle; + float distanceToCamera; + bool isPresent; + bool isClickable; + bool isObstacle; + int unknown1; + bool isTarget; + bool isMoving; + bool isRetired; }; BladeRunnerEngine *_vm; @@ -70,9 +72,9 @@ public: SceneObjects(BladeRunnerEngine *vm, View *view); ~SceneObjects(); - bool addActor(int sceneObjectId, BoundingBox *boundingBox, Common::Rect *screenRectangle, bool isClickable, bool isMoving, bool isTarget, bool isRetired); - bool addObject(int sceneObjectId, BoundingBox *boundingBox, bool isClickable, bool isObstacle, uint8 unknown1, bool isTarget); - bool addItem(int sceneObjectId, BoundingBox *boundingBox, Common::Rect *screenRectangle, bool isTarget, bool isObstacle); + bool addActor(int sceneObjectId, const BoundingBox &boundingBox, const Common::Rect &screenRectangle, bool isClickable, bool isMoving, bool isTarget, bool isRetired); + bool addObject(int sceneObjectId, const BoundingBox &boundingBox, bool isClickable, bool isObstacle, uint8 unknown1, bool isTarget); + bool addItem(int sceneObjectId, const BoundingBox &boundingBox, const Common::Rect &screenRectangle, bool isTarget, bool isObstacle); bool remove(int sceneObjectId); void clear(); int findByXYZ(bool *isClickable, bool *isObstacle, bool *isTarget, Vector3 &position, bool findClickables, bool findObstacles, bool findTargets) const; @@ -80,16 +82,18 @@ public: void setMoving(int sceneObjectId, bool isMoving); void setRetired(int sceneObjectId, bool isRetired); bool isBetween(float sourceX, float sourceZ, float targetX, float targetZ, int sceneObjectId) const; - bool isObstacleBetween(float sourceX, float sourceZ, float targetX, float targetZ, float altitude, int exceptSceneObjectId) const; + bool isObstacleBetween(const Vector3 &source, const Vector3 &target, int exceptSceneObjectId) const; void setIsClickable(int sceneObjectId, bool isClickable); void setIsObstacle(int sceneObjectId, bool isObstacle); void setIsTarget(int sceneObjectId, bool isTarget); void updateObstacles(); + void save(SaveFileWriteStream &f); + void load(SaveFileReadStream &f); private: int findById(int sceneObjectId) const; - bool addSceneObject(int sceneObjectId, SceneObjectType sceneObjectType, BoundingBox *boundingBox, Common::Rect *screenRectangle, bool isClickable, bool isObstacle, uint8 unknown1, bool isTarget, bool isMoving, bool isRetired); + bool addSceneObject(int sceneObjectId, SceneObjectType sceneObjectType, const BoundingBox &boundingBox, const Common::Rect &screenRectangle, bool isClickable, bool isObstacle, uint8 unknown1, bool isTarget, bool isMoving, bool isRetired); int findEmpty() const; }; diff --git a/engines/bladerunner/screen_effects.h b/engines/bladerunner/screen_effects.h index ad0fb5090f..d16f1cf284 100644 --- a/engines/bladerunner/screen_effects.h +++ b/engines/bladerunner/screen_effects.h @@ -39,12 +39,12 @@ class ScreenEffects { public: struct Entry { Color256 palette[16]; - uint16 x; - uint16 y; - uint16 width; - uint16 height; - uint16 z; - uint8 *data; + uint16 x; + uint16 y; + uint16 width; + uint16 height; + uint16 z; + uint8 *data; }; BladeRunnerEngine *_vm; @@ -58,7 +58,7 @@ public: ~ScreenEffects(); void readVqa(Common::SeekableReadStream *stream); - void getColor(Color256 *outColor, uint16 x, uint16 y, uint16 z) const ; + void getColor(Color256 *outColor, uint16 x, uint16 y, uint16 z) const; //TODO //bool isAffectingArea(int x, int y, int width, int height, int unk); diff --git a/engines/bladerunner/script/ai/bullet_bob.cpp b/engines/bladerunner/script/ai/bullet_bob.cpp new file mode 100644 index 0000000000..87394fa44e --- /dev/null +++ b/engines/bladerunner/script/ai/bullet_bob.cpp @@ -0,0 +1,555 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can 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 "bladerunner/script/ai_script.h" + +namespace BladeRunner { + +AIScriptBulletBob::AIScriptBulletBob(BladeRunnerEngine *vm) : AIScriptBase(vm) { + _var1 = 0; + _var2 = 6; + _var3 = 1; + _var4 = 0; +} + +void AIScriptBulletBob::Initialize() { + _animationFrame = 0; + _animationState = 0; + _animationStateNext = 0; + _animationNext = 0; + + _var1 = 0; + _var2 = 6; + _var3 = 1; + _var4 = 0; + + Actor_Set_Goal_Number(kActorBulletBob, 0); + Actor_Set_Targetable(kActorBulletBob, 1); +} + +bool AIScriptBulletBob::Update() { + if (Game_Flag_Query(289) && Actor_Query_Goal_Number(kActorBulletBob) != 4) { + Actor_Set_Goal_Number(kActorBulletBob, 4); + } + if (Player_Query_Combat_Mode() != 1 + || Player_Query_Current_Scene() != kSceneRC04 + || Game_Flag_Query(296) + || Global_Variable_Query(kVariableChapter) >= 4) { + if (Actor_Query_Goal_Number(kActorBulletBob) == 1 && !Player_Query_Combat_Mode()) { + AI_Countdown_Timer_Reset(kActorBulletBob, 2); + Game_Flag_Reset(296); + Game_Flag_Set(303); + Actor_Set_Goal_Number(kActorBulletBob, 0); + } + } else { + AI_Countdown_Timer_Reset(kActorBulletBob, 2); + AI_Countdown_Timer_Start(kActorBulletBob, 2, 10); + Actor_Set_Goal_Number(kActorBulletBob, 1); + Actor_Modify_Friendliness_To_Other(kActorBulletBob, kActorMcCoy, -15); + Game_Flag_Set(296); + } + if (Actor_Query_Goal_Number(kActorBulletBob) != 2 || Game_Flag_Query(295) || _animationState) { + if (Game_Flag_Query(303) == 1 && Player_Query_Combat_Mode() == 1 && Actor_Query_Goal_Number(kActorBulletBob) != 4) { + Actor_Set_Goal_Number(kActorBulletBob, 2); + } else { + return false; + } + } else { + Actor_Face_Heading(kActorBulletBob, 208, 0); + _animationFrame = 0; + _animationState = 2; + Actor_Set_Goal_Number(kActorBulletBob, 3); + Game_Flag_Set(295); + } + + return true; +} + +void AIScriptBulletBob::TimerExpired(int timer) { + if (timer != 2 || Actor_Query_Goal_Number(kActorBulletBob) != 1) + return; //false; + + Actor_Set_Goal_Number(kActorBulletBob, 2); + AI_Countdown_Timer_Reset(kActorBulletBob, 2); + + return; //true; +} + +void AIScriptBulletBob::CompletedMovementTrack() { + //return false; +} + +void AIScriptBulletBob::ReceivedClue(int clueId, int fromActorId) { + //return false; +} + +void AIScriptBulletBob::ClickedByPlayer() { + //return false; +} + +void AIScriptBulletBob::EnteredScene(int sceneId) { + // return false; +} + +void AIScriptBulletBob::OtherAgentEnteredThisScene(int otherActorId) { + // return false; +} + +void AIScriptBulletBob::OtherAgentExitedThisScene(int otherActorId) { + // return false; +} + +void AIScriptBulletBob::OtherAgentEnteredCombatMode(int otherActorId, int combatMode) { + // return false; +} + +void AIScriptBulletBob::ShotAtAndMissed() { + // return false; +} + +bool AIScriptBulletBob::ShotAtAndHit() { + Global_Variable_Increment(24, 1); + if (Global_Variable_Query(24) > 0) { + Actor_Set_Targetable(kActorBulletBob, 0); + Actor_Set_Goal_Number(kActorBulletBob, 99); + _animationFrame = 0; + _animationState = 3; + Ambient_Sounds_Play_Speech_Sound(2, 9000, 100, 0, 0, 0); + Actor_Face_Heading(kActorBulletBob, 281, 0); + } + + return false; +} + +void AIScriptBulletBob::Retired(int byActorId) { + // return false; +} + +int AIScriptBulletBob::GetFriendlinessModifierIfGetsClue(int otherActorId, int clueId) { + return 0; +} + +bool AIScriptBulletBob::GoalChanged(int currentGoalNumber, int newGoalNumber) { + if (newGoalNumber || Game_Flag_Query(303) != 1 || Player_Query_Current_Scene() != kSceneRC04) { + if (newGoalNumber == 1 && !Game_Flag_Query(303) && Player_Query_Current_Scene() == kSceneRC04) { + Actor_Says(kActorBulletBob, 120, 37); + Actor_Says(kActorMcCoy, 4915, 13); + return true; + } + if (newGoalNumber == 6) { + Scene_Exits_Disable(); + Actor_Force_Stop_Walking(kActorMcCoy); + Ambient_Sounds_Play_Speech_Sound(kActorMcCoy, 9900, 100, 0, 0, 0); + Actor_Change_Animation_Mode(kActorMcCoy, 48); + Actor_Retired_Here(kActorMcCoy, 6, 6, 1, -1); + Scene_Exits_Enable(); + } + if (newGoalNumber != 4) { + return false; + } + if (Actor_Clue_Query(kActorMcCoy, 164) != 1) { + Delay(2000); + Actor_Voice_Over(2100, kActorVoiceOver); + Actor_Voice_Over(2110, kActorVoiceOver); + Actor_Voice_Over(2120, kActorVoiceOver); + Actor_Voice_Over(2130, kActorVoiceOver); + } + } else { + Actor_Says(kActorBulletBob, 140, 16); + } + + return true; +} + +bool AIScriptBulletBob::UpdateAnimation(int *animation, int *frame) { + switch (_animationState) { + case 0: + if (_var1 == 1) { + *animation = 516; + if (_var4) { + _var4--; + } else { + if (++_animationFrame == 6) { + _var4 = Random_Query(4, 8); + } + if (_animationFrame == 11) { + _var4 = Random_Query(2, 6); + } + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(515)) { + _animationFrame = 0; + _var1 = 0; + _var3 = 2 * Random_Query(0, 1) - 1; + _var2 = Random_Query(3, 7); + _var4 = Random_Query(0, 4); + } + } + } else if (_var1 == 0) { + *animation = 514; + if (_var4) { + _var4--; + } else { + _animationFrame += _var3; + if (_animationFrame < 0) { + _animationFrame = Slice_Animation_Query_Number_Of_Frames(514) - 1; + } else if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(514)) { + _animationFrame = 0; + } + if (!--_var2) { + _var3 = 2 * Random_Query(0, 1) - 1; + _var2 = Random_Query(3, 7); + _var4 = Random_Query(0, 4); + } + if (!_animationFrame) { + _var1 = Random_Query(0, 1); + } + } + } + break; + + case 1: + *animation = 506; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(506)) { + _animationFrame = 0; + } + break; + + case 2: + *animation = 513; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(513)) { + _animationFrame = 0; + _animationState = 1; + *animation = 506; + } + if (_animationFrame == 10) { + Sound_Play(492, 75, 0, 0, 50); + } + if (_animationFrame == 5) { + Sound_Play(493, 90, 0, 0, 50); + Actor_Set_Goal_Number(kActorBulletBob, 6); + } + break; + + case 3: + *animation = 510; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(510) - 1) { + _animationFrame = Slice_Animation_Query_Number_Of_Frames(510) - 1; + _animationState = 16; + Game_Flag_Set(289); + } + break; + + case 4: + break; + + case 5: + *animation = 525; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(525)) { + *animation = 514; + _animationFrame = 0; + _animationState = 0; + } + break; + + case 6: + *animation = 517; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(517)) { + _animationFrame = 0; + } + break; + + case 7: + *animation = 518; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(518)) { + _animationFrame = 0; + _animationState = 6; + *animation = 517; + } + break; + + case 8: + *animation = 519; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(519)) { + _animationFrame = 0; + _animationState = 6; + *animation = 517; + } + break; + + case 9: + *animation = 520; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(520)) { + _animationFrame = 0; + _animationState = 6; + *animation = 517; + } + break; + + case 10: + *animation = 521; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(521)) { + _animationFrame = 0; + _animationState = 6; + *animation = 517; + } + break; + + case 11: + *animation = 522; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(522)) { + _animationFrame = 0; + _animationState = 6; + *animation = 517; + } + break; + + case 12: + *animation = 523; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(523)) { + _animationFrame = 0; + _animationState = 6; + *animation = 517; + } + break; + + case 13: + *animation = 524; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(524)) { + _animationFrame = 0; + _animationState = 6; + *animation = 517; + } + break; + + case 14: + *animation = 512; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(512)) { + _animationFrame = 0; + _animationState = 1; + *animation = 506; + } + break; + + case 15: + if (_var1 == 1) { + *animation = 516; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(516)) { + _animationFrame += 2; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(516)) { + _animationFrame = 0; + *animation = _animationNext; + _animationState = _animationStateNext; + } + } else { + _animationFrame -= 2; + if (_animationFrame <= 0) { + _animationFrame = 0; + *animation = _animationNext; + _animationState = _animationStateNext; + } + } + } else if (_var1 == 0) { + *animation = 514; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(514)) { + _animationFrame += 2; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(514)) { + _animationFrame = 0; + *animation = _animationNext; + _animationState = _animationStateNext; + } + } else { + _animationFrame -= 2; + if (_animationFrame <= 0) { + _animationFrame = 0; + *animation = _animationNext; + _animationState = _animationStateNext; + } + } + } + break; + + case 16: + *animation = 510; + _animationFrame = Slice_Animation_Query_Number_Of_Frames(510) - 1; + break; + + default: + break; + } + *frame = _animationFrame; + + return true; +} + +bool AIScriptBulletBob::ChangeAnimationMode(int mode) { + switch (mode) { + case 0: + if (_animationState > 4 || _animationState) { + _animationState = 0; + _animationFrame = 0; + } + break; + + case 3: + case 9: + case 30: + if (_animationState < 6 || _animationState > 13) { + _animationState = 15; + _animationStateNext = 6; + _animationNext = 517; + } + break; + + case 4: + if (_animationState <= 4 && !_animationState) { + _animationState = 14; + _animationFrame = 0; + } + break; + + case 6: + _animationState = 2; + _animationFrame = 0; + break; + + case 10: + case 31: + if (_animationState < 6 || _animationState > 13) { + _animationState = 15; + _animationStateNext = 7; + _animationNext = 518; + } + break; + + case 11: + case 33: + if (_animationState < 6 || _animationState > 13) { + _animationState = 15; + _animationStateNext = 9; + _animationNext = 520; + } + break; + + case 21: + case 22: + _animationState = 3; + _animationFrame = 0; + break; + + case 23: + _animationState = 5; + _animationFrame = 0; + break; + + case 32: + if (_animationState < 6 || _animationState > 13) { + _animationState = 15; + _animationStateNext = 8; + _animationNext = 519; + } + break; + + case 34: + if (_animationState < 6 || _animationState > 13) { + _animationState = 15; + _animationStateNext = 10; + _animationNext = 521; + } + break; + + case 35: + if (_animationState < 6 || _animationState > 13) { + _animationState = 15; + _animationStateNext = 11; + _animationNext = 522; + } + break; + + case 36: + if (_animationState < 6 || _animationState > 13) { + _animationState = 15; + _animationStateNext = 12; + _animationNext = 523; + } + break; + + case 37: + if (_animationState < 6 || _animationState > 13) { + _animationState = 15; + _animationStateNext = 13; + _animationNext = 524; + } + break; + + case 48: + _animationState = 4; + _animationFrame = 0; + break; + + case 88: + _animationState = 16; + _animationFrame = Slice_Animation_Query_Number_Of_Frames(510) - 1; + break; + + default: + _animationState = 0; + _animationFrame = 0; + break; + } + return true; +} + +void AIScriptBulletBob::QueryAnimationState(int *animationState, int *animationFrame, int *animationStateNext, int *animationNext) { + *animationState = _animationState; + *animationFrame = _animationFrame; + *animationStateNext = _animationStateNext; + *animationNext = _animationNext; +} + +void AIScriptBulletBob::SetAnimationState(int animationState, int animationFrame, int animationStateNext, int animationNext) { + _animationState = animationState; + _animationFrame = animationFrame; + _animationStateNext = animationStateNext; + _animationNext = animationNext; +} + +bool AIScriptBulletBob::ReachedMovementTrackWaypoint(int waypointId) { + return true; +} + +void AIScriptBulletBob::FledCombat() { + // return false; +} + +} // End of namespace BladeRunner diff --git a/engines/bladerunner/script/ai/clovis.cpp b/engines/bladerunner/script/ai/clovis.cpp index 9c8976e02d..58180f3699 100644 --- a/engines/bladerunner/script/ai/clovis.cpp +++ b/engines/bladerunner/script/ai/clovis.cpp @@ -56,24 +56,23 @@ bool AIScriptClovis::Update() { } else if (Global_Variable_Query(kVariableChapter) == 3 && Actor_Query_Goal_Number(kActorClovis) < 350) { Actor_Set_Goal_Number(kActorClovis, 350); return true; - } else if (Global_Variable_Query(kVariableChapter) != 4 || Game_Flag_Query(542)) { + } else if (Global_Variable_Query(kVariableChapter) == 4 && !Game_Flag_Query(542)) { + Game_Flag_Set(542); + Actor_Set_Goal_Number(kActorClovis, 400); + return true; + } else { if (Global_Variable_Query(kVariableChapter) == 5 && Actor_Query_Goal_Number(kActorClovis) < 500) { Actor_Set_Goal_Number(kActorClovis, 500); } if (Actor_Query_Goal_Number(kActorClovis) == 511 && Game_Flag_Query(657)) { Actor_Set_Goal_Number(kActorClovis, 512); } - if (Game_Flag_Query(653) != 1 || Game_Flag_Query(696) || Game_Flag_Query(697) != 1) { - return true; - } else { + if (Game_Flag_Query(653) && !Game_Flag_Query(696) && Game_Flag_Query(697)) { Actor_Set_Goal_Number(kActorClovis, 517); Game_Flag_Set(696); return true; } - } else { - Game_Flag_Set(542); - Actor_Set_Goal_Number(kActorClovis, 400); - return true; + return false; } } @@ -83,11 +82,11 @@ void AIScriptClovis::TimerExpired(int timer) { void AIScriptClovis::CompletedMovementTrack() { switch (Actor_Query_Goal_Number(kActorClovis)) { - case 100: + case 101: Actor_Set_Goal_Number(kActorClovis, 103); break; - case 101: + case 102: Actor_Set_Goal_Number(kActorClovis, 102); break; @@ -114,7 +113,7 @@ void AIScriptClovis::ReceivedClue(int clueId, int fromActorId) { void AIScriptClovis::ClickedByPlayer() { if (Actor_Query_Goal_Number(kActorClovis) == 599) { - Actor_Face_Actor(kActorMcCoy, kActorClovis, 1); + Actor_Face_Actor(kActorMcCoy, kActorClovis, true); Actor_Says(kActorMcCoy, 8630, 16); } } @@ -132,14 +131,12 @@ void AIScriptClovis::OtherAgentExitedThisScene(int otherActorId) { } void AIScriptClovis::OtherAgentEnteredCombatMode(int otherActorId, int combatMode) { - if (Game_Flag_Query(653) != 1 || !Actor_Query_In_Set(kActorMcCoy, kSetKP07)) { - return; //false; + if (Game_Flag_Query(653) && Actor_Query_In_Set(kActorMcCoy, kSetKP07)) { + Game_Flag_Set(697); + Game_Flag_Set(714); + // return true; } - - Game_Flag_Set(697); - Game_Flag_Set(714); - - return; //true; + // return false; } void AIScriptClovis::ShotAtAndMissed() { @@ -152,14 +149,14 @@ bool AIScriptClovis::ShotAtAndHit() { ADQ_Flush(); Actor_Set_Goal_Number(kActorClovis, 599); shotAnim(); - Actor_Set_Targetable(kActorClovis, 0); + Actor_Set_Targetable(kActorClovis, false); ADQ_Add(kActorMcCoy, 2340, -1); Music_Stop(3); } else if (Actor_Query_Goal_Number(kActorClovis) == 513 || Actor_Query_Goal_Number(kActorClovis) == 518) { ADQ_Flush(); Actor_Set_Goal_Number(kActorClovis, 599); shotAnim(); - Actor_Set_Targetable(kActorClovis, 0); + Actor_Set_Targetable(kActorClovis, false); Music_Stop(3); } } @@ -167,7 +164,7 @@ bool AIScriptClovis::ShotAtAndHit() { } void AIScriptClovis::Retired(int byActorId) { - if (Game_Flag_Query(653) == 1) { + if (Game_Flag_Query(653)) { if (Actor_Query_In_Set(kActorClovis, kSetKP07)) { Global_Variable_Decrement(51, 1); Actor_Set_Goal_Number(kActorClovis, 599); @@ -175,8 +172,8 @@ void AIScriptClovis::Retired(int byActorId) { if (!Global_Variable_Query(51)) { Player_Loses_Control(); Delay(2000); - Player_Set_Combat_Mode(0); - Loop_Actor_Walk_To_XYZ(kActorMcCoy, -12.0f, -41.58f, 72.0f, 0, 1, 0, 0); + Player_Set_Combat_Mode(false); + Loop_Actor_Walk_To_XYZ(kActorMcCoy, -12.0f, -41.58f, 72.0f, 0, true, false, 0); Ambient_Sounds_Remove_All_Non_Looping_Sounds(1); Ambient_Sounds_Remove_All_Looping_Sounds(1); Game_Flag_Set(579); @@ -188,40 +185,38 @@ void AIScriptClovis::Retired(int byActorId) { } int AIScriptClovis::GetFriendlinessModifierIfGetsClue(int otherActorId, int clueId) { - if (otherActorId != kActorMcCoy) + if (otherActorId != kActorMcCoy) { return 0; + } switch (clueId) { - case 212: - case 230: + case kClueMcCoyKilledRunciter1: + case kClueMcCoyKilledRunciter2: return 6; - break; - case 214: - case 239: - case 240: + case kClueMcCoyIsABladeRunner: + case kClueMcCoyIsStupid: + case kClueMcCoyIsAnnoying: return -2; - case 215: - case 217: - case 218: - case 219: - case 220: - case 221: - case 241: + case kClueMcCoyLetZubenEscape: + case kClueMcCoyHelpedIzoIzoIsAReplicant: + case kClueMcCoyHelpedDektora: + case kClueMcCoyHelpedLucy: + case kClueMcCoyHelpedGordo: + case kClueMcCoyShotGuzza: + case kClueMcCoyIsKind: return 4; - case 216: + case kClueMcCoyWarnedIzo: return 2; - case 222: + case kClueMcCoyRetiredZuben: return -3; - case 223: - case 224: + case kClueMcCoyRetiredLucy: + case kClueMcCoyRetiredDektora: return -10; - case 226: - case 227: - case 228: - case 242: + case kClueMcCoyRetiredSadik: + case kClueMcCoyShotZubenInTheBack: + case kClueMcCoyRetiredLutherLance: + case kClueMcCoyIsInsane: return -5; - default: - return 0; } return 0; } @@ -251,30 +246,30 @@ bool AIScriptClovis::GoalChanged(int currentGoalNumber, int newGoalNumber) { case 103: Actor_Set_Goal_Number(kActorSadik, 107); Actor_Says(kActorClovis, 10, 15); - Actor_Says(kActorSadik, 0, 3); - Actor_Face_Actor(kActorClovis, kActorSadik, 1); + Actor_Says(kActorSadik, 0, kAnimationModeTalk); + Actor_Face_Actor(kActorClovis, kActorSadik, true); Actor_Says(kActorClovis, 20, 13); Actor_Says(kActorClovis, 30, 12); - Actor_Face_Actor(kActorSadik, kActorClovis, 1); - Actor_Says(kActorSadik, 10, 3); + Actor_Face_Actor(kActorSadik, kActorClovis, true); + Actor_Says(kActorSadik, 10, kAnimationModeTalk); Actor_Says(kActorClovis, 40, 17); - Actor_Says(kActorSadik, 20, 3); - Actor_Face_Actor(kActorClovis, 0, 1); - Actor_Face_Actor(kActorSadik, 0, 1); + Actor_Says(kActorSadik, 20, kAnimationModeTalk); + Actor_Face_Actor(kActorClovis, kActorMcCoy, true); + Actor_Face_Actor(kActorSadik, kActorMcCoy, true); Actor_Says(kActorClovis, 50, 14); - Actor_Change_Animation_Mode(kActorClovis, 53); + Actor_Change_Animation_Mode(kActorClovis, kAnimationModeSit); return true; case 105: Actor_Says(kActorClovis, 60, 30); - Actor_Says(kActorSadik, 30, 3); + Actor_Says(kActorSadik, 30, kAnimationModeTalk); Actor_Says(kActorClovis, 70, 30); Actor_Says(kActorClovis, 80, 30); Actor_Change_Animation_Mode(kActorClovis, 29); - Actor_Says(kActorSadik, 40, 3); - Actor_Says(kActorSadik, 50, 3); + Actor_Says(kActorSadik, 40, kAnimationModeTalk); + Actor_Says(kActorSadik, 50, kAnimationModeTalk); Actor_Says(kActorClovis, 90, 13); - Actor_Face_Current_Camera(5, 1); + Actor_Face_Current_Camera(5, true); Actor_Says(kActorClovis, 100, 17); Delay(1000); if (!Game_Flag_Query(48)) { @@ -343,7 +338,7 @@ bool AIScriptClovis::GoalChanged(int currentGoalNumber, int newGoalNumber) { return true; case 510: - if (Game_Flag_Query(653) == 1) { + if (Game_Flag_Query(653)) { Actor_Set_Goal_Number(kActorClovis, 513); } else { Actor_Set_Goal_Number(kActorClovis, 511); @@ -357,26 +352,26 @@ bool AIScriptClovis::GoalChanged(int currentGoalNumber, int newGoalNumber) { return true; case 512: - Actor_Says(kActorClovis, 110, 3); - Actor_Says(kActorMcCoy, 2255, 3); - Actor_Says(kActorClovis, 120, 3); - Actor_Says(kActorClovis, 130, 3); - Actor_Says(kActorClovis, 140, 3); - Actor_Says(kActorMcCoy, 2260, 3); - Actor_Says(kActorClovis, 150, 3); + Actor_Says(kActorClovis, 110, kAnimationModeTalk); + Actor_Says(kActorMcCoy, 2255, kAnimationModeTalk); + Actor_Says(kActorClovis, 120, kAnimationModeTalk); + Actor_Says(kActorClovis, 130, kAnimationModeTalk); + Actor_Says(kActorClovis, 140, kAnimationModeTalk); + Actor_Says(kActorMcCoy, 2260, kAnimationModeTalk); + Actor_Says(kActorClovis, 150, kAnimationModeTalk); Actor_Set_Goal_Number(kActorClovis, 513); return true; case 513: Actor_Put_In_Set(kActorClovis, kSetKP07); - Actor_Set_Targetable(kActorClovis, 1); - if (Game_Flag_Query(653) == 1) { + Actor_Set_Targetable(kActorClovis, true); + if (Game_Flag_Query(653)) { Global_Variable_Set(51, 0); Global_Variable_Increment(51, 1); Actor_Set_At_XYZ(kActorClovis, 45.0f, -41.52f, -85.0f, 750); } else { Actor_Set_At_XYZ(kActorClovis, 84.85f, -50.56f, -68.87f, 800); - Actor_Face_Heading(kActorClovis, 1022, 0); + Actor_Face_Heading(kActorClovis, 1022, false); } someAnim(); return true; @@ -384,15 +379,15 @@ bool AIScriptClovis::GoalChanged(int currentGoalNumber, int newGoalNumber) { case 514: Actor_Says(kActorMcCoy, 2345, 16); Actor_Says(kActorClovis, 170, -1); - Actor_Says(kActorClovis, 180, 3); + Actor_Says(kActorClovis, 180, kAnimationModeTalk); Actor_Says(kActorMcCoy, 2350, 17); if (!Game_Flag_Query(714)) { Actor_Says(kActorMcCoy, 2355, 11); } Actor_Says(kActorClovis, 190, -1); - Actor_Says(kActorClovis, 200, 3); + Actor_Says(kActorClovis, 200, kAnimationModeTalk); Actor_Says(kActorMcCoy, 2360, 18); - Actor_Says(kActorClovis, 210, 3); + Actor_Says(kActorClovis, 210, kAnimationModeTalk); Actor_Says(kActorClovis, 220, -1); Actor_Set_Goal_Number(kActorClovis, 515); return true; @@ -409,15 +404,15 @@ bool AIScriptClovis::GoalChanged(int currentGoalNumber, int newGoalNumber) { return true; case 516: - Actor_Says(kActorMcCoy, 8501, 3); - Actor_Says(kActorClovis, 1260, 3); - Actor_Says(kActorMcCoy, 8502, 3); - Actor_Says(kActorClovis, 1270, 3); - Actor_Says(kActorMcCoy, 8504, 3); - Actor_Says(kActorClovis, 1290, 3); - Actor_Says(kActorMcCoy, 8505, 3); - Actor_Says(kActorClovis, 1300, 3); - Actor_Says(kActorClovis, 1310, 3); + Actor_Says(kActorMcCoy, 8501, kAnimationModeTalk); + Actor_Says(kActorClovis, 1260, kAnimationModeTalk); + Actor_Says(kActorMcCoy, 8502, kAnimationModeTalk); + Actor_Says(kActorClovis, 1270, kAnimationModeTalk); + Actor_Says(kActorMcCoy, 8504, kAnimationModeTalk); + Actor_Says(kActorClovis, 1290, kAnimationModeTalk); + Actor_Says(kActorMcCoy, 8505, kAnimationModeTalk); + Actor_Says(kActorClovis, 1300, kAnimationModeTalk); + Actor_Says(kActorClovis, 1310, kAnimationModeTalk); Ambient_Sounds_Remove_All_Non_Looping_Sounds(1); Ambient_Sounds_Remove_All_Looping_Sounds(1); Outtake_Play(20, 0, -1); @@ -440,29 +435,29 @@ bool AIScriptClovis::GoalChanged(int currentGoalNumber, int newGoalNumber) { Global_Variable_Decrement(51, 1); } if (Global_Variable_Query(kVariableChapter) == 5 && Actor_Query_In_Set(kActorDektora, kSetKP07)) { - Non_Player_Actor_Combat_Mode_On(kActorDektora, 0, 0, 0, 19, 4, 7, 8, 0, 0, 100, 10, 300, 0); + Non_Player_Actor_Combat_Mode_On(kActorDektora, kActorCombatStateIdle, false, kActorMcCoy, 19, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, 0, 0, 100, 10, 300, false); } if (Global_Variable_Query(kVariableChapter) == 5 && Actor_Query_In_Set(kActorZuben, kSetKP07)) { - Non_Player_Actor_Combat_Mode_On(kActorZuben, 0, 0, 0, 19, 4, 7, 8, 0, 0, 100, 10, 300, 0); + Non_Player_Actor_Combat_Mode_On(kActorZuben, kActorCombatStateIdle, false, kActorMcCoy, 19, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, 0, 0, 100, 10, 300, false); } if (Global_Variable_Query(kVariableChapter) == 5 && Actor_Query_In_Set(kActorSadik, kSetKP07)) { - Non_Player_Actor_Combat_Mode_On(kActorSadik, 0, 1, 0, 19, 4, 7, 8, 0, 0, 100, 10, 300, 0); + Non_Player_Actor_Combat_Mode_On(kActorSadik, kActorCombatStateIdle, true, kActorMcCoy, 19, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, 0, 0, 100, 10, 300, false); } if (Global_Variable_Query(kVariableChapter) == 5 && Actor_Query_In_Set(kActorIzo, kSetKP07)) { - Non_Player_Actor_Combat_Mode_On(kActorIzo, 0, 0, 0, 19, 4, 7, 8, 0, 0, 100, 10, 300, 0); + Non_Player_Actor_Combat_Mode_On(kActorIzo, kActorCombatStateIdle, false, kActorMcCoy, 19, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, 0, 0, 100, 10, 300, false); } if (Global_Variable_Query(kVariableChapter) == 5 && Actor_Query_In_Set(kActorGordo, kSetKP07)) { - Non_Player_Actor_Combat_Mode_On(kActorGordo, 0, 1, 0, 19, 4, 7, 8, 0, 0, 100, 10, 300, 0); + Non_Player_Actor_Combat_Mode_On(kActorGordo, kActorCombatStateIdle, true, kActorMcCoy, 19, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, 0, 0, 100, 10, 300, false); } if (Global_Variable_Query(kVariableChapter) == 5 && Actor_Query_In_Set(kActorClovis, kSetKP07)) { - Non_Player_Actor_Combat_Mode_On(kActorClovis, 0, 0, 0, 19, 4, 7, 8, 0, 0, 100, 10, 300, 0); + Non_Player_Actor_Combat_Mode_On(kActorClovis, kActorCombatStateIdle, false, kActorMcCoy, 19, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, 0, 0, 100, 10, 300, false); } return true; case 518: Actor_Set_At_XYZ(kActorClovis, 84.85f, -50.56f, -68.87f, 800); - Actor_Face_Heading(kActorClovis, 1022, 0); - Actor_Set_Targetable(kActorClovis, 1); + Actor_Face_Heading(kActorClovis, 1022, false); + Actor_Set_Targetable(kActorClovis, true); Game_Flag_Set(685); someAnim(); return true; @@ -702,7 +697,7 @@ bool AIScriptClovis::UpdateAnimation(int *animation, int *frame) { if (!_animationFrame && _flag) { _animationState = 2; _animationFrame = 0; - Actor_Change_Animation_Mode(kActorClovis, 53); + Actor_Change_Animation_Mode(kActorClovis, kAnimationModeSit); } else { _animationFrame++; if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(241)) { @@ -1163,11 +1158,11 @@ bool AIScriptClovis::UpdateAnimation(int *animation, int *frame) { bool AIScriptClovis::ChangeAnimationMode(int mode) { switch (mode) { - case 0: + case kAnimationModeIdle: if (!Game_Flag_Query(685)) { switch (_animationState) { case 2: - Actor_Change_Animation_Mode(kActorClovis, 53); + Actor_Change_Animation_Mode(kActorClovis, kAnimationModeSit); break; case 4: break; @@ -1199,17 +1194,17 @@ bool AIScriptClovis::ChangeAnimationMode(int mode) { _animationFrame = 0; break; - case 1: + case kAnimationModeWalk: _animationState = 21; _animationFrame = 0; break; - case 2: + case kAnimationModeRun: _animationState = 22; _animationFrame = 0; break; - case 3: + case kAnimationModeTalk: case 9: if (Game_Flag_Query(685)) { _animationFrame = 0; @@ -1224,7 +1219,7 @@ bool AIScriptClovis::ChangeAnimationMode(int mode) { } break; - case 4: + case kAnimationModeCombatIdle: switch (_animationState) { case 13: case 14: @@ -1244,28 +1239,17 @@ bool AIScriptClovis::ChangeAnimationMode(int mode) { } break; - case 5: - case 18: - case 19: - case 23: - case 24: - case 25: - case 26: - case 27: - case 28: - break; - - case 6: + case kAnimationModeCombatAttack: _animationState = 16; _animationFrame = 0; break; - case 7: + case kAnimationModeCombatWalk: _animationState = 21; _animationFrame = 0; break; - case 8: + case kAnimationModeCombatRun: _animationState = 22; _animationFrame = 0; break; @@ -1331,7 +1315,7 @@ bool AIScriptClovis::ChangeAnimationMode(int mode) { _animationFrame = 0; break; - case 21: + case kAnimationModeHit: if ((unsigned int)(_animationState - 13) > 3) { if ((unsigned int)(_animationState - 32) > 8) { if (Random_Query(0, 1)) { @@ -1354,7 +1338,7 @@ bool AIScriptClovis::ChangeAnimationMode(int mode) { } break; - case 22: + case kAnimationModeCombatHit: if (Random_Query(0, 1)) { _animationState = 17; } else { @@ -1379,12 +1363,12 @@ bool AIScriptClovis::ChangeAnimationMode(int mode) { _animationFrame = Slice_Animation_Query_Number_Of_Frames(226) - 1; break; - case 48: + case kAnimationModeDie: _animationState = 41; _animationFrame = 0; break; - case 53: + case kAnimationModeSit: switch (_animationState) { case 4: case 5: @@ -1465,7 +1449,7 @@ void AIScriptClovis::someAnim() { switch (_animationState) { case 2: - Actor_Change_Animation_Mode(kActorClovis, 53); + Actor_Change_Animation_Mode(kActorClovis, kAnimationModeSit); break; case 4: break; diff --git a/engines/bladerunner/script/ai/dektora.cpp b/engines/bladerunner/script/ai/dektora.cpp index 905c3d16da..28bdf3c83d 100644 --- a/engines/bladerunner/script/ai/dektora.cpp +++ b/engines/bladerunner/script/ai/dektora.cpp @@ -280,7 +280,7 @@ void AIScriptDektora::Retired(int byActorId) { } if (byActorId == kActorSteele && Actor_Query_In_Set(kActorSteele, kSetHF06) && Actor_Query_In_Set(kActorMcCoy, kSetHF06)) { - Non_Player_Actor_Combat_Mode_On(kActorSteele, 3, 1, 0, 15, 4, 7, 8, 0, 0, 100, 25, 300, 0); + Non_Player_Actor_Combat_Mode_On(kActorSteele, kActorCombatStateUncover, true, kActorMcCoy, 15, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, 0, 0, 100, 25, 300, false); } if (Actor_Query_In_Set(kActorDektora, kSetKP07)) { @@ -290,8 +290,8 @@ void AIScriptDektora::Retired(int byActorId) { if (!Global_Variable_Query(51)) { Player_Loses_Control(); Delay(2000); - Player_Set_Combat_Mode(0); - Loop_Actor_Walk_To_XYZ(0, -12.0, -41.580002, 72.0, 0, 1, 0, 0); + Player_Set_Combat_Mode(false); + Loop_Actor_Walk_To_XYZ(kActorMcCoy, -12.0f, -41.58f, 72.0f, 0, true, false, 0); Ambient_Sounds_Remove_All_Non_Looping_Sounds(1); Ambient_Sounds_Remove_All_Looping_Sounds(1); Game_Flag_Set(579); @@ -1095,11 +1095,11 @@ void AIScriptDektora::checkCombat() { && Global_Variable_Query(kVariableChapter) == 5 && Actor_Query_Goal_Number(kActorDektora) != 450) { if (Global_Variable_Query(kVariableAffectionTowards) == 2) { - Global_Variable_Set(45, 0); + Global_Variable_Set(kVariableAffectionTowards, 0); } Actor_Set_Goal_Number(kActorDektora, 450); - Non_Player_Actor_Combat_Mode_On(kActorDektora, 0, 0, kActorMcCoy, 4, 4, 7, 8, 0, -1, -1, 20, 300, 0); + Non_Player_Actor_Combat_Mode_On(kActorDektora, kActorCombatStateIdle, false, kActorMcCoy, 4, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, 0, -1, -1, 20, 300, false); } } diff --git a/engines/bladerunner/script/ai/early_q.cpp b/engines/bladerunner/script/ai/early_q.cpp new file mode 100644 index 0000000000..180493fb6e --- /dev/null +++ b/engines/bladerunner/script/ai/early_q.cpp @@ -0,0 +1,1037 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can 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 "bladerunner/script/ai_script.h" + +namespace BladeRunner { + +AIScriptEarlyQ::AIScriptEarlyQ(BladeRunnerEngine *vm) : AIScriptBase(vm) { + _var1 = 0; + _var2 = 0; + _var3 = 1; + _flag = false; +} + +void AIScriptEarlyQ::Initialize() { + _animationFrame = 0; + _animationState = 0; + _animationStateNext = 0; + _animationNext = 0; + + _var1 = 0; + _var2 = 0; + _var3 = 1; + _flag = 0; +} + +bool AIScriptEarlyQ::Update() { + if (Global_Variable_Query(kVariableChapter) != 1 || Game_Flag_Query(490)) { + if (Global_Variable_Query(kVariableChapter) != 2 || Game_Flag_Query(491)) { + if (Global_Variable_Query(kVariableChapter) != 3 || Game_Flag_Query(564)) { + return false; + } else { + Game_Flag_Set(564); + Actor_Put_In_Set(kActorEarlyQ, kSetFreeSlotH); + Actor_Set_At_Waypoint(kActorEarlyQ, 40, 0); + Actor_Set_Goal_Number(kActorEarlyQ, 200); + } + } else { + Game_Flag_Set(491); + Actor_Put_In_Set(kActorEarlyQ, kSetFreeSlotH); + Actor_Set_At_Waypoint(kActorEarlyQ, 40, 0); + Actor_Set_Goal_Number(kActorEarlyQ, 100); + } + } else { + Game_Flag_Set(490); + Actor_Put_In_Set(kActorEarlyQ, kSetFreeSlotH); + Actor_Set_At_Waypoint(kActorEarlyQ, 40, 0); + Actor_Set_Goal_Number(kActorEarlyQ, 0); + } + + return true; +} + +void AIScriptEarlyQ::TimerExpired(int timer) { + if (Actor_Query_Goal_Number(kActorEarlyQ) == 221 && !timer) { + if (Player_Query_Current_Scene() == 58) { + AI_Countdown_Timer_Reset(kActorEarlyQ, 0); + Actor_Set_Goal_Number(kActorEarlyQ, 222); + } else { + Actor_Set_Goal_Number(kActorEarlyQ, 220); + } + } + if (Actor_Query_Goal_Number(kActorEarlyQ) != 205 || timer) { + if (Actor_Query_Goal_Number(kActorEarlyQ) == 211 && timer == 1) { + AI_Countdown_Timer_Reset(kActorEarlyQ, 1); + Player_Loses_Control(); + Actor_Change_Animation_Mode(kActorEarlyQ, 29); + Delay(2500); + Actor_Face_Actor(kActorEarlyQ, kActorMcCoy, 1); + Actor_Change_Animation_Mode(kActorEarlyQ, 6); + Delay(100); + _vm->_aiScripts->callChangeAnimationMode(kActorMcCoy, 22); + Delay(250); + _vm->_aiScripts->callChangeAnimationMode(kActorMcCoy, 48); + Actor_Retired_Here(kActorMcCoy, 12, 12, 1, -1); + } else { + return; //false; + } + } else { + Player_Loses_Control(); + AI_Countdown_Timer_Reset(kActorEarlyQ, 0); + Actor_Set_Goal_Number(kActorEarlyQ, 215); + } + + return; //true; +} + +void AIScriptEarlyQ::CompletedMovementTrack() { + switch (Actor_Query_Goal_Number(kActorEarlyQ)) { + case 0: + if (Random_Query(1, 2) == 1) { + Actor_Set_Goal_Number(kActorEarlyQ, 1); + } else { + Actor_Set_Goal_Number(kActorEarlyQ, 2); + } + break; + + case 1: + case 2: + Actor_Set_Goal_Number(kActorEarlyQ, 0); + break; + + case 100: + if (Random_Query(1, 2) != 1) { + Actor_Set_Goal_Number(kActorEarlyQ, 102); + break; + } + Actor_Set_Goal_Number(kActorEarlyQ, 101); + break; + + case 101: + Actor_Set_Goal_Number(kActorEarlyQ, 100); + break; + + case 102: + Actor_Set_Goal_Number(kActorEarlyQ, 100); + break; + + case 201: + Game_Flag_Set(569); + Player_Set_Combat_Mode(0); + Actor_Set_Targetable(kActorEarlyQ, 1); + Actor_Set_Goal_Number(kActorEarlyQ, 202); + break; + + case 203: + Actor_Set_Goal_Number(kActorEarlyQ, 204); + break; + + case 222: + Actor_Set_Goal_Number(kActorEarlyQ, 223); + return; //false; + + case 230: + Actor_Set_Goal_Number(kActorEarlyQ, 200); + return; //false; + + default: + return; //false; + } + + return; //true; +} + +void AIScriptEarlyQ::ReceivedClue(int clueId, int fromActorId) { + //return false; +} + +void AIScriptEarlyQ::ClickedByPlayer() { + //return false; +} + +void AIScriptEarlyQ::EnteredScene(int sceneId) { + // return false; +} + +void AIScriptEarlyQ::OtherAgentEnteredThisScene(int otherActorId) { + // return false; +} + +void AIScriptEarlyQ::OtherAgentExitedThisScene(int otherActorId) { + // return false; +} + +void AIScriptEarlyQ::OtherAgentEnteredCombatMode(int otherActorId, int combatMode) { + if (Game_Flag_Query(569) != 1 || otherActorId || combatMode != 1 || Game_Flag_Query(609)) { + if (Actor_Query_Goal_Number(kActorEarlyQ) != 211 || otherActorId || combatMode) { + return; //false; + } else { + if (Game_Flag_Query(565) == 1) { + Game_Flag_Reset(565); + } + AI_Countdown_Timer_Reset(kActorEarlyQ, 1); + Actor_Set_Goal_Number(kActorEarlyQ, 213); + } + } else { + if (!Game_Flag_Query(565)) { + Game_Flag_Set(565); + } + Game_Flag_Set(609); + AI_Countdown_Timer_Reset(kActorEarlyQ, 0); + Actor_Set_Goal_Number(kActorEarlyQ, 206); + } + + return; //true; +} + +void AIScriptEarlyQ::ShotAtAndMissed() { + if (Actor_Query_Goal_Number(kActorEarlyQ) != 211) + return; //false; + + Actor_Set_Goal_Number(kActorEarlyQ, 216); + return; //true; +} + +bool AIScriptEarlyQ::ShotAtAndHit() { + if (Actor_Query_Goal_Number(kActorEarlyQ) < 201 || Actor_Query_Goal_Number(kActorEarlyQ) > 217) + return 0; + + Actor_Set_Goal_Number(kActorEarlyQ, 216); + + return true; +} + +void AIScriptEarlyQ::Retired(int byActorId) { + // return false; +} + +int AIScriptEarlyQ::GetFriendlinessModifierIfGetsClue(int otherActorId, int clueId) { + return 0; +} + +bool AIScriptEarlyQ::GoalChanged(int currentGoalNumber, int newGoalNumber) { + switch (newGoalNumber) { + case 0: + AI_Movement_Track_Flush(kActorEarlyQ); + AI_Movement_Track_Append(kActorEarlyQ, 40, 0); + AI_Movement_Track_Repeat(kActorEarlyQ); + break; + + case 1: + AI_Movement_Track_Flush(kActorEarlyQ); + AI_Movement_Track_Append(kActorEarlyQ, 291, 0); + AI_Movement_Track_Append(kActorEarlyQ, 285, 0); + AI_Movement_Track_Append(kActorEarlyQ, 292, 30); + AI_Movement_Track_Append(kActorEarlyQ, 293, 30); + AI_Movement_Track_Append(kActorEarlyQ, 294, 30); + AI_Movement_Track_Append(kActorEarlyQ, 295, 30); + AI_Movement_Track_Repeat(kActorEarlyQ); + break; + + case 2: + AI_Movement_Track_Flush(kActorEarlyQ); + AI_Movement_Track_Append(kActorEarlyQ, 40, 120); + AI_Movement_Track_Repeat(kActorEarlyQ); + break; + + case 100: + AI_Movement_Track_Flush(kActorEarlyQ); + AI_Movement_Track_Append(kActorEarlyQ, 40, 0); + AI_Movement_Track_Repeat(kActorEarlyQ); + break; + + case 101: + AI_Movement_Track_Flush(kActorEarlyQ); + AI_Movement_Track_Append(kActorEarlyQ, 291, 0); + AI_Movement_Track_Append(kActorEarlyQ, 285, 0); + AI_Movement_Track_Append(kActorEarlyQ, 292, 30); + AI_Movement_Track_Append(kActorEarlyQ, 293, 30); + AI_Movement_Track_Append(kActorEarlyQ, 294, 30); + AI_Movement_Track_Append(kActorEarlyQ, 295, 30); + AI_Movement_Track_Repeat(kActorEarlyQ); + break; + + case 102: + AI_Movement_Track_Flush(kActorEarlyQ); + AI_Movement_Track_Append(kActorEarlyQ, 40, 120); + AI_Movement_Track_Repeat(kActorEarlyQ); + break; + + case 200: + AI_Movement_Track_Flush(kActorEarlyQ); + Actor_Put_In_Set(kActorEarlyQ, kSetFreeSlotH); + Actor_Set_At_Waypoint(kActorEarlyQ, 40, 0); + if (Game_Flag_Query(47) == 1 && Game_Flag_Query(592) && Game_Flag_Query(593)) { + Actor_Set_Goal_Number(kActorEarlyQ, 220); + } else if (Game_Flag_Query(47)) { + Actor_Set_Goal_Number(kActorEarlyQ, 230); + } else { + Actor_Set_Goal_Number(kActorEarlyQ, 220); + } + break; + + case 201: + AI_Movement_Track_Flush(kActorEarlyQ); + AI_Movement_Track_Append(kActorEarlyQ, 40, 0); + AI_Movement_Track_Append(kActorEarlyQ, 322, 0); + AI_Movement_Track_Append(kActorEarlyQ, 354, 0); + AI_Movement_Track_Repeat(kActorEarlyQ); + break; + + case 203: + AI_Movement_Track_Flush(kActorEarlyQ); + AI_Movement_Track_Append(kActorEarlyQ, 355, 0); + AI_Movement_Track_Repeat(kActorEarlyQ); + Actor_Face_Object(kActorMcCoy, "BAR", 1); + break; + + case 205: + Loop_Actor_Walk_To_Actor(kActorEarlyQ, 0, 36, 0, 0); + AI_Countdown_Timer_Reset(kActorEarlyQ, 0); + AI_Countdown_Timer_Start(kActorEarlyQ, 0, 4); + break; + + case 206: + Player_Set_Combat_Mode(kActorSteele); + Actor_Face_Actor(kActorEarlyQ, kActorMcCoy, 1); + Actor_Face_Actor(kActorMcCoy, kActorEarlyQ, 1); + Actor_Change_Animation_Mode(kActorMcCoy, kAnimationModeCombatIdle); + _vm->_aiScripts->callChangeAnimationMode(kActorMcCoy, 5); + Actor_Says(kActorEarlyQ, 130, 3); + Actor_Says(kActorMcCoy, 3400, 5); + Actor_Says_With_Pause(kActorEarlyQ, 140, 1.0, 3); + Actor_Says_With_Pause(kActorEarlyQ, 150, 1.0, 3); + Actor_Says(kActorMcCoy, 3405, 5); + Actor_Says(kActorEarlyQ, 160, 3); + Actor_Says(kActorMcCoy, 3410, 5); + _vm->_aiScripts->callChangeAnimationMode(kActorMcCoy, 4); + Loop_Actor_Walk_To_XYZ(kActorMcCoy, 31.22f, 0.0f, 267.51f, 0, 1, 0, 0); + Actor_Set_Goal_Number(kActorEarlyQ, 207); + break; + + case 208: + if (Game_Flag_Query(374) == 1) { + Actor_Set_Goal_Number(kActorEarlyQ, 210); + } else { + Actor_Set_Goal_Number(kActorEarlyQ, 209); + } + break; + + case 210: + Actor_Set_Targetable(kActorEarlyQ, 0); + Game_Flag_Set(606); + Delay(3500); + Actor_Change_Animation_Mode(kActorEarlyQ, 76); + Delay(2000); + Actor_Set_At_XYZ(kActorEarlyQ, 109.0, 0.0, 374.0, 0); + Actor_Retired_Here(kActorEarlyQ, 12, 12, 1, -1); + Actor_Voice_Over(4180, kActorVoiceOver); + Scene_Exits_Enable(); + break; + + case 211: + AI_Countdown_Timer_Reset(kActorEarlyQ, 1); + AI_Countdown_Timer_Start(kActorEarlyQ, 1, 5); + break; + + case 212: + Actor_Says(kActorEarlyQ, 0, 3); + Actor_Says(kActorEarlyQ, 10, 3); + Actor_Says(kActorEarlyQ, 20, 3); + Actor_Clue_Lose(kActorMcCoy, 89); + Scene_Exits_Enable(); + Player_Gains_Control(); + Game_Flag_Set(627); + Actor_Set_Goal_Number(kActorHanoi, 220); + break; + + case 215: + if (Actor_Query_Inch_Distance_From_Actor(kActorMcCoy, kActorEarlyQ) > 36) { + Loop_Actor_Walk_To_Actor(kActorEarlyQ, kActorMcCoy, 36, kActorMcCoy, kActorMcCoy); + } + Actor_Face_Actor(kActorMcCoy, kActorEarlyQ, 1); + Actor_Face_Actor(kActorEarlyQ, kActorMcCoy, 1); + Actor_Change_Animation_Mode(kActorEarlyQ, 23); + Scene_Loop_Start_Special(2, 2, 0); + Ambient_Sounds_Play_Sound(582, 50, 99, 0, 0); + Actor_Set_Goal_Number(kActorMcCoy, 220); + break; + + case 216: + AI_Movement_Track_Flush(kActorEarlyQ); + Actor_Change_Animation_Mode(kActorEarlyQ, 48); + Delay(250); + Actor_Set_At_XYZ(kActorEarlyQ, 109.0, 0.0, 374.0, 0); + Actor_Set_Goal_Number(kActorHanoi, 240); + Player_Set_Combat_Mode(0); + break; + + case 217: + AI_Movement_Track_Flush(kActorEarlyQ); + AI_Movement_Track_Append(kActorEarlyQ, 354, 0); + AI_Movement_Track_Append(kActorEarlyQ, 322, 0); + AI_Movement_Track_Append(kActorEarlyQ, 40, 0); + AI_Movement_Track_Repeat(kActorEarlyQ); + break; + + case 220: + if (Player_Query_Current_Set() == 13) { + Actor_Set_Goal_Number(kActorEarlyQ, 230); + } else { + Actor_Put_In_Set(kActorEarlyQ, kSetNR05_NR08); + Actor_Set_At_XYZ(kActorEarlyQ, -671.56f, 0.0f, -287.02f, 849); + } + break; + + case 221: + AI_Countdown_Timer_Reset(kActorEarlyQ, 0); + AI_Countdown_Timer_Start(kActorEarlyQ, 0, 20); + break; + + case 222: + AI_Movement_Track_Flush(kActorEarlyQ); + AI_Movement_Track_Append(kActorEarlyQ, 429, 0); + AI_Movement_Track_Repeat(kActorEarlyQ); + break; + + case 223: + if (Player_Query_Current_Scene() == 58) { + Actor_Says(kActorEarlyQ, 670, 3); + Actor_Says(kActorEarlyQ, 690, 3); + Actor_Set_Goal_Number(kActorDektora, 210); + Actor_Set_Goal_Number(kActorEarlyQ, 224); + Actor_Set_Goal_Number(kActorHanoi, 230); + } else { + Actor_Set_Goal_Number(kActorEarlyQ, 220); + } + break; + + case 224: + Game_Flag_Set(620); + break; + + case 229: + AI_Movement_Track_Flush(kActorEarlyQ); + AI_Countdown_Timer_Reset(kActorEarlyQ, 0); + break; + + case 230: + AI_Movement_Track_Flush(kActorEarlyQ); + if (Random_Query(1, 3) > 1) { + AI_Movement_Track_Append(kActorEarlyQ, 322, Random_Query(15, 30)); + AI_Movement_Track_Append(kActorEarlyQ, 39, Random_Query(15, 45)); + AI_Movement_Track_Append(kActorEarlyQ, 40, Random_Query(15, 30)); + } else { + AI_Movement_Track_Append(kActorEarlyQ, 322, Random_Query(5, 15)); + AI_Movement_Track_Append(kActorEarlyQ, 39, Random_Query(5, 15)); + AI_Movement_Track_Append(kActorEarlyQ, 40, Random_Query(5, 15)); + AI_Movement_Track_Append(kActorEarlyQ, 39, Random_Query(5, 15)); + AI_Movement_Track_Append(kActorEarlyQ, 34, Random_Query(10, 20)); + } + AI_Movement_Track_Repeat(kActorEarlyQ); + break; + + default: + return false; + } + + return true; +} + +bool AIScriptEarlyQ::UpdateAnimation(int *animation, int *frame) { + switch (_animationState) { + case 0: + if (_var2 == 1) { + *animation = 370; + if (_var1) { + _var1--; + } else { + if (++_animationFrame == 6) { + _var1 = Random_Query(8, 15); + } + if (_animationFrame < 6) { + _var1 = 1; + } + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(370)) { + _animationFrame = 0; + _var2 = 0; + } + } + } else if (_var2 == 0) { + *animation = 369; + if (_var1) { + _var1--; + if (!Random_Query(0, 6)) { + _var3 = -_var3; + } + } else { + _animationFrame += _var3; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(369)) { + _animationFrame = 0; + } + if (_animationFrame < 0) { + _animationFrame = Slice_Animation_Query_Number_Of_Frames(369) - 1; + } + _var1 = Random_Query(0, 1); + if (!_animationFrame) { + if (!Random_Query(0, 3)) { + _var2 = 1; + } + } + if (!_animationFrame || _animationFrame == 5) { + if (Random_Query(0, 1)) { + _var1 = Random_Query(2, 8); + } + } + } + } + break; + + case 1: + *animation = 381; + _animationFrame++; + if (_animationFrame == 18) { + Ambient_Sounds_Play_Sound(255, 99, 0, 0, 20); + } + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(*animation)) { + Actor_Change_Animation_Mode(kActorEarlyQ, 74); + _animationFrame = 0; + _animationState = 2; + *animation = 382; + } + break; + + case 2: + *animation = 382; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(382)) { + _animationFrame = 0; + } + break; + + case 3: + *animation = 371; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(371)) { + *animation = 369; + _animationFrame = 0; + _animationState = 0; + Actor_Change_Animation_Mode(kActorEarlyQ, 0); + } + break; + + case 4: + *animation = 368; + if (_animationFrame < Slice_Animation_Query_Number_Of_Frames(368) - 1) { + _animationFrame++; + } + break; + + case 5: + *animation = 365; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(365)) { + _animationFrame = 0; + } + break; + + case 6: + *animation = 361; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(361)) { + _animationFrame = 0; + } + break; + + case 7: + *animation = 383; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(383)) { + _animationFrame = 0; + _animationState = 9; + *animation = 384; + } + break; + + case 8: + *animation = 387; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(387)) { + *animation = 369; + _animationFrame = 0; + _animationState = 0; + } + break; + + case 9: + *animation = 384; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(384)) { + _animationFrame = 0; + } + break; + + case 10: + *animation = 385; + if (!_animationFrame && _flag) { + _flag = 0; + _animationState = 9; + _var2 = 0; + *animation = 384; + Actor_Change_Animation_Mode(kActorEarlyQ, 53); + } else { + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(385)) { + _animationFrame = 0; + } + } + break; + + case 11: + *animation = 386; + if (_animationFrame < Slice_Animation_Query_Number_Of_Frames(386) - 1) { + _animationFrame++; + } + if (_animationFrame == 1) { + Ambient_Sounds_Play_Sound(555, 59, 0, 0, 20); + } + if (_animationFrame == 8) { + Ambient_Sounds_Play_Sound(254, 47, 0, 0, 20); + } + if (_animationFrame == 11) { + Ambient_Sounds_Play_Sound(560, 27, 0, 0, 20); + } + if (_animationFrame == 14) { + Ambient_Sounds_Play_Sound(206, 41, 0, 0, 20); + } + break; + + case 12: + *animation = 360; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(360)) { + _animationFrame = 0; + } + break; + + case 13: + *animation = 362; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(362)) { + _animationFrame = 0; + _animationState = 12; + *animation = 360; + } + break; + + case 14: + *animation = 363; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(363)) { + *animation = 369; + _animationFrame = 0; + _animationState = 0; + } + break; + + case 15: + *animation = 364; + _animationFrame++; + if (_animationFrame == 2) { + Ambient_Sounds_Play_Sound(12, 60, 0, 0, 20); + } + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(364)) { + _animationFrame = 0; + _animationState = 12; + *animation = 360; + Actor_Change_Animation_Mode(kActorEarlyQ, 4); + } + break; + + case 16: + *animation = 366; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(366)) { + *animation = 369; + _animationFrame = 0; + _animationState = 0; + Actor_Change_Animation_Mode(kActorEarlyQ, 0); + } + break; + + case 17: + *animation = 367; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(367)) { + *animation = 369; + _animationFrame = 0; + _animationState = 0; + Actor_Change_Animation_Mode(kActorEarlyQ, 0); + } + break; + + case 18: + *animation = 366; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(366)) { + *animation = 369; + _animationFrame = 0; + _animationState = 0; + Actor_Change_Animation_Mode(kActorEarlyQ, 0); + } + break; + + case 19: + *animation = 367; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(367)) { + *animation = 369; + _animationFrame = 0; + _animationState = 0; + Actor_Change_Animation_Mode(kActorEarlyQ, 0); + } + break; + + case 20: + *animation = 372; + if (!_animationFrame && _flag) { + *animation = 369; + _animationFrame = 0; + _flag = 0; + _animationState = 0; + } else { + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(372)) { + _animationFrame = 0; + } + } + break; + + case 21: + *animation = 373; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(373)) { + _animationFrame = 0; + _animationState = 20; + *animation = 372; + } + break; + + case 22: + *animation = 374; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(374)) { + _animationFrame = 0; + _animationState = 20; + *animation = 372; + } + break; + + case 23: + *animation = 375; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(375)) { + _animationFrame = 0; + _animationState = 20; + *animation = 372; + } + break; + + case 24: + *animation = 376; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(376)) { + _animationFrame = 0; + _animationState = 20; + *animation = 372; + } + break; + + case 25: + *animation = 377; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(377)) { + _animationFrame = 0; + _animationState = 20; + *animation = 372; + } + break; + + case 26: + *animation = 378; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(378)) { + _animationFrame = 0; + _animationState = 20; + *animation = 372; + } + break; + + case 27: + *animation = 379; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(379)) { + _animationFrame = 0; + _animationState = 20; + *animation = 372; + } + break; + + case 28: + *animation = 380; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(380)) { + _animationFrame = 0; + _animationState = 20; + *animation = 372; + } + break; + + default: + break; + } + *frame = _animationFrame; + + return true; +} + +bool AIScriptEarlyQ::ChangeAnimationMode(int mode) { + switch (mode) { + case 0: + switch (_animationState) { + case 1: + Actor_Change_Animation_Mode(kActorEarlyQ, 73); + break; + + case 2: + Actor_Change_Animation_Mode(kActorEarlyQ, 74); + break; + + case 9: + Actor_Change_Animation_Mode(kActorEarlyQ, 29); + break; + + case 10: + case 20: + case 21: + case 22: + case 23: + case 24: + case 25: + case 26: + case 27: + case 28: + _flag = 1; + break; + + case 12: + case 13: + case 15: + _animationState = 14; + _animationFrame = 0; + break; + + case 14: + return 1; + + default: + _animationState = 0; + _animationFrame = 0; + break; + } + break; + + case 1: + _animationState = 5; + _animationFrame = 0; + break; + + case 3: + _animationState = 20; + _animationFrame = 0; + _flag = 0; + break; + + case 4: + if ((unsigned int)(_animationState - 12) > 3 || (_animationState != 12 && _animationState != 13 && _animationState != 15)) { + _animationState = 13; + _animationFrame = 0; + } + break; + + case 6: + _animationState = 15; + _animationFrame = 0; + break; + + case 7: + _animationState = 6; + _animationFrame = 0; + break; + + case 12: + _animationState = 21; + _animationFrame = 0; + _flag = 0; + break; + + case 13: + _animationState = 22; + _animationFrame = 0; + _flag = 0; + break; + + case 14: + _animationState = 23; + _animationFrame = 0; + _flag = 0; + break; + + case 15: + _animationState = 24; + _animationFrame = 0; + _flag = 0; + break; + + case 16: + _animationState = 25; + _animationFrame = 0; + _flag = 0; + break; + + case 17: + _animationState = 26; + _animationFrame = 0; + _flag = 0; + break; + + case 18: + _animationState = 27; + _animationFrame = 0; + _flag = 0; + break; + + case 19: + _animationState = 28; + _animationFrame = 0; + _flag = 0; + break; + + case 21: + if ((unsigned int)(_animationState - 12) > 3 || (_animationState != 12 && _animationState != 13 && _animationState != 15)) { + if (Random_Query(0, 1)) { + _animationState = 16; + } else { + _animationState = 17; + } + _animationFrame = 0; + } else { + if (Random_Query(0, 1)) { + _animationState = 18; + } else { + _animationState = 19; + } + _animationFrame = 0; + } + break; + + case 23: + _animationState = 3; + _animationFrame = 0; + break; + + case 29: + _animationState = 8; + _animationFrame = 0; + break; + + case 30: + _animationState = 10; + _animationFrame = 0; + _flag = 0; + break; + + case 48: + _animationState = 4; + _animationFrame = 0; + break; + + case 53: + _animationState = 9; + _animationFrame = 0; + break; + + case 73: + if (_animationState != 1) { + _animationState = 1; + _animationFrame = 0; + } + break; + + case 74: + if (_animationState != 2) { + _animationState = 2; + _animationFrame = 0; + } + break; + + case 76: + _animationState = 11; + _animationFrame = 0; + break; + + case 85: + _animationState = 7; + _animationFrame = 0; + break; + + default: + return true; + } + + return true; +} + +void AIScriptEarlyQ::QueryAnimationState(int *animationState, int *animationFrame, int *animationStateNext, int *animationNext) { + *animationState = _animationState; + *animationFrame = _animationFrame; + *animationStateNext = _animationStateNext; + *animationNext = _animationNext; +} + +void AIScriptEarlyQ::SetAnimationState(int animationState, int animationFrame, int animationStateNext, int animationNext) { + _animationState = animationState; + _animationFrame = animationFrame; + _animationStateNext = animationStateNext; + _animationNext = animationNext; +} + +bool AIScriptEarlyQ::ReachedMovementTrackWaypoint(int waypointId) { + return true; +} + +void AIScriptEarlyQ::FledCombat() { + // return false; +} + +} // End of namespace BladeRunner diff --git a/engines/bladerunner/script/ai/free_slot_a.cpp b/engines/bladerunner/script/ai/free_slot_a.cpp new file mode 100644 index 0000000000..b017a9e3f2 --- /dev/null +++ b/engines/bladerunner/script/ai/free_slot_a.cpp @@ -0,0 +1,659 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can 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 "bladerunner/script/ai_script.h" + +namespace BladeRunner { + +AIScriptFreeSlotA::AIScriptFreeSlotA(BladeRunnerEngine *vm) : AIScriptBase(vm) { + _var1 = 0; + _var2 = 1; + _var3 = 0.0f; + _var4 = 0.0f; // not initialized in original + _var5 = 0.0f; // not initialized in original +} + +void AIScriptFreeSlotA::Initialize() { + _animationFrame = 0; + _animationState = 0; + _animationStateNext = 0; + _animationNext = 0; + + _var1 = 0; + _var2 = 1; + _var3 = 0.0f; + _var4 = 0.0f; // not initialized in original + _var5 = 0.0f; // not initialized in original + + World_Waypoint_Set(525, 45, -780.0f, -615.49f, 2611.0f); + World_Waypoint_Set(526, 45, -780.0f, -615.49f, 2759.0f); +} + +bool AIScriptFreeSlotA::Update() { + switch (Global_Variable_Query(kVariableChapter)) { + case 4: + if (Actor_Query_Which_Set_In(kActorMcCoy) == kSceneUG02 && Actor_Query_Which_Set_In(kActorFreeSlotA) == kSceneUG02) { + int goal = Actor_Query_Goal_Number(kActorFreeSlotA); + if ((goal == 302 || goal == 303) && Actor_Query_Inch_Distance_From_Actor(kActorFreeSlotA, kActorMcCoy) <= 48) { + Actor_Set_Goal_Number(kActorFreeSlotA, 304); + } else if (goal == 309) { + float x, y, z; + + Actor_Query_XYZ(kActorMcCoy, &x, &y, &z); + _var4 += _var3; + if (_var5 < _var4) { + _var3 -= 0.2f; + } else { + _var4 = _var5; + Actor_Set_Goal_Number(kActorFreeSlotA, 0); + } + Actor_Set_At_XYZ(kActorFreeSlotA, x, _var4, z, Actor_Query_Facing_1024(kActorFreeSlotA)); + } + } else { + switch (Actor_Query_Goal_Number(kActorFreeSlotA)) { + case 306: + if (Actor_Query_Which_Set_In(kActorFreeSlotA) == Player_Query_Current_Set() + && Actor_Query_Inch_Distance_From_Actor(kActorFreeSlotA, kActorMcCoy) <= 48) { + Actor_Set_Goal_Number(kActorFreeSlotA, 308); + } + break; + + case 308: + if (Actor_Query_Which_Set_In(kActorFreeSlotA) != Player_Query_Current_Set()) { + Actor_Set_Goal_Number(kActorFreeSlotA, 306); + } + break; + + case 599: + if (Actor_Query_Which_Set_In(kActorFreeSlotA) != Player_Query_Current_Set()) { + Game_Flag_Reset(631); + Game_Flag_Reset(677); + Actor_Set_Goal_Number(kActorFreeSlotA, 0); + } + break; + + default: + if (!Game_Flag_Query(631)) { + Game_Flag_Set(631); + Actor_Set_Goal_Number(kActorFreeSlotA, 306); + Actor_Set_Targetable(kActorFreeSlotA, 1); + } + } + } + return true; + + case 5: + if (Actor_Query_Goal_Number(kActorFreeSlotA) < 400) { + AI_Movement_Track_Flush(kActorFreeSlotA); + Actor_Set_Goal_Number(kActorFreeSlotA, 400); + } else if (Actor_Query_Goal_Number(kActorFreeSlotA) == 405 && Actor_Query_Which_Set_In(kActorMcCoy) == kSceneKP05) { + Actor_Set_Targetable(kActorFreeSlotA, 1); + Actor_Set_Goal_Number(kActorFreeSlotA, 406); + } + return true; + + default: + return false; + } +} + +void AIScriptFreeSlotA::TimerExpired(int timer) { + //return false; +} + +void AIScriptFreeSlotA::CompletedMovementTrack() { + switch (Actor_Query_Goal_Number(kActorFreeSlotA)) { + case 301: + Actor_Set_Goal_Number(kActorFreeSlotA, 302); + break; + + case 302: + Actor_Set_Goal_Number(kActorFreeSlotA, 303); + break; + + case 303: + Actor_Set_Goal_Number(kActorFreeSlotA, 300); + break; + + case 306: + Actor_Set_Goal_Number(kActorFreeSlotA, 307); + break; + + case 307: + Actor_Set_Goal_Number(kActorFreeSlotA, 306); + break; + + case 400: + Actor_Set_Goal_Number(kActorFreeSlotA, 405); + break; + + case 406: + Non_Player_Actor_Combat_Mode_On(kActorFreeSlotA, 0, 0, 0, 8, 4, 7, 8, 0, 0, 100, 5, 300, 0); + break; + + default: + return; //false; + } + + return; //true; +} + +void AIScriptFreeSlotA::ReceivedClue(int clueId, int fromActorId) { + //return false; +} + +void AIScriptFreeSlotA::ClickedByPlayer() { + if (Actor_Query_Goal_Number(kActorFreeSlotA) != 599) { + return; //false; + } + + Actor_Face_Actor(kActorMcCoy, kActorFreeSlotA, 1); + if (Random_Query(1, 2) == 1) { + Actor_Says(kActorMcCoy, 8655, 16); + } else { + Actor_Says(kActorMcCoy, 8665, 16); + } +} + +void AIScriptFreeSlotA::EnteredScene(int sceneId) { + // return false; +} + +void AIScriptFreeSlotA::OtherAgentEnteredThisScene(int otherActorId) { + // return false; +} + +void AIScriptFreeSlotA::OtherAgentExitedThisScene(int otherActorId) { + // return false; +} + +void AIScriptFreeSlotA::OtherAgentEnteredCombatMode(int otherActorId, int combatMode) { + // return false; +} + +void AIScriptFreeSlotA::ShotAtAndMissed() { + if (Actor_Query_In_Set(kActorFreeSlotA, kSetUG15) == 1) + calcHit(); +} + +bool AIScriptFreeSlotA::ShotAtAndHit() { + if (Actor_Query_In_Set(kActorFreeSlotA, kSetUG15) == 1) { + calcHit(); + Actor_Set_Goal_Number(kActorFreeSlotA, 305); + return true; + } + + return false; +} + +void AIScriptFreeSlotA::Retired(int byActorId) { + Actor_Set_Goal_Number(kActorFreeSlotA, 599); +} + +int AIScriptFreeSlotA::GetFriendlinessModifierIfGetsClue(int otherActorId, int clueId) { + return 0; +} + +bool AIScriptFreeSlotA::GoalChanged(int currentGoalNumber, int newGoalNumber) { + switch (newGoalNumber) { + case 300: + AI_Movement_Track_Flush(kActorFreeSlotA); + Actor_Change_Animation_Mode(kActorFreeSlotA, 0); + Actor_Set_Targetable(kActorFreeSlotA, 0); + break; + + case 301: + Actor_Force_Stop_Walking(kActorMcCoy); + AI_Movement_Track_Flush(kActorFreeSlotA); + World_Waypoint_Set(444, 87, -48.75f, 44.66f, 87.57f); + AI_Movement_Track_Append(kActorFreeSlotA, 444, 1); + AI_Movement_Track_Repeat(kActorFreeSlotA); + break; + + case 302: + AI_Movement_Track_Flush(kActorFreeSlotA); + World_Waypoint_Set(444, 87, -237.0f, 48.07f, 208.0f); + AI_Movement_Track_Append(kActorFreeSlotA, 444, 1); + AI_Movement_Track_Repeat(kActorFreeSlotA); + Actor_Set_Targetable(kActorFreeSlotA, 1); + break; + + case 303: + AI_Movement_Track_Flush(kActorFreeSlotA); + World_Waypoint_Set(444, 87, 3.52f, 52.28f, 90.68f); + AI_Movement_Track_Append(kActorFreeSlotA, 444, 0); + AI_Movement_Track_Repeat(kActorFreeSlotA); + break; + + case 304: + Player_Loses_Control(); + Actor_Force_Stop_Walking(kActorMcCoy); + AI_Movement_Track_Flush(kActorFreeSlotA); + Actor_Face_Actor(kActorFreeSlotA, kActorMcCoy, 1); + Actor_Change_Animation_Mode(kActorFreeSlotA, 6); + Actor_Change_Animation_Mode(kActorMcCoy, 48); + break; + + case 305: + AI_Movement_Track_Flush(kActorFreeSlotA); + Actor_Set_Targetable(kActorFreeSlotA, 0); + Game_Flag_Set(676); + _animationState = 7; + _animationFrame = 0; + break; + + case 306: + AI_Movement_Track_Flush(kActorFreeSlotA); + processGoal306(); + AI_Movement_Track_Repeat(kActorFreeSlotA); + break; + + case 307: + AI_Movement_Track_Flush(kActorFreeSlotA); + AI_Movement_Track_Append(kActorFreeSlotA, 39, 1); + AI_Movement_Track_Repeat(kActorFreeSlotA); + break; + + case 308: + Actor_Set_Targetable(kActorFreeSlotA, 1); + Non_Player_Actor_Combat_Mode_On(kActorFreeSlotA, 0, 0, 0, 8, 4, 7, 8, 25, 0, 75, 5, 300, 0); + break; + + case 309: + Actor_Force_Stop_Walking(kActorFreeSlotA); + AI_Movement_Track_Flush(kActorFreeSlotA); + _var4 = 52.46f; + _var3 = -4.0f; + _var5 = -10.0f; + if (_animationState != 7 && _animationState != 8) { + _animationState = 7; + _animationFrame = 0; + } + break; + + case 310: + AI_Movement_Track_Flush(kActorFreeSlotA); + Actor_Put_In_Set(kActorFreeSlotA, kSetUG15); + Actor_Set_At_XYZ(kActorFreeSlotA, 3.52f, 52.28f, 90.68f, 700); + Actor_Set_Goal_Number(kActorFreeSlotA, 300); + break; + + case 400: + AI_Movement_Track_Append(kActorFreeSlotA, 39, 0); + AI_Movement_Track_Repeat(kActorFreeSlotA); + break; + + case 406: + AI_Movement_Track_Flush(kActorFreeSlotA); + AI_Movement_Track_Append(kActorFreeSlotA, 525, 0); + AI_Movement_Track_Repeat(kActorFreeSlotA); + break; + + case 599: + Actor_Set_Health(kActorFreeSlotA, 20, 20); + Actor_Set_Friendliness_To_Other(kActorFreeSlotA, kActorMcCoy, 40); + break; + + default: + return false; + } + + return true; +} + +bool AIScriptFreeSlotA::UpdateAnimation(int *animation, int *frame) { + switch (_animationState) { + case 0: + *animation = 861; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(861)) { + _animationFrame = 0; + } + break; + + case 1: + *animation = 862; + if (_var1) { + _var1--; + } else { + _animationFrame += _var2; + if (_animationFrame < 8) { + _var2 = 1; + } else { + if (_animationFrame > 8) { + _var2 = -1; + } else if (Random_Query(0, 4)) { + _var2 = -_var2; + } + } + if (_animationFrame >= 7 && _animationFrame <= 9) { + _var1 = Random_Query(0, 1); + } + } + break; + + case 2: + *animation = 862; + _animationFrame++; + if (_animationFrame > Slice_Animation_Query_Number_Of_Frames(862) - 1) { + *animation = 861; + _animationFrame = 0; + _animationState = 0; + } + break; + + case 3: + *animation = 858; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(858)) { + _animationFrame = 0; + } + break; + + case 4: + *animation = 857; + _animationFrame++; + if (_animationFrame == 1) { + int snd; + if (Random_Query(1, 2) == 1) { + snd = 9010; + } else { + snd = 9015; + } + Sound_Play_Speech_Line(64, snd, 75, 0, 99); + } + if (_animationFrame == 3) { + Ambient_Sounds_Play_Sound(438, 99, 0, 0, 20); + Actor_Combat_AI_Hit_Attempt(kActorFreeSlotA); + } + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(857)) { + _animationState = 0; + _animationFrame = 0; + Actor_Change_Animation_Mode(kActorFreeSlotA, 4); + } + break; + + case 5: + *animation = 874; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(874) - 1) { // bug? shuld not be '-1' + Actor_Change_Animation_Mode(kActorFreeSlotA, 0); + } + break; + + case 6: + if (_animationFrame == 1) { + Ambient_Sounds_Play_Sound(437, 99, 0, 0, 20); + } + *animation = 860; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(860)) { + _animationFrame = 0; + _animationState = 0; + Actor_Change_Animation_Mode(kActorFreeSlotA, 0); + } + break; + + case 7: + *animation = 859; + _animationFrame++; + if (_animationFrame == 0) { + Ambient_Sounds_Play_Sound(439, 99, 0, 0, 25); + } + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(859) - 1) { // bug? shuld not be '-1' + _animationFrame = Slice_Animation_Query_Number_Of_Frames(859) - 1; + _animationState = 8; + Actor_Set_Goal_Number(kActorFreeSlotA, 599); + } + break; + + case 8: + *animation = 859; + _animationFrame = Slice_Animation_Query_Number_Of_Frames(859) - 1; + break; + + default: + break; + } + *frame = _animationFrame; + return true; +} + +bool AIScriptFreeSlotA::ChangeAnimationMode(int mode) { + switch (mode) { + case 0: + if ((unsigned int)(_animationState - 1) > 1) { + _animationState = 0; + _animationFrame = 0; + } else if (_animationState == 1) { + _animationState = 2; + } + break; + + case 1: + _animationState = 3; + _animationFrame = 0; + break; + + case 4: + if ((unsigned int)(_animationState - 1) > 1) { + _animationState = 0; + _animationFrame = 0; + } else if (_animationState == 1) { + _animationState = 2; + } + break; + + case 6: + _animationState = 4; + _animationFrame = 0; + break; + + case 7: + _animationState = 3; + _animationFrame = 0; + break; + + case 8: + _animationState = 3; + _animationFrame = 0; + break; + + case 21: + _animationState = 6; + _animationFrame = 0; + break; + + case 43: + _animationState = 1; + _animationFrame = 0; + break; + + case 48: + _animationState = 7; + _animationFrame = 0; + break; + } + return true; +} + +void AIScriptFreeSlotA::QueryAnimationState(int *animationState, int *animationFrame, int *animationStateNext, int *animationNext) { + *animationState = _animationState; + *animationFrame = _animationFrame; + *animationStateNext = _animationStateNext; + *animationNext = _animationNext; +} + +void AIScriptFreeSlotA::SetAnimationState(int animationState, int animationFrame, int animationStateNext, int animationNext) { + _animationState = animationState; + _animationFrame = animationFrame; + _animationStateNext = animationStateNext; + _animationNext = animationNext; +} + +bool AIScriptFreeSlotA::ReachedMovementTrackWaypoint(int waypointId) { + if (Actor_Query_Which_Set_In(kActorFreeSlotA) == kSetUG01) { + if (waypointId == 465) { + Actor_Change_Animation_Mode(kActorFreeSlotA, 43); + } + } else if (Actor_Query_Goal_Number(kActorFreeSlotA) == 302) { + Actor_Face_Actor(kActorFreeSlotA, kActorMcCoy, 1); + } + + return true; +} + +void AIScriptFreeSlotA::FledCombat() { + // return false; +} + +void AIScriptFreeSlotA::calcHit() { + float x, y, z; + + Actor_Query_XYZ(kActorFreeSlotA, &x, &y, &z); + + if (x >= -30.0f && x < -150.0f) { + Game_Flag_Set(677); + } +} + +void AIScriptFreeSlotA::processGoal306() { + switch (Random_Query(1, 14)) { + case 1: + AI_Movement_Track_Append(kActorFreeSlotA, 450, 1); + AI_Movement_Track_Append(kActorFreeSlotA, 451, 5); + AI_Movement_Track_Append(kActorFreeSlotA, 450, 0); + break; + + case 2: + World_Waypoint_Set(463, 74, 144.98f, -50.13f, -175.75f); + World_Waypoint_Set(464, 74, 105.6f, -50.13f, -578.46f); + World_Waypoint_Set(465, 74, 62.0f, -50.13f, -574.0f); + AI_Movement_Track_Append(kActorFreeSlotA, 463, 1); + AI_Movement_Track_Append(kActorFreeSlotA, 464, 1); + AI_Movement_Track_Append(kActorFreeSlotA, 465, 5); + AI_Movement_Track_Append(kActorFreeSlotA, 463, 5); + break; + + case 3: + AI_Movement_Track_Append(kActorFreeSlotA, 446, 15); + AI_Movement_Track_Append(kActorFreeSlotA, 447, 1); + AI_Movement_Track_Append(kActorFreeSlotA, 449, 1); + AI_Movement_Track_Append(kActorFreeSlotA, 448, 2); + AI_Movement_Track_Append(kActorFreeSlotA, 449, 0); + break; + + case 4: + World_Waypoint_Set(463, 77, -22.7f, 6.39f, 33.12f); + World_Waypoint_Set(464, 77, -6.70f, -1.74f, -362.88f); + World_Waypoint_Set(465, 77, 164.0f, 11.87f, -1013.0f); + AI_Movement_Track_Append(kActorFreeSlotA, 463, 2); + AI_Movement_Track_Append(kActorFreeSlotA, 464, 0); + AI_Movement_Track_Append(kActorFreeSlotA, 465, 0); + break; + + case 5: + AI_Movement_Track_Append(kActorFreeSlotA, 457, 15); + AI_Movement_Track_Append(kActorFreeSlotA, 458, 0); + AI_Movement_Track_Append(kActorFreeSlotA, 459, 15); + break; + + case 6: + AI_Movement_Track_Append(kActorFreeSlotA, 460, 15); + AI_Movement_Track_Append(kActorFreeSlotA, 461, 5); + AI_Movement_Track_Append(kActorFreeSlotA, 460, 15); + break; + + case 7: + if (Actor_Query_In_Set(kActorClovis, kSetUG07)) { + AI_Movement_Track_Append(kActorFreeSlotA, 39, 10); + } else { + World_Waypoint_Set(463, 80, -88.78f, -12.21f, -184.08f); + World_Waypoint_Set(464, 80, 250.0f, -12.21f, -342.0f); + World_Waypoint_Set(465, 80, -164.78f, -12.21f, -832.08f); + AI_Movement_Track_Append(kActorFreeSlotA, 463, 5); + AI_Movement_Track_Append(kActorFreeSlotA, 464, 1); + } + break; + + case 8: + World_Waypoint_Set(463, 80, -88.78f, -12.21f, -184.08f); + World_Waypoint_Set(464, 80, 250.0f, -12.21f, -342.0f); + World_Waypoint_Set(465, 80, -164.78f, -12.21f, -832.08f); + AI_Movement_Track_Append(kActorFreeSlotA, 464, 5); + AI_Movement_Track_Append(kActorFreeSlotA, 463, 1); + break; + + case 9: + World_Waypoint_Set(463, 80, -88.78f, -12.21f, -184.08f); + World_Waypoint_Set(464, 80, 250.0f, -12.21f, -342.0f); + World_Waypoint_Set(465, 80, -164.78f, -12.21f, -832.08f); + AI_Movement_Track_Append(kActorFreeSlotA, 464, 5); + AI_Movement_Track_Append(kActorFreeSlotA, 465, 1); + break; + + case 10: + World_Waypoint_Set(463, 80, -88.78f, -12.21f, -184.08f); + World_Waypoint_Set(464, 80, 250.0f, -12.21f, -342.0f); + World_Waypoint_Set(465, 80, -164.78f, -12.21f, -832.08f); + AI_Movement_Track_Append(kActorFreeSlotA, 465, 5); + AI_Movement_Track_Append(kActorFreeSlotA, 464, 1); + break; + + case 11: + World_Waypoint_Set(463, 82, 91.0f, 156.94f, -498.0f); + World_Waypoint_Set(464, 82, -149.0f, 156.94f, -498.0f); + AI_Movement_Track_Append(kActorFreeSlotA, 463, 5); + AI_Movement_Track_Append(kActorFreeSlotA, 464, 1); + break; + + case 12: + World_Waypoint_Set(463, 82, 91.0f, 156.94f, -498.0f); + World_Waypoint_Set(464, 82, -149.0f, 156.94f, -498.0f); + AI_Movement_Track_Append(kActorFreeSlotA, 464, 5); + AI_Movement_Track_Append(kActorFreeSlotA, 463, 1); + break; + + case 13: + World_Waypoint_Set(463, 82, -152.51f, 277.31f, 311.98f); + World_Waypoint_Set(464, 82, -124.51f, 275.08f, 319.98f); + AI_Movement_Track_Append(kActorFreeSlotA, 463, 1); + AI_Movement_Track_Append(kActorFreeSlotA, 464, 8); + AI_Movement_Track_Append(kActorFreeSlotA, 463, 1); + break; + + case 14: + World_Waypoint_Set(463, 84, -360.67f, 21.39f, 517.55f); + World_Waypoint_Set(464, 84, -250.67f, 21.39f, 477.55f); + World_Waypoint_Set(465, 84, -248.67f, 21.39f, -1454.45f); + AI_Movement_Track_Append(kActorFreeSlotA, 463, 1); + AI_Movement_Track_Append(kActorFreeSlotA, 464, 8); + AI_Movement_Track_Append(kActorFreeSlotA, 465, 1); + break; + + default: + AI_Movement_Track_Append(kActorFreeSlotA, 39, Random_Query(1, 10)); + break; + } +} + +} // End of namespace BladeRunner diff --git a/engines/bladerunner/script/ai/free_slot_b.cpp b/engines/bladerunner/script/ai/free_slot_b.cpp new file mode 100644 index 0000000000..431b27a062 --- /dev/null +++ b/engines/bladerunner/script/ai/free_slot_b.cpp @@ -0,0 +1,541 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can 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 "bladerunner/script/ai_script.h" + +namespace BladeRunner { + +AIScriptFreeSlotB::AIScriptFreeSlotB(BladeRunnerEngine *vm) : AIScriptBase(vm) { + _var1 = 0; + _var2 = 1; +} + +void AIScriptFreeSlotB::Initialize() { + _animationFrame = 0; + _animationState = 0; + _animationStateNext = 0; + _animationNext = 0; + + _var1 = 0; + _var2 = 1; + + World_Waypoint_Set(527, 45, -468.46f, -616.58f, 2840.60f); + World_Waypoint_Set(528, 45, -1024.46f, -615.49f, 2928.60f); + World_Waypoint_Set(529, 45, -1024.46f, -615.49f, 2788.60f); +} + +bool AIScriptFreeSlotB::Update() { + if (Global_Variable_Query(kVariableChapter) > 5) { + return false; + } + + if (Global_Variable_Query(kVariableChapter) == 4) { + switch (Actor_Query_Goal_Number(kActorFreeSlotB)) { + case 300: + Actor_Set_Goal_Number(kActorFreeSlotB, 301); + Actor_Set_Targetable(kActorFreeSlotB, 1); + break; + + case 301: + if (Actor_Query_Which_Set_In(kActorFreeSlotB) == Player_Query_Current_Set() + && Actor_Query_Inch_Distance_From_Actor(kActorFreeSlotB, kActorMcCoy) <= 48) { + Actor_Set_Goal_Number(kActorFreeSlotB, 302); + } + break; + + case 302: + if (Actor_Query_Which_Set_In(kActorFreeSlotB) != Player_Query_Current_Set()) { + Actor_Set_Goal_Number(kActorFreeSlotB, 301); + } + break; + + case 599: + if (Actor_Query_Which_Set_In(kActorFreeSlotB) != Player_Query_Current_Set()) { + Actor_Set_Goal_Number(kActorFreeSlotB, 300); + } + break; + + default: + Actor_Set_Goal_Number(kActorFreeSlotB, 300); + break; + } + + return false; + } + if (Actor_Query_Goal_Number(kActorFreeSlotB) < 400) { + AI_Movement_Track_Flush(kActorFreeSlotB); + Actor_Set_Goal_Number(kActorFreeSlotB, 400); + return true; + } else { + if (Actor_Query_Goal_Number(kActorFreeSlotB) != 405 || Actor_Query_Which_Set_In(kActorMcCoy) != kSetKP02) { + if (Actor_Query_Goal_Number(kActorFreeSlotB) == 599) { + if (Actor_Query_Which_Set_In(kActorFreeSlotB) != Player_Query_Current_Set()) { + Non_Player_Actor_Combat_Mode_Off(kActorFreeSlotB); + Actor_Set_Goal_Number(kActorFreeSlotB, 400); + return true; + } + } + return false; + } + Actor_Set_Goal_Number(kActorFreeSlotB, 406); + Actor_Set_Targetable(kActorFreeSlotB, 1); + return true; + } +} + +void AIScriptFreeSlotB::TimerExpired(int timer) { + //return false; +} + +void AIScriptFreeSlotB::CompletedMovementTrack() { + switch (Actor_Query_Goal_Number(kActorFreeSlotB)) { + case 300: + Actor_Set_Goal_Number(kActorFreeSlotB, 301); + break; + + case 301: + Actor_Set_Goal_Number(kActorFreeSlotB, 300); + break; + + case 400: + Actor_Set_Goal_Number(kActorFreeSlotB, 405); + break; + + case 406: + Non_Player_Actor_Combat_Mode_On(kActorFreeSlotB, 0, 0, 0, 8, 4, 7, 8, 0, 0, 100, 5, 300, 0); + break; + + default: + return; //false; + } + + return; //true; +} + +void AIScriptFreeSlotB::ReceivedClue(int clueId, int fromActorId) { + //return false; +} + +void AIScriptFreeSlotB::ClickedByPlayer() { + if (Actor_Query_Goal_Number(kActorFreeSlotB) != 599) + return; //false; + + Actor_Face_Actor(kActorMcCoy, kActorFreeSlotB, 1); + if (Random_Query(1, 2) == 1) { + Actor_Says(kActorMcCoy, 8655, 16); + } else { + Actor_Says(kActorMcCoy, 8665, 16); + } +} + +void AIScriptFreeSlotB::EnteredScene(int sceneId) { + // return false; +} + +void AIScriptFreeSlotB::OtherAgentEnteredThisScene(int otherActorId) { + // return false; +} + +void AIScriptFreeSlotB::OtherAgentExitedThisScene(int otherActorId) { + // return false; +} + +void AIScriptFreeSlotB::OtherAgentEnteredCombatMode(int otherActorId, int combatMode) { + // return false; +} + +void AIScriptFreeSlotB::ShotAtAndMissed() { + // return false; +} + +bool AIScriptFreeSlotB::ShotAtAndHit() { + return false; +} + +void AIScriptFreeSlotB::Retired(int byActorId) { + Actor_Set_Goal_Number(kActorFreeSlotB, 599); +} + +int AIScriptFreeSlotB::GetFriendlinessModifierIfGetsClue(int otherActorId, int clueId) { + return 0; +} + +bool AIScriptFreeSlotB::GoalChanged(int currentGoalNumber, int newGoalNumber) { + switch (newGoalNumber) { + case 300: + AI_Movement_Track_Flush(kActorFreeSlotB); + AI_Movement_Track_Append(kActorFreeSlotB, 39, 2); + AI_Movement_Track_Repeat(kActorFreeSlotB); + break; + + case 301: + AI_Movement_Track_Flush(kActorFreeSlotB); + processGoal301(); + AI_Movement_Track_Repeat(kActorFreeSlotB); + break; + + case 302: + Actor_Set_Targetable(kActorFreeSlotB, 1); + Non_Player_Actor_Combat_Mode_On(kActorFreeSlotB, 0, 0, 0, 8, 4, 7, 8, 25, 0, 75, 5, 300, 0); + break; + + case 400: + AI_Movement_Track_Append(kActorFreeSlotB, 39, 0); + AI_Movement_Track_Repeat(kActorFreeSlotB); + break; + + case 406: + AI_Movement_Track_Flush(kActorFreeSlotB); + AI_Movement_Track_Append(kActorFreeSlotB, 527, 0); + AI_Movement_Track_Repeat(kActorFreeSlotB); + break; + + case 599: + Actor_Set_Health(kActorFreeSlotB, 20, 20); + break; + + default: + return false; + } + + return true; +} + +bool AIScriptFreeSlotB::UpdateAnimation(int *animation, int *frame) { + switch (_animationState) { + case 0: + *animation = 861; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(861)) { + _animationFrame = 0; + } + break; + + case 1: + *animation = 862; + if (_var1) { + _var1--; + } else { + _animationFrame += _var2; + if (_animationFrame < 8) { + _var2 = 1; + } else { + if (_animationFrame > 8) { + _var2 = -1; + } else if (Random_Query(0, 4)) { + _var2 = -_var2; + } + } + if (_animationFrame >= 7 && _animationFrame <= 9) { + _var1 = Random_Query(0, 1); + } + } + break; + + case 2: + *animation = 862; + _animationFrame++; + if (_animationFrame > Slice_Animation_Query_Number_Of_Frames(862) - 1) { + *animation = 861; + _animationFrame = 0; + _animationState = 0; + } + break; + + case 3: + *animation = 858; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(858)) { + _animationFrame = 0; + } + break; + + case 4: + *animation = 857; + _animationFrame++; + if (_animationFrame == 3) { + int snd; + if (Random_Query(1, 2) == 1) { + snd = 9010; + } else { + snd = 9015; + } + Sound_Play_Speech_Line(kActorFreeSlotB, snd, 75, 0, 99); + } + if (_animationFrame == 3) { + Actor_Combat_AI_Hit_Attempt(kActorFreeSlotB); + } + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(*animation)) { + *animation = 861; + _animationFrame = 0; + _animationState = 0; + } + break; + + case 5: + *animation = 874; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(874) - 1) { + _animationState = 8; + _animationFrame = Slice_Animation_Query_Number_Of_Frames(874) - 1; + } + break; + + case 6: + if (_animationFrame == 1) { + Ambient_Sounds_Play_Sound(437, 99, 0, 0, 20); + } + *animation = 860; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(860)) { + _animationFrame = 0; + _animationState = 0; + Actor_Change_Animation_Mode(kActorFreeSlotB, 0); + } + break; + + case 7: + *animation = 859; + _animationFrame++; + if (_animationFrame == 1) { + Ambient_Sounds_Play_Sound(439, 99, 0, 0, 25); + } + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(859)) { + _animationFrame = Slice_Animation_Query_Number_Of_Frames(859); + } + _animationState = 8; + break; + + case 8: + *animation = 859; + _animationFrame = Slice_Animation_Query_Number_Of_Frames(859) - 1; + break; + + default: + break; + } + *frame = _animationFrame; + return true; +} + +bool AIScriptFreeSlotB::ChangeAnimationMode(int mode) { + switch (mode) { + case 0: + if ((unsigned int)(_animationState - 1) > 1) { + _animationState = 0; + _animationFrame = 0; + } else if (_animationState == 1) { + _animationState = 2; + } + break; + + case 1: + _animationState = 3; + _animationFrame = 0; + break; + + case 4: + if ((unsigned int)(_animationState - 1) > 1) { + _animationState = 0; + _animationFrame = 0; + } else if (_animationState == 1) { + _animationState = 2; + } + break; + + case 6: + _animationState = 4; + _animationFrame = 0; + break; + + case 7: + _animationState = 3; + _animationFrame = 0; + break; + + case 8: + _animationState = 3; + _animationFrame = 0; + break; + + case 21: + _animationState = 6; + _animationFrame = 0; + break; + + case 43: + _animationState = 1; + _animationFrame = 0; + break; + + case 48: + _animationState = 7; + _animationFrame = 0; + break; + } + + return true; +} + +void AIScriptFreeSlotB::QueryAnimationState(int *animationState, int *animationFrame, int *animationStateNext, int *animationNext) { + *animationState = _animationState; + *animationFrame = _animationFrame; + *animationStateNext = _animationStateNext; + *animationNext = _animationNext; +} + +void AIScriptFreeSlotB::SetAnimationState(int animationState, int animationFrame, int animationStateNext, int animationNext) { + _animationState = animationState; + _animationFrame = animationFrame; + _animationStateNext = animationStateNext; + _animationNext = animationNext; +} + +bool AIScriptFreeSlotB::ReachedMovementTrackWaypoint(int waypointId) { + return true; +} + +void AIScriptFreeSlotB::FledCombat() { + // return false; +} + +void AIScriptFreeSlotB::processGoal301() { + switch (Random_Query(1, 14)) { + case 1: + AI_Movement_Track_Append(kActorFreeSlotB, 450, 1); + AI_Movement_Track_Append(kActorFreeSlotB, 451, 5); + AI_Movement_Track_Append(kActorFreeSlotB, 450, 0); + break; + + case 2: + World_Waypoint_Set(466, 74, 144.98f, -50.13f, -175.75f); + World_Waypoint_Set(547, 74, 105.6f, -50.13f, -578.46f); + World_Waypoint_Set(548, 74, 62.0f, -50.13f, -574.0f); + AI_Movement_Track_Append(kActorFreeSlotB, 466, 1); + AI_Movement_Track_Append(kActorFreeSlotB, 547, 1); + AI_Movement_Track_Append(kActorFreeSlotB, 548, 5); + AI_Movement_Track_Append(kActorFreeSlotB, 466, 5); + break; + + case 3: + AI_Movement_Track_Append(kActorFreeSlotB, 446, 15); + AI_Movement_Track_Append(kActorFreeSlotB, 447, 1); + AI_Movement_Track_Append(kActorFreeSlotB, 449, 1); + AI_Movement_Track_Append(kActorFreeSlotB, 448, 2); + AI_Movement_Track_Append(kActorFreeSlotB, 449, 0); + break; + + case 4: + World_Waypoint_Set(466, 77, -22.70f, 6.39f, 33.12f); + World_Waypoint_Set(547, 77, -6.70f, -1.74f, -362.88f); + World_Waypoint_Set(548, 77, 164.0f, 11.87f, -1013.0f); + AI_Movement_Track_Append(kActorFreeSlotB, 466, 2); + AI_Movement_Track_Append(kActorFreeSlotB, 547, 0); + AI_Movement_Track_Append(kActorFreeSlotB, 548, 0); + break; + + case 5: + AI_Movement_Track_Append(kActorFreeSlotB, 457, 15); + AI_Movement_Track_Append(kActorFreeSlotB, 458, 0); + AI_Movement_Track_Append(kActorFreeSlotB, 459, 15); + break; + + case 6: + AI_Movement_Track_Append(kActorFreeSlotB, 460, 15); + AI_Movement_Track_Append(kActorFreeSlotB, 461, 5); + AI_Movement_Track_Append(kActorFreeSlotB, 460, 15); + break; + + case 7: + if (Actor_Query_In_Set(kActorClovis, kSetUG07)) { + AI_Movement_Track_Append(kActorFreeSlotB, 39, 10); + } else { + World_Waypoint_Set(466, 80, -88.78f, -12.21f, -184.08f); + World_Waypoint_Set(547, 80, 250.0f, -12.21f, -342.0f); + World_Waypoint_Set(548, 80, -164.78f, -12.21f, -832.08f); + AI_Movement_Track_Append(kActorFreeSlotB, 466, 5); + AI_Movement_Track_Append(kActorFreeSlotB, 547, 1); + } + break; + + case 8: + World_Waypoint_Set(466, 80, -88.78f, -12.21f, -184.08f); + World_Waypoint_Set(547, 80, 250.0f, -12.21f, -342.0f); + World_Waypoint_Set(548, 80, -164.78f, -12.21f, -832.08f); + AI_Movement_Track_Append(kActorFreeSlotB, 547, 5); + AI_Movement_Track_Append(kActorFreeSlotB, 466, 1); + break; + + case 9: + World_Waypoint_Set(466, 80, -88.78f, -12.21f, -184.08f); + World_Waypoint_Set(547, 80, 250.0f, -12.21f, -342.0f); + World_Waypoint_Set(548, 80, -164.78f, -12.21f, -832.08f); + AI_Movement_Track_Append(kActorFreeSlotB, 547, 5); + AI_Movement_Track_Append(kActorFreeSlotB, 548, 1); + break; + + case 10: + World_Waypoint_Set(466, 80, -88.78f, -12.21f, -184.08f); + World_Waypoint_Set(547, 80, 250.0f, -12.21f, -342.0f); + World_Waypoint_Set(548, 80, -164.78f, -12.21f, -832.08f); + AI_Movement_Track_Append(kActorFreeSlotB, 548, 5); + AI_Movement_Track_Append(kActorFreeSlotB, 547, 1); + break; + + case 11: + World_Waypoint_Set(466, 82, 91.0f, 156.94f, -498.0f); + World_Waypoint_Set(547, 82, -149.0f, 156.94f, -498.0f); + AI_Movement_Track_Append(kActorFreeSlotB, 466, 5); + AI_Movement_Track_Append(kActorFreeSlotB, 547, 1); + break; + + case 12: + World_Waypoint_Set(466, 82, 91.0f, 156.94f, -498.0f); + World_Waypoint_Set(547, 82, -149.0f, 156.94f, -498.0f); + AI_Movement_Track_Append(kActorFreeSlotB, 547, 5); + AI_Movement_Track_Append(kActorFreeSlotB, 466, 1); + break; + + case 13: + World_Waypoint_Set(466, 82, -152.51f, 277.31f, 311.98f); + World_Waypoint_Set(547, 82, -124.51f, 275.08f, 319.98f); + AI_Movement_Track_Append(kActorFreeSlotB, 466, 1); + AI_Movement_Track_Append(kActorFreeSlotB, 547, 8); + AI_Movement_Track_Append(kActorFreeSlotB, 466, 1); + break; + + case 14: + World_Waypoint_Set(466, 84, -360.67f, 21.39f, 517.55f); + World_Waypoint_Set(547, 84, -250.67f, 21.39f, 477.55f); + World_Waypoint_Set(548, 84, -248.67f, 21.39f, -1454.45f); + AI_Movement_Track_Append(kActorFreeSlotB, 466, 1); + AI_Movement_Track_Append(kActorFreeSlotB, 547, 8); + AI_Movement_Track_Append(kActorFreeSlotB, 548, 1); + break; + + default: + AI_Movement_Track_Append(kActorFreeSlotB, 39, Random_Query(1, 10)); + break; + } +} + +} // End of namespace BladeRunner diff --git a/engines/bladerunner/script/ai/gaff.cpp b/engines/bladerunner/script/ai/gaff.cpp index c26f0d907f..c5e629cab0 100644 --- a/engines/bladerunner/script/ai/gaff.cpp +++ b/engines/bladerunner/script/ai/gaff.cpp @@ -287,7 +287,7 @@ bool AIScriptGaff::GoalChanged(int currentGoalNumber, int newGoalNumber) { return true; case 303: Actor_Face_Actor(kActorGaff, kActorMcCoy, 1); - Actor_Change_Animation_Mode(kActorGaff, kAnimationModeCombatShoot); + Actor_Change_Animation_Mode(kActorGaff, kAnimationModeCombatAttack); Sound_Play(27, 100, 0, 0, 50); Actor_Change_Animation_Mode(kActorMcCoy, 48); Actor_Retired_Here(kActorMcCoy, 12, 12, 1, -1); diff --git a/engines/bladerunner/script/ai/generic_walker_a.cpp b/engines/bladerunner/script/ai/generic_walker_a.cpp index 7acea89c25..0ba48811ed 100644 --- a/engines/bladerunner/script/ai/generic_walker_a.cpp +++ b/engines/bladerunner/script/ai/generic_walker_a.cpp @@ -354,8 +354,7 @@ bool AIScriptGenericWalkerA::prepareWalker() { } else { model = Random_Query(0, 5); } - } - while (model == Global_Variable_Query(kVariableGenericWalkerBModel) || model == Global_Variable_Query(kVariableGenericWalkerCModel)); + } while (model == Global_Variable_Query(kVariableGenericWalkerBModel) || model == Global_Variable_Query(kVariableGenericWalkerCModel)); Global_Variable_Set(kVariableGenericWalkerAModel, model); Game_Flag_Set(kFlagGenericWalkerWaiting); @@ -459,7 +458,7 @@ bool AIScriptGenericWalkerA::preparePath() { int waypointEnd = 0; do { waypointStart = Random_Query(167, 171); - } while (waypointEnd == 168 || waypointEnd == 169); + } while (waypointStart == 168 || waypointStart == 169); do { waypointEnd = Random_Query(167, 171); } while (waypointEnd == waypointStart || waypointEnd == 168 || waypointEnd == 169); diff --git a/engines/bladerunner/script/ai/generic_walker_b.cpp b/engines/bladerunner/script/ai/generic_walker_b.cpp index a2ff58113f..2a6c3a1732 100644 --- a/engines/bladerunner/script/ai/generic_walker_b.cpp +++ b/engines/bladerunner/script/ai/generic_walker_b.cpp @@ -330,8 +330,7 @@ bool AIScriptGenericWalkerB::prepareWalker() { } else { model = Random_Query(0, 5); } - } - while (model == Global_Variable_Query(kVariableGenericWalkerAModel) || model == Global_Variable_Query(kVariableGenericWalkerCModel)); + } while (model == Global_Variable_Query(kVariableGenericWalkerAModel) || model == Global_Variable_Query(kVariableGenericWalkerCModel)); Global_Variable_Set(kVariableGenericWalkerBModel, model); Game_Flag_Set(kFlagGenericWalkerWaiting); @@ -435,7 +434,7 @@ bool AIScriptGenericWalkerB::preparePath() { int waypointEnd = 0; do { waypointStart = Random_Query(167, 171); - } while (waypointEnd == 168 || waypointEnd == 169); + } while (waypointStart == 168 || waypointStart == 169); do { waypointEnd = Random_Query(167, 171); } while (waypointEnd == waypointStart || waypointEnd == 168 || waypointEnd == 169); diff --git a/engines/bladerunner/script/ai/generic_walker_c.cpp b/engines/bladerunner/script/ai/generic_walker_c.cpp index f317d5ad49..5c0478c645 100644 --- a/engines/bladerunner/script/ai/generic_walker_c.cpp +++ b/engines/bladerunner/script/ai/generic_walker_c.cpp @@ -331,9 +331,8 @@ bool AIScriptGenericWalkerC::prepareWalker() { } else { model = Random_Query(0, 5); } - } // Here is probably bug in original code, because it not using kVariableGenericWalkerBModel but kVariableGenericWalkerCModel - while (model == Global_Variable_Query(kVariableGenericWalkerAModel) || model == Global_Variable_Query(kVariableGenericWalkerBModel)); + } while (model == Global_Variable_Query(kVariableGenericWalkerAModel) || model == Global_Variable_Query(kVariableGenericWalkerBModel)); Global_Variable_Set(kVariableGenericWalkerCModel, model); @@ -438,7 +437,7 @@ bool AIScriptGenericWalkerC::preparePath() { int waypointEnd = 0; do { waypointStart = Random_Query(167, 171); - } while (waypointEnd == 168 || waypointEnd == 169); + } while (waypointStart == 168 || waypointStart == 169); do { waypointEnd = Random_Query(167, 171); } while (waypointEnd == waypointStart || waypointEnd == 168 || waypointEnd == 169); diff --git a/engines/bladerunner/script/ai/gordo.cpp b/engines/bladerunner/script/ai/gordo.cpp index fd64112897..c44497176a 100644 --- a/engines/bladerunner/script/ai/gordo.cpp +++ b/engines/bladerunner/script/ai/gordo.cpp @@ -1157,7 +1157,7 @@ bool AIScriptGordo::ChangeAnimationMode(int mode) { break; } break; - case kAnimationModeCombatShoot: + case kAnimationModeCombatAttack: _animationState = 18; _animationFrame = 0; break; diff --git a/engines/bladerunner/script/ai/guzza.cpp b/engines/bladerunner/script/ai/guzza.cpp index 0f99fa8a46..5a4459535e 100644 --- a/engines/bladerunner/script/ai/guzza.cpp +++ b/engines/bladerunner/script/ai/guzza.cpp @@ -728,7 +728,7 @@ bool AIScriptGuzza::ChangeAnimationMode(int mode) { _animationFrame = 0; } break; - case kAnimationModeCombatShoot: + case kAnimationModeCombatAttack: _animationState = 31; _animationFrame = 0; break; diff --git a/engines/bladerunner/script/ai/hanoi.cpp b/engines/bladerunner/script/ai/hanoi.cpp new file mode 100644 index 0000000000..4b88be7c5f --- /dev/null +++ b/engines/bladerunner/script/ai/hanoi.cpp @@ -0,0 +1,740 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can 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 "bladerunner/script/ai_script.h" + +namespace BladeRunner { + +AIScriptHanoi::AIScriptHanoi(BladeRunnerEngine *vm) : AIScriptBase(vm) { + _var1 = 0; + _var2 = 0; + _var3 = 0; + _var4 = 1; +} + +void AIScriptHanoi::Initialize() { + _animationFrame = 0; + _animationState = 0; + _animationStateNext = 0; + _animationNext = 0; + + _var1 = 0; + _var2 = 0; + _var3 = 0; + _var4 = 1; + + Actor_Set_Goal_Number(kActorHanoi, 0); +} + +bool AIScriptHanoi::Update() { + if (Actor_Query_Goal_Number(kActorHolloway) == 240) { + AI_Countdown_Timer_Reset(kActorHanoi, 0); + } + if (Global_Variable_Query(kVariableChapter) == 3 && Actor_Query_Goal_Number(kActorHanoi) < 200) { + Actor_Set_Goal_Number(kActorHanoi, 210); + } + if (Player_Query_Current_Scene() != 56 && Actor_Query_Goal_Number(kActorHanoi) == 236) { + Actor_Set_Goal_Number(kActorHanoi, 210); + } + if (Player_Query_Current_Scene() == 56 + && Actor_Query_Goal_Number(kActorHanoi) != 215 + && Actor_Query_Goal_Number(kActorHanoi) != 230 + && Actor_Query_Goal_Number(kActorHanoi) != 235 + && Actor_Query_Goal_Number(kActorHanoi) != 236) { + if (Actor_Query_Inch_Distance_From_Waypoint(kActorMcCoy, 364) < 420) { + if (Actor_Query_Goal_Number(kActorHanoi) == 210) { + Actor_Set_Goal_Number(kActorHanoi, 211); + } + } else if (Actor_Query_Goal_Number(kActorHanoi) == 211) { + Actor_Set_Goal_Number(kActorHanoi, 210); + } + if (Actor_Query_Inch_Distance_From_Waypoint(kActorMcCoy, 361) < 240) { + if (Actor_Query_Goal_Number(kActorHanoi) == 210) { + Actor_Set_Goal_Number(kActorHanoi, 212); + } + } else if (Actor_Query_Goal_Number(kActorHanoi) == 212) { + Actor_Set_Goal_Number(kActorHanoi, 210); + } + if (Actor_Query_Inch_Distance_From_Actor(kActorMcCoy, kActorHysteriaPatron1) < 120 + && Actor_Query_Which_Set_In(kActorHanoi) == 55 + && Actor_Query_Goal_Number(kActorHanoi) != 213) { + Actor_Set_Goal_Number(kActorHanoi, 213); + } + } + + return false; +} + +void AIScriptHanoi::TimerExpired(int timer) { + if (timer == 0) { + if (Actor_Query_Goal_Number(kActorHanoi) == 215) { + Actor_Set_Goal_Number(kActorHanoi, 210); + return; //true; + } + + if (Actor_Query_Goal_Number(kActorHanoi) == 220) + return; //false; + + Actor_Set_Goal_Number(kActorHanoi, 202); + return; //true; + } + return; //false; +} + +void AIScriptHanoi::CompletedMovementTrack() { + switch (Actor_Query_Goal_Number(kActorHanoi)) { + case 235: + Actor_Set_Goal_Number(kActorHanoi, 236); + break; + + case 240: + Actor_Set_Goal_Number(kActorHanoi, 241); + break; + + case 202: + Actor_Says(kActorHanoi, 130, 3); + Actor_Says(kActorDektora, 540, 30); + Actor_Set_Goal_Number(kActorHanoi, 203); + break; + + case 203: + Actor_Face_Actor(kActorHanoi, kActorMcCoy, 1); + Actor_Face_Actor(kActorMcCoy, kActorHanoi, 1); + Actor_Change_Animation_Mode(kActorHanoi, 23); + Actor_Set_Invisible(kActorMcCoy, 1); + Actor_Says(kActorMcCoy, 3595, 3); + Actor_Says(kActorHanoi, 140, 3); + Actor_Set_Goal_Number(kActorHanoi, 220); + break; + + case 213: + Actor_Set_Goal_Number(kActorHanoi, 210); + break; + + default: + return; //false; + } + + return; //true; +} + +void AIScriptHanoi::ReceivedClue(int clueId, int fromActorId) { + //return false; +} + +void AIScriptHanoi::ClickedByPlayer() { + if (Actor_Query_Goal_Number(kActorHanoi) == 230 || Actor_Query_Goal_Number(kActorHanoi) == 235) { + Actor_Face_Actor(kActorMcCoy, kActorHanoi, 1); + Actor_Says(kActorMcCoy, 8915, 11); + + if (Actor_Query_Goal_Number(kActorHanoi) == 230) { + Actor_Says(kActorHanoi, 210, 3); + } + } +} + +void AIScriptHanoi::EnteredScene(int sceneId) { + // return false; +} + +void AIScriptHanoi::OtherAgentEnteredThisScene(int otherActorId) { + // return false; +} + +void AIScriptHanoi::OtherAgentExitedThisScene(int otherActorId) { + // return false; +} + +void AIScriptHanoi::OtherAgentEnteredCombatMode(int otherActorId, int combatMode) { + if (Player_Query_Current_Scene() != 56 || otherActorId || combatMode != 1) { + return; //false; + } + Player_Set_Combat_Mode(0); + Player_Loses_Control(); + Actor_Set_Goal_Number(kActorHanoi, 220); + + return; //true; +} + +void AIScriptHanoi::ShotAtAndMissed() { + // return false; +} + +bool AIScriptHanoi::ShotAtAndHit() { + return false; +} + +void AIScriptHanoi::Retired(int byActorId) { + // return false; +} + +int AIScriptHanoi::GetFriendlinessModifierIfGetsClue(int otherActorId, int clueId) { + return 0; +} + +bool AIScriptHanoi::GoalChanged(int currentGoalNumber, int newGoalNumber) { + if (!newGoalNumber) { + AI_Movement_Track_Flush(kActorHanoi); + AI_Movement_Track_Append(kActorHanoi, 39, 0); + AI_Movement_Track_Repeat(kActorHanoi); + + return true; + } + + switch (newGoalNumber) { + case 200: + AI_Countdown_Timer_Start(kActorHanoi, 0, 45); + break; + + case 201: + AI_Countdown_Timer_Reset(kActorHanoi, 0); + break; + + case 202: + if (Actor_Query_Which_Set_In(kActorMcCoy) == kSetNR07 && Actor_Query_In_Set(kActorDektora, kSetNR07)) { + Player_Loses_Control(); + Actor_Put_In_Set(kActorHanoi, kSetNR07); + Actor_Set_At_XYZ(kActorHanoi, -102.0f, -73.5f, -233.0f, 0); + Async_Actor_Walk_To_Waypoint(kActorMcCoy, 338, 0, 0); + AI_Movement_Track_Flush(kActorHanoi); + AI_Movement_Track_Append(kActorHanoi, 336, 1); + AI_Movement_Track_Repeat(kActorHanoi); + } else { + Actor_Set_Goal_Number(kActorHanoi, 0); + } + break; + + case 203: + if (Actor_Query_Which_Set_In(kActorMcCoy) != kSetNR07) { + return false; + } + AI_Movement_Track_Flush(kActorHanoi); + AI_Movement_Track_Append(kActorHanoi, 337, 0); + AI_Movement_Track_Repeat(kActorHanoi); + break; + + case 204: + Actor_Says(kActorHanoi, 210, 3); + Actor_Change_Animation_Mode(kActorHanoi, 23); + break; + + case 210: + AI_Movement_Track_Flush(kActorHanoi); + AI_Movement_Track_Append_With_Facing(kActorHanoi, 362, 0, 300); + AI_Movement_Track_Repeat(kActorHanoi); + break; + + case 211: + AI_Movement_Track_Flush(kActorHanoi); + AI_Movement_Track_Append_With_Facing(kActorHanoi, 363, 0, 500); + AI_Movement_Track_Repeat(kActorHanoi); + break; + + case 212: + AI_Movement_Track_Flush(kActorHanoi); + AI_Movement_Track_Append_With_Facing(kActorHanoi, 361, 0, 457); + AI_Movement_Track_Repeat(kActorHanoi); + break; + + case 213: + AI_Movement_Track_Flush(kActorHanoi); + AI_Movement_Track_Append_With_Facing(kActorHanoi, 365, Random_Query(15, 20), 600); + AI_Movement_Track_Repeat(kActorHanoi); + break; + + case 215: + Actor_Put_In_Set(kActorHanoi, kSetNR03); + Actor_Set_At_Waypoint(kActorHanoi, 362, 300); + AI_Countdown_Timer_Reset(kActorHanoi, 0); + AI_Countdown_Timer_Start(kActorHanoi, 0, 6); + break; + + case 220: + Game_Flag_Set(604); + AI_Countdown_Timer_Reset(kActorHanoi, 0); + Player_Loses_Control(); + Player_Set_Combat_Mode(0); + Actor_Force_Stop_Walking(kActorMcCoy); + Actor_Change_Animation_Mode(kActorMcCoy, 48); + Actor_Set_Invisible(kActorMcCoy, 1); + AI_Movement_Track_Flush(kActorHanoi); + Actor_Put_In_Set(kActorHanoi, kSetNR01); + Actor_Set_At_XYZ(kActorHanoi, -444.0f, 24.0f, -845.0f, 512); + Actor_Change_Animation_Mode(kActorHanoi, 78); + Set_Enter(kSetNR01, kSetNR01); + break; + + case 230: + AI_Movement_Track_Flush(kActorHanoi); + Actor_Put_In_Set(kActorHanoi, kSetNR05_NR08); + Actor_Set_At_XYZ(kActorHanoi, -1387.51f, 0.32f, 288.16f, 292); + break; + + case 235: + AI_Movement_Track_Flush(kActorHanoi); + AI_Movement_Track_Append(kActorHanoi, 439, 0); + AI_Movement_Track_Append(kActorHanoi, 39, 45); + AI_Movement_Track_Repeat(kActorHanoi); + break; + + case 236: + break; + + case 240: + Actor_Put_In_Set(kActorHanoi, kSetNR04); + Actor_Set_At_XYZ(kActorHanoi, -47.0f, 0.0f, 334.0f, 535); + AI_Movement_Track_Flush(kActorHanoi); + AI_Movement_Track_Append(kActorHanoi, 549, 0); + AI_Movement_Track_Repeat(kActorHanoi); + break; + + case 241: + Actor_Face_Actor(kActorHanoi, kActorMcCoy, 1); + Actor_Change_Animation_Mode(kActorHanoi, 6); + Actor_Retired_Here(kActorMcCoy, 12, 12, 1, -1); + break; + + case 9999: + AI_Movement_Track_Flush(kActorHanoi); + break; + + default: + return false; + } + + return true; +} + +bool AIScriptHanoi::UpdateAnimation(int *animation, int *frame) { + switch (_animationState) { + case 0: + if (_var2 == 1) { + *animation = 649; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(649)) { + *animation = 648; + _animationFrame = 0; + _var2 = 0; + } + } else if (_var2 == 0) { + *animation = 648; + if (_var3) { + _var3--; + if (!Random_Query(0, 6)) { + _var4 = -_var4; + } + } else { + _animationFrame += _var4; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(648)) { + _animationFrame = 0; + } + if (_animationFrame < 0) { + _animationFrame = Slice_Animation_Query_Number_Of_Frames(648) - 1; + } + if (_animationFrame == 5 || _animationFrame == 15 || _animationFrame == 11 || !_animationFrame) { + _var3 = Random_Query(5, 12); + } + if (_animationFrame >= 10 && _animationFrame <= 13) { + _var3 = Random_Query(0, 1); + } + if (!_animationFrame) { + if (!Random_Query(0, 4)) { + _var2 = 1; + } + } + } + } + break; + + case 1: + if (_var2) { + *animation = 649; + if ( Slice_Animation_Query_Number_Of_Frames(649) < Slice_Animation_Query_Number_Of_Frames(649)) { + _animationFrame += 2; + } else { + _animationFrame -= 2; + } + if (_animationFrame > Slice_Animation_Query_Number_Of_Frames(649) - 1 + || _animationFrame <= 0) { + _animationFrame = 0; + _animationState = _animationStateNext; + *animation = _animationNext; + } + } else { + _animationFrame = 0; + _animationState = _animationStateNext; + *animation = _animationNext; + } + break; + + case 2: + *animation = 657; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(657)) { + _animationFrame = 0; + _animationState = 3; + *animation = 658; + } + break; + + case 3: + *animation = 658; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(658)) { + _animationFrame = 0; + } + break; + + case 4: + *animation = 659; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(659)) { + _animationFrame = 0; + _animationState = 3; + *animation = 658; + } + break; + + case 5: + *animation = 657; + _animationFrame--; + if (_animationFrame == 0) { + _animationState = 0; + _animationFrame = 0; + *animation = 648; + Actor_Face_Actor(kActorMcCoy, kActorHanoi, 1); + Actor_Set_Invisible(kActorMcCoy, 0); + + if (Actor_Query_In_Set(kActorHanoi, kSetNR01) == 1) { + AI_Movement_Track_Flush(kActorHanoi); + AI_Movement_Track_Append(kActorHanoi, 350, 0); + AI_Movement_Track_Append(kActorHanoi, 39, 0); + AI_Movement_Track_Repeat(kActorHanoi); + } + } + break; + + case 6: + *animation = 345; + _animationFrame++; + if (_animationFrame > 26) { + Actor_Change_Animation_Mode(kActorHanoi, 0); + _animationState = 0; + _animationFrame = 0; + *animation = 648; + Actor_Set_Goal_Number(kActorMcCoy, 210); + Actor_Set_Goal_Number(kActorHanoi, 210); + } + break; + + case 7: + *animation = 645; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(645)) { + _animationFrame = 0; + } + break; + + case 8: + *animation = 642; + _animationFrame++; + if (_animationFrame > Slice_Animation_Query_Number_Of_Frames(642) - 1) { + _animationFrame = 0; + } + break; + + case 9: + *animation = 643; + _animationFrame++; + if (_animationFrame > Slice_Animation_Query_Number_Of_Frames(643) - 1) { + Actor_Change_Animation_Mode(kActorHanoi, 4); + _animationState = 8; + _animationFrame = 0; + *animation = 642; + Actor_Set_Goal_Number(kActorHanoi, 241); + } + break; + + case 10: + *animation = 644; + _animationFrame++; + if (_animationFrame == 4) { + Ambient_Sounds_Play_Sound(492, 77, 0, 0, 20); + } + if (_animationFrame == 6) { + Ambient_Sounds_Play_Sound(493, 97, 0, 0, 20); + } + if (_animationFrame == 5) { + Actor_Force_Stop_Walking(kActorMcCoy); + Actor_Change_Animation_Mode(kActorMcCoy, 48); + } + if (_animationFrame > Slice_Animation_Query_Number_Of_Frames(*animation) - 1) { + Actor_Change_Animation_Mode(kActorHanoi, 4); + _animationFrame = 0; + _animationState = 8; + *animation = 642; + } + break; + + case 11: + *animation = 660; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(660)) { + *animation = 648; + _animationFrame = 0; + _animationState = 0; + } + break; + + case 12: + *animation = 646; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(646)) { + *animation = 642; + _animationFrame = 0; + _animationState = 0; + } + break; + + case 13: + *animation = 647; + if (_animationFrame < Slice_Animation_Query_Number_Of_Frames(647) - 1) { + _animationFrame++; + } + break; + + case 14: + *animation = 650; + if (!_animationFrame && _var1) { + _animationState = 0; + } else { + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(650)) { + _animationFrame = 0; + } + } + break; + + case 15: + *animation = 651; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(651)) { + _animationFrame = 0; + _animationState = 14; + *animation = 650; + } + break; + + case 16: + *animation = 652; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(652)) { + _animationFrame = 0; + _animationState = 14; + *animation = 650; + } + break; + + case 17: + *animation = 653; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(653)) { + _animationFrame = 0; + _animationState = 14; + *animation = 650; + } + break; + + case 18: + *animation = 654; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(654)) { + _animationFrame = 0; + _animationState = 14; + *animation = 650; + } + break; + + case 19: + *animation = 655; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(655)) { + _animationFrame = 0; + _animationState = 14; + *animation = 650; + } + break; + + case 20: + *animation = 656; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(656)) { + _animationFrame = 0; + _animationState = 14; + *animation = 650; + } + break; + + default: + break; + } + *frame = _animationFrame; + + return true; +} + +bool AIScriptHanoi::ChangeAnimationMode(int mode) { + switch (mode) { + case kAnimationModeIdle: + if ((unsigned int)(_animationState - 2) > 1) { + _animationState = 0; + } else { + _animationState = 3; + } + _animationFrame = 0; + break; + + case kAnimationModeWalk: + _animationState = 7; + _animationFrame = 0; + break; + + case kAnimationModeTalk: + if (_animationState == 3) { + _animationState = 4; + _animationFrame = 0; + } else { + _animationStateNext = 14; + _animationNext = 650; + _animationState = 1; + } + break; + + case kAnimationModeCombatIdle: + _animationState = 8; + _animationFrame = 0; + break; + + case kAnimationModeCombatAttack: + _animationState = 10; + _animationFrame = 0; + break; + + case 12: + _animationStateNext = 15; + _animationNext = 651; + _animationState = 1; + break; + + case 13: + _animationStateNext = 16; + _animationNext = 652; + _animationState = 1; + break; + + case 14: + _animationStateNext = 17; + _animationNext = 653; + _animationState = 1; + break; + + case 15: + _animationStateNext = 18; + _animationNext = 654; + _animationState = 1; + break; + + case 16: + _animationStateNext = 18; + _animationNext = 654; + _animationState = 1; + break; + + case 17: + _animationStateNext = 20; + _animationNext = 656; + _animationState = 1; + break; + + case kAnimationModeHit: + case kAnimationModeCombatHit: + _animationState = 12; + _animationFrame = 0; + break; + + case 23: + if (_animationState != 3 && _animationState != 4) { + Actor_Set_Invisible(kActorMcCoy, true); + _animationState = 2; + _animationFrame = 0; + } else { + _animationState = 5; + _animationFrame = Slice_Animation_Query_Number_Of_Frames(657) - 1; + } + break; + + case kAnimationModeDie: + _animationState = 13; + _animationFrame = 0; + break; + + case 71: + _animationState = 9; + _animationFrame = 0; + break; + + case 78: + _animationState = 6; + _animationFrame = 16; + break; + } + + return true; +} + +void AIScriptHanoi::QueryAnimationState(int *animationState, int *animationFrame, int *animationStateNext, int *animationNext) { + *animationState = _animationState; + *animationFrame = _animationFrame; + *animationStateNext = _animationStateNext; + *animationNext = _animationNext; +} + +void AIScriptHanoi::SetAnimationState(int animationState, int animationFrame, int animationStateNext, int animationNext) { + _animationState = animationState; + _animationFrame = animationFrame; + _animationStateNext = animationStateNext; + _animationNext = animationNext; +} + +bool AIScriptHanoi::ReachedMovementTrackWaypoint(int waypointId) { + if (waypointId == 365) { + Actor_Face_Actor(kActorHanoi, kActorHysteriaPatron1, true); + } + + return true; +} + +void AIScriptHanoi::FledCombat() { + // return false; +} + +} // End of namespace BladeRunner diff --git a/engines/bladerunner/script/ai/hasan.cpp b/engines/bladerunner/script/ai/hasan.cpp new file mode 100644 index 0000000000..ed2ebc717f --- /dev/null +++ b/engines/bladerunner/script/ai/hasan.cpp @@ -0,0 +1,315 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can 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 "bladerunner/script/ai_script.h" + +namespace BladeRunner { + +AIScriptHasan::AIScriptHasan(BladeRunnerEngine *vm) : AIScriptBase(vm) { + _var1 = 6; + _var2 = 1; + _var3 = 0; + _var4 = 0; + _var5 = 0; + _var6 = 0; +} + +void AIScriptHasan::Initialize() { + _animationFrame = 0; + _animationState = 0; + _animationStateNext = 0; + _animationNext = 0; + + _var1 = 6; + _var2 = 1; + _var3 = 0; + _var4 = 0; + _var5 = 0; + _var6 = 0; + + Actor_Put_In_Set(kActorHasan, 0); + Actor_Set_At_XYZ(kActorHasan, -214.0f, 0.0f, -1379.0f, 371); + Actor_Set_Goal_Number(kActorHasan, 0); +} + +bool AIScriptHasan::Update() { + if (Global_Variable_Query(kVariableChapter) != 3 || Actor_Query_Goal_Number(kActorHasan) >= 300) + return false; + + Actor_Set_Goal_Number(kActorHasan, 300); + return true; +} + +void AIScriptHasan::TimerExpired(int timer) { + //return false; +} + +void AIScriptHasan::CompletedMovementTrack() { + //return false; +} + +void AIScriptHasan::ReceivedClue(int clueId, int fromActorId) { + //return false; +} + +void AIScriptHasan::ClickedByPlayer() { + //return false; +} + +void AIScriptHasan::EnteredScene(int sceneId) { + // return false; +} + +void AIScriptHasan::OtherAgentEnteredThisScene(int otherActorId) { + // return false; +} + +void AIScriptHasan::OtherAgentExitedThisScene(int otherActorId) { + // return false; +} + +void AIScriptHasan::OtherAgentEnteredCombatMode(int otherActorId, int combatMode) { + // return false; +} + +void AIScriptHasan::ShotAtAndMissed() { + // return false; +} + +bool AIScriptHasan::ShotAtAndHit() { + return false; +} + +void AIScriptHasan::Retired(int byActorId) { + // return false; +} + +int AIScriptHasan::GetFriendlinessModifierIfGetsClue(int otherActorId, int clueId) { + return 0; +} + +bool AIScriptHasan::GoalChanged(int currentGoalNumber, int newGoalNumber) { + if (newGoalNumber == 300) { + Actor_Put_In_Set(kActorHasan, kSetFreeSlotH); + Actor_Set_At_Waypoint(kActorHasan, 40, 0); + } + return false; +} + +bool AIScriptHasan::UpdateAnimation(int *animation, int *frame) { + if (_var4) { + _var4--; + } + if (_var5) { + _var5--; + } + + switch (_animationState) { + case 0: + if (_var6 == 1) { + *animation = 922; + if (_var3) { + _var3--; + } else { + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(922)) { + _animationFrame = 0; + _var6 = 0; + *animation = 921; + _var1 = Random_Query(6, 14); + _var2 = 2 * Random_Query(0, 1) - 1; + _var4 = Random_Query(40, 60); + } + if (_animationFrame >= 10 && _animationFrame <= 14) { + _var3 = Random_Query(0, 1); + } + } + } else if (_var6 == 2) { + *animation = 923; + if (_var3) { + _var3--; + } else { + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(923)) { + _animationFrame = 0; + _var6 = 0; + *animation = 921; + _var1 = Random_Query(6, 14); + _var2 = 2 * Random_Query(0, 1) - 1; + _var5 = Random_Query(40, 60); + } + if (_animationFrame == 14) { + _var3 = Random_Query(3, 10); + } + if (_animationFrame == 23) { + _var3 = Random_Query(0, 4); + } + } + } else if (_var6 == 0) { + *animation = 921; + if (_var3) { + _var3--; + } else { + _animationFrame += _var2; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(921)) { + _animationFrame = 0; + } + if (_animationFrame < 0) { + _animationFrame = Slice_Animation_Query_Number_Of_Frames(921) - 1; + } + if (!--_var1) { + _var2 = 2 * Random_Query(0, 1) - 1; + _var1 = Random_Query(6, 14); + _var3 = Random_Query(0, 4); + } + if (!_animationFrame) { + _var6 = Random_Query(0, 2); + } + if (_var6 == 1 && _var4) { + _var6 = 0; + } + if (_var6 == 2 && _var5) { + _var6 = 0; + } + } + } + break; + + case 1: + *animation = 925; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(925)) { + _animationFrame = 0; + } + break; + + case 2: + *animation = 926; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(926)) { + _animationFrame = 0; + _animationState = 1; + *animation = 925; + } + break; + + case 3: + *animation = 927; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(927)) { + _animationFrame = 0; + _animationState = 1; + *animation = 925; + } + break; + + case 4: + *animation = 928; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(928)) { + _animationFrame = 0; + _animationState = 1; + *animation = 925; + } + break; + + case 5: + *animation = 929; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(929)) { + _animationFrame = 0; + _animationState = 1; + *animation = 925; + } + break; + + case 6: + *animation = 930; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(930)) { + _animationFrame = 0; + _animationState = 1; + *animation = 925; + } + break; + + default: + break; + } + *frame = _animationFrame; + + return true; +} + +bool AIScriptHasan::ChangeAnimationMode(int mode) { + switch (mode) { + case 0: + _animationState = 0; + _var6 = 0; + _animationFrame = 0; + break; + + case 3: + _animationState = 1; + _var6 = 0; + _animationFrame = 0; + break; + + case 12: + case 13: + case 14: + case 15: + case 16: + _animationState = 6; + _var6 = 0; + _animationFrame = 0; + break; + + default: + break; + } + return true; +} + +void AIScriptHasan::QueryAnimationState(int *animationState, int *animationFrame, int *animationStateNext, int *animationNext) { + *animationState = _animationState; + *animationFrame = _animationFrame; + *animationStateNext = _animationStateNext; + *animationNext = _animationNext; +} + +void AIScriptHasan::SetAnimationState(int animationState, int animationFrame, int animationStateNext, int animationNext) { + _animationState = animationState; + _animationFrame = animationFrame; + _animationStateNext = animationStateNext; + _animationNext = animationNext; +} + +bool AIScriptHasan::ReachedMovementTrackWaypoint(int waypointId) { + return true; +} + +void AIScriptHasan::FledCombat() { + // return false; +} + +} // End of namespace BladeRunner diff --git a/engines/bladerunner/script/ai/hysteria_patron1.cpp b/engines/bladerunner/script/ai/hysteria_patron1.cpp index 44278ac5e9..6eadd74976 100644 --- a/engines/bladerunner/script/ai/hysteria_patron1.cpp +++ b/engines/bladerunner/script/ai/hysteria_patron1.cpp @@ -34,7 +34,7 @@ void AIScriptHysteriaPatron1::Initialize() { _animationNext = 0; Actor_Put_In_Set(kActorHysteriaPatron1, kSetNR03); - Actor_Set_At_XYZ(kActorHysteriaPatron1, 50.0f, -6.5900002f, -1030.0f, 524); + Actor_Set_At_XYZ(kActorHysteriaPatron1, 50.0f, -6.59f, -1030.0f, 524); } bool AIScriptHysteriaPatron1::Update() { diff --git a/engines/bladerunner/script/ai/leon.cpp b/engines/bladerunner/script/ai/leon.cpp index fad9da3cfb..fdcb538f0a 100644 --- a/engines/bladerunner/script/ai/leon.cpp +++ b/engines/bladerunner/script/ai/leon.cpp @@ -386,7 +386,7 @@ bool AIScriptLeon::ChangeAnimationMode(int mode) { _animationFrame = 0; var_45EDAC = 0; break; - case kAnimationModeCombatShoot: + case kAnimationModeCombatAttack: _animationState = 10; _animationFrame = 0; break; diff --git a/engines/bladerunner/script/ai/lucy.cpp b/engines/bladerunner/script/ai/lucy.cpp index aebeac7322..464f228179 100644 --- a/engines/bladerunner/script/ai/lucy.cpp +++ b/engines/bladerunner/script/ai/lucy.cpp @@ -227,7 +227,7 @@ void AIScriptLucy::Retired(int byActorId) { if ((byActorId == kActorSteele || byActorId == kActorMcCoy) && Actor_Query_In_Set(kActorSteele, kSetHF06) && Actor_Query_In_Set(kActorMcCoy, kSetHF06)) { - Non_Player_Actor_Combat_Mode_On(kActorSteele, 3, 1, 0, 15, 4, 7, 8, 0, 0, 100, 25, 300, 0); + Non_Player_Actor_Combat_Mode_On(kActorSteele, kActorCombatStateUncover, true, kActorMcCoy, 15, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, 0, 0, 100, 25, 300, false); } if (Query_Difficulty_Level() && byActorId == kActorMcCoy && Game_Flag_Query(46)) { Global_Variable_Increment(2, 200); @@ -861,10 +861,10 @@ void AIScriptLucy::checkCombat() { && Global_Variable_Query(kVariableChapter) == 5 && Actor_Query_Goal_Number(kActorLucy) != 450) { if (Global_Variable_Query(kVariableAffectionTowards) == 3) { - Global_Variable_Set(45, 0); + Global_Variable_Set(kVariableAffectionTowards, 0); } Actor_Set_Goal_Number(kActorLucy, 450); - Non_Player_Actor_Combat_Mode_On(kActorLucy, 0, 0, 0, 4, 0, 1, 2, -1, 0, 0, 10, 300, 0); + Non_Player_Actor_Combat_Mode_On(kActorLucy, kActorCombatStateIdle, false, kActorMcCoy, 4, kAnimationModeIdle, kAnimationModeWalk, kAnimationModeRun, -1, 0, 0, 10, 300, false); } } diff --git a/engines/bladerunner/script/ai/luther.cpp b/engines/bladerunner/script/ai/luther.cpp new file mode 100644 index 0000000000..21df880d64 --- /dev/null +++ b/engines/bladerunner/script/ai/luther.cpp @@ -0,0 +1,439 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "bladerunner/script/ai_script.h" + +namespace BladeRunner { + +AIScriptLuther::AIScriptLuther(BladeRunnerEngine *vm) : AIScriptBase(vm) { + _flag = false; +} + +void AIScriptLuther::Initialize() { + _animationFrame = 0; + _animationState = 0; + _animationStateNext = 0; + _animationNext = 0; + + _flag = false; + + Actor_Put_In_Set(kActorLuther, kSetUG16); + Actor_Set_At_XYZ(kActorLuther, 176.91f, -40.67f, 225.92f, 486); + Actor_Set_Goal_Number(kActorLuther, 400); + Actor_Set_Targetable(kActorLuther, 1); +} + +bool AIScriptLuther::Update() { + if (!Actor_Query_Is_In_Current_Set(kActorLuther) + || Player_Query_Combat_Mode() != 1 + || Global_Variable_Query(29) + || Game_Flag_Query(596) + || Global_Variable_Query(kVariableChapter) != 4) { + if (Actor_Query_Goal_Number(kActorLuther) == 400 && Actor_Query_Goal_Number(kActorLuther) != 499) { + Actor_Set_Goal_Number(kActorLuther, 401); + } else if (Actor_Query_Goal_Number(kActorLuther) == 494) { + Actor_Set_Goal_Number(kActorLuther, 495); + ChangeAnimationMode(48); + } else if (Actor_Query_Goal_Number(kActorLuther) != 495 || Game_Flag_Query(587)) { + if (Actor_Query_Goal_Number(kActorLuther) != 497 + || Global_Variable_Query(29) >= 2 + || Game_Flag_Query(568)) { + if (Actor_Query_Goal_Number(kActorLuther) != 497 + || Global_Variable_Query(29) <= 1 + || Game_Flag_Query(568)) { + if (Actor_Query_Goal_Number(kActorLuther) == 498) { + Game_Flag_Set(595); + Actor_Set_Goal_Number(kActorLuther, 499); + Actor_Set_Targetable(kActorLuther, 0); + } else { + return false; + } + } else { + Actor_Set_Targetable(kActorLuther, 0); + Actor_Set_Goal_Number(kActorLuther, 498); + Actor_Set_Targetable(kActorLuther, 0); + } + } else { + Game_Flag_Set(568); + ChangeAnimationMode(50); + ChangeAnimationMode(48); + Actor_Set_Goal_Number(kActorLuther, 498); + Actor_Set_Targetable(kActorLuther, 0); + Scene_Loop_Set_Default(5); + Scene_Loop_Start_Special(2, 4, 1); + Ambient_Sounds_Play_Sound(559, 50, 0, 0, 99); + Ambient_Sounds_Remove_Looping_Sound(516, 1); + } + } else { + AI_Countdown_Timer_Reset(kActorLuther, 2); + AI_Countdown_Timer_Start(kActorLuther, 2, 5); + Actor_Set_Goal_Number(kActorLuther, 496); + Game_Flag_Set(587); + } + } else { + Actor_Says(kActorMcCoy, 5720, 12); + Actor_Says(kActorLuther, 80, 13); + Actor_Says(kActorLance, 40, 12); + Game_Flag_Set(596); + } + + return false; +} + +void AIScriptLuther::TimerExpired(int timer) { + if (timer != 2) + return; //false; + + AI_Countdown_Timer_Reset(kActorLuther, 2); + Actor_Set_Goal_Number(kActorLuther, 497); + + return; //true; +} + +void AIScriptLuther::CompletedMovementTrack() { + if (Actor_Query_Goal_Number(kActorLuther) != 401) + return; //false; + + Actor_Set_Goal_Number(kActorLuther, 402); + + return; //true; +} + +void AIScriptLuther::ReceivedClue(int clueId, int fromActorId) { + //return false; +} + +void AIScriptLuther::ClickedByPlayer() { + //return false; +} + +void AIScriptLuther::EnteredScene(int sceneId) { + // return false; +} + +void AIScriptLuther::OtherAgentEnteredThisScene(int otherActorId) { + // return false; +} + +void AIScriptLuther::OtherAgentExitedThisScene(int otherActorId) { + // return false; +} + +void AIScriptLuther::OtherAgentEnteredCombatMode(int otherActorId, int combatMode) { + // return false; +} + +void AIScriptLuther::ShotAtAndMissed() { + // return false; +} + +bool AIScriptLuther::ShotAtAndHit() { + if (Actor_Query_Which_Set_In(kActorLuther) == 19) { + Actor_Set_Health(kActorLuther, 50, 50); + } + Global_Variable_Increment(29, 1); + Music_Stop(2); + if (Global_Variable_Query(29) <= 0) { + return false; + } + if (!Game_Flag_Query(560)) { + Game_Flag_Set(557); + } + Actor_Set_Goal_Number(kActorLuther, 494); + + return true; +} + +void AIScriptLuther::Retired(int byActorId) { + Actor_Set_Goal_Number(kActorLuther, 599); +} + +int AIScriptLuther::GetFriendlinessModifierIfGetsClue(int otherActorId, int clueId) { + return 0; +} + +bool AIScriptLuther::GoalChanged(int currentGoalNumber, int newGoalNumber) { + switch (newGoalNumber) { + case 401: + AI_Movement_Track_Flush(kActorLuther); + AI_Movement_Track_Append(kActorLuther, 39, 20); + AI_Movement_Track_Append_With_Facing(kActorLuther, 368, 120, 486); + AI_Movement_Track_Append(kActorLuther, 40, 10); + AI_Movement_Track_Repeat(kActorLuther); + break; + + case 402: + Actor_Set_Goal_Number(kActorLuther, 401); + break; + + case 403: + AI_Movement_Track_Flush(kActorLuther); + break; + + case 499: + Actor_Set_Goal_Number(kActorLuther, 599); + break; + } + + return false; +} + +bool AIScriptLuther::UpdateAnimation(int *animation, int *frame) { + switch (_animationState) { + case 0: + *animation = 346; + _animationFrame++; + if (_animationFrame > Slice_Animation_Query_Number_Of_Frames(346) - 1) { + _animationFrame = 0; + } + break; + + case 1: + *animation = 348; + _animationFrame++; + if (_animationFrame > Slice_Animation_Query_Number_Of_Frames(348) - 1) { + *animation = 346; + _animationFrame = 0; + _animationState = 0; + Actor_Change_Animation_Mode(kActorLuther, 0); + } + break; + + case 2: + if (!_animationFrame && _flag) { + *animation = 346; + _animationState = 0; + } else { + *animation = 349; + _animationFrame++; + if (_animationFrame > Slice_Animation_Query_Number_Of_Frames(349) - 1) { + _animationFrame = 0; + } + } + break; + + case 3: + *animation = 350; + _animationFrame++; + if (_animationFrame > Slice_Animation_Query_Number_Of_Frames(350) - 1) { + _animationFrame = 0; + _animationState = 2; + *animation = 349; + } + break; + + case 4: + *animation = 351; + _animationFrame++; + if (_animationFrame > Slice_Animation_Query_Number_Of_Frames(351) - 1) { + _animationFrame = 0; + _animationState = 2; + *animation = 349; + } + break; + + case 5: + *animation = 352; + _animationFrame++; + if (_animationFrame > Slice_Animation_Query_Number_Of_Frames(352) - 1) { + _animationFrame = 0; + _animationState = 2; + *animation = 349; + } + break; + + case 6: + *animation = 353; + _animationFrame++; + if (_animationFrame > Slice_Animation_Query_Number_Of_Frames(353) - 1) { + _animationFrame = 0; + _animationState = 2; + *animation = 349; + } + break; + + case 7: + *animation = 354; + _animationFrame++; + if (_animationFrame > Slice_Animation_Query_Number_Of_Frames(354) - 1) { + _animationFrame = 0; + _animationState = 2; + *animation = 349; + } + break; + + case 8: + *animation = 355; + _animationFrame++; + if (_animationFrame > Slice_Animation_Query_Number_Of_Frames(355) - 1) { + _animationFrame = 0; + _animationState = 2; + *animation = 349; + } + break; + + case 9: + *animation = 356; + _animationFrame++; + if (_animationFrame > Slice_Animation_Query_Number_Of_Frames(356) - 1) { + *animation = 346; + _animationFrame = 0; + _animationState = 0; + Actor_Change_Animation_Mode(kActorLuther, 0); + } + break; + + case 10: + *animation = 357; + _animationFrame++; + if (_animationFrame > Slice_Animation_Query_Number_Of_Frames(357) - 1) { + Actor_Change_Animation_Mode(kActorLuther, 50); + *animation = 358; + _animationFrame = 0; + } + break; + + case 11: + *animation = 358; + if (_animationFrame < Slice_Animation_Query_Number_Of_Frames(358) - 1) { + _animationFrame++; + } + break; + + case 12: + *animation = 359; + if (_animationFrame == 12) { + Ambient_Sounds_Play_Sound(557, 59, 0, 0, 20); + } + if (_animationFrame < Slice_Animation_Query_Number_Of_Frames(*animation) - 1) { + _animationFrame++; + } + break; + + default: + break; + } + *frame = _animationFrame; + + return true; +} + +bool AIScriptLuther::ChangeAnimationMode(int mode) { + switch (mode) { + case 0: + if ((unsigned int)(_animationState - 2) > 6) { + _animationState = 0; + _animationFrame = 0; + } else { + _flag = 1; + } + break; + + case 3: + _animationState = 2; + _animationFrame = 0; + _flag = 0; + break; + + case 6: + _animationState = 9; + _animationFrame = 0; + break; + + case 12: + _animationState = 3; + _animationFrame = 0; + _flag = 0; + break; + + case 13: + _animationState = 4; + _animationFrame = 0; + _flag = 0; + break; + + case 14: + _animationState = 5; + _animationFrame = 0; + _flag = 0; + break; + + case 15: + _animationState = 6; + _animationFrame = 0; + _flag = 0; + break; + + case 16: + _animationState = 7; + _animationFrame = 0; + _flag = 0; + break; + + case 17: + _animationState = 8; + _animationFrame = 0; + _flag = 0; + break; + + case 23: + _animationState = 1; + _animationFrame = 0; + break; + + case 48: + _animationState = 12; + _animationFrame = 0; + break; + + case 50: + _animationState = 11; + _animationFrame = 0; + break; + } + + return true; +} + +void AIScriptLuther::QueryAnimationState(int *animationState, int *animationFrame, int *animationStateNext, int *animationNext) { + *animationState = _animationState; + *animationFrame = _animationFrame; + *animationStateNext = _animationStateNext; + *animationNext = _animationNext; +} + +void AIScriptLuther::SetAnimationState(int animationState, int animationFrame, int animationStateNext, int animationNext) { + _animationState = animationState; + _animationFrame = animationFrame; + _animationStateNext = animationStateNext; + _animationNext = animationNext; +} + +bool AIScriptLuther::ReachedMovementTrackWaypoint(int waypointId) { + return true; +} + +void AIScriptLuther::FledCombat() { + // return false; +} + +} // End of namespace BladeRunner diff --git a/engines/bladerunner/script/ai/mccoy.cpp b/engines/bladerunner/script/ai/mccoy.cpp index 554de0cd80..1e3dff8c1b 100644 --- a/engines/bladerunner/script/ai/mccoy.cpp +++ b/engines/bladerunner/script/ai/mccoy.cpp @@ -242,22 +242,22 @@ bool AIScriptMcCoy::ShotAtAndHit() { void AIScriptMcCoy::Retired(int byActorId) { if (byActorId == kActorSteele && Actor_Query_In_Set(kActorSteele, kSetHF06)) { if (Actor_Query_In_Set(kActorDektora, kSetHF06) && Actor_Query_Goal_Number(kActorDektora) != 599) { - Non_Player_Actor_Combat_Mode_On(kActorSteele, 3, 1, kActorDektora, 15, 4, 7, 8, 0, 0, 100, 25, 300, 0); + Non_Player_Actor_Combat_Mode_On(kActorSteele, kActorCombatStateUncover, true, kActorDektora, 15, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, 0, 0, 100, 25, 300, false); } else if (Actor_Query_In_Set(kActorLucy, kSetHF06) && Actor_Query_Goal_Number(kActorLucy) != 599) { - Non_Player_Actor_Combat_Mode_On(kActorSteele, 3, 1, kActorLucy, 15, 4, 7, 8, 0, 0, 100, 25, 300, 0); + Non_Player_Actor_Combat_Mode_On(kActorSteele, kActorCombatStateUncover, true, kActorLucy, 15, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, 0, 0, 100, 25, 300, false); } } if (Actor_Query_In_Set(kActorMcCoy, kSetHF05) && Actor_Query_In_Set(kActorOfficerLeary, kSetHF05) && Actor_Query_In_Set(kActorDektora, kSetHF05) && Actor_Query_Goal_Number(kActorDektora) != 599) { - Non_Player_Actor_Combat_Mode_On(kActorOfficerLeary, 3, 1, kActorDektora, 4, 4, 7, 8, 0, 0, 100, 25, 300, 0); + Non_Player_Actor_Combat_Mode_On(kActorOfficerLeary, kActorCombatStateUncover, true, kActorDektora, 4, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, 0, 0, 100, 25, 300, false); } if (Actor_Query_In_Set(kActorMcCoy, kSetHF05) && Actor_Query_In_Set(kActorOfficerGrayford, kSetHF05) && Actor_Query_In_Set(kActorDektora, kSetHF05) && Actor_Query_Goal_Number(kActorDektora) != 599) { - Non_Player_Actor_Combat_Mode_On(kActorOfficerGrayford, 3, 1, kActorDektora, 4, 4, 7, 8, 0, 0, 100, 25, 300, 0); + Non_Player_Actor_Combat_Mode_On(kActorOfficerGrayford, kActorCombatStateUncover, true, kActorDektora, 4, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, 0, 0, 100, 25, 300, false); } if (Actor_Query_In_Set(kActorMcCoy, kSetHF05) && Actor_Query_In_Set(kActorOfficerLeary, kSetHF05) && Actor_Query_In_Set(kActorLucy, kSetHF05) && Actor_Query_Goal_Number(kActorLucy) != 599) { - Non_Player_Actor_Combat_Mode_On(kActorOfficerLeary, 3, 1, kActorLucy, 4, 4, 7, 8, 0, 0, 100, 25, 300, 0); + Non_Player_Actor_Combat_Mode_On(kActorOfficerLeary, kActorCombatStateUncover, true, kActorLucy, 4, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, 0, 0, 100, 25, 300, false); } if (Actor_Query_In_Set(kActorMcCoy, kSetHF05) && Actor_Query_In_Set(kActorOfficerGrayford, kSetHF05) && Actor_Query_In_Set(kActorLucy, kSetHF05) && Actor_Query_Goal_Number(kActorLucy) != 599) { - Non_Player_Actor_Combat_Mode_On(kActorOfficerGrayford, 3, 1, kActorLucy, 4, 4, 7, 8, 0, 0, 100, 25, 300, 0); + Non_Player_Actor_Combat_Mode_On(kActorOfficerGrayford, kActorCombatStateUncover, true, kActorLucy, 4, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, 0, 0, 100, 25, 300, false); } } @@ -307,7 +307,7 @@ bool AIScriptMcCoy::GoalChanged(int currentGoalNumber, int newGoalNumber) { return true; case 230: dword_45A0FC = Actor_Query_Goal_Number(kActorSteele) == 215; - Actor_Change_Animation_Mode(kActorMcCoy, kAnimationModeCombatShoot); + Actor_Change_Animation_Mode(kActorMcCoy, kAnimationModeCombatAttack); return true; case 220: Actor_Change_Animation_Mode(kActorMcCoy, 75); @@ -412,7 +412,7 @@ bool AIScriptMcCoy::GoalChanged(int currentGoalNumber, int newGoalNumber) { case 400: Actor_Set_Health(kActorMcCoy, 50, 50); Game_Flag_Set(373); - v5 = Global_Variable_Query(45); + v5 = Global_Variable_Query(kVariableAffectionTowards); if (v5 == 1) { Actor_Modify_Friendliness_To_Other(kActorSteele, kActorMcCoy, 3); } else if (v5 == 2) { @@ -428,14 +428,14 @@ bool AIScriptMcCoy::GoalChanged(int currentGoalNumber, int newGoalNumber) { if (Actor_Query_Friendliness_To_Other(kActorSteele, kActorMcCoy) < Actor_Query_Friendliness_To_Other(kActorClovis, kActorMcCoy)) { Game_Flag_Set(653); } - v7 = Global_Variable_Query(45); + v7 = Global_Variable_Query(kVariableAffectionTowards); if (v7 == 1) { if (Game_Flag_Query(653)) { - Global_Variable_Set(45, 0); + Global_Variable_Set(kVariableAffectionTowards, 0); } } else if (v7 == 2 || v7 == 3) { if (!Game_Flag_Query(653)) { - Global_Variable_Set(45, 0); + Global_Variable_Set(kVariableAffectionTowards, 0); } } if (!Game_Flag_Query(653)) { @@ -1382,7 +1382,7 @@ bool AIScriptMcCoy::ChangeAnimationMode(int mode) { break; } break; - case kAnimationModeCombatShoot: + case kAnimationModeCombatAttack: _animationState = 21; _animationFrame = 0; break; @@ -1503,7 +1503,7 @@ bool AIScriptMcCoy::ChangeAnimationMode(int mode) { } } break; - case 21: + case kAnimationModeHit: switch (_animationState) { case 14: case 15: @@ -1520,7 +1520,7 @@ bool AIScriptMcCoy::ChangeAnimationMode(int mode) { } _animationFrame = 0; break; - case 16: + default: if (Random_Query(0, 1) == 1) { _animationState = 26; } else { @@ -1530,7 +1530,7 @@ bool AIScriptMcCoy::ChangeAnimationMode(int mode) { break; } break; - case 22: + case kAnimationModeCombatHit: if (Random_Query(0, 1) == 1) { _animationState = 23; } else { @@ -1587,7 +1587,7 @@ bool AIScriptMcCoy::ChangeAnimationMode(int mode) { _animationState = 41; _animationFrame = 0; break; - case 48: + case kAnimationModeDie: switch (_animationState) { case 14: case 15: @@ -1610,7 +1610,7 @@ bool AIScriptMcCoy::ChangeAnimationMode(int mode) { break; } break; - case 49: + case kAnimationModeCombatDie: _animationState = 28; _animationFrame = 0; break; @@ -1622,7 +1622,7 @@ bool AIScriptMcCoy::ChangeAnimationMode(int mode) { _animationState = 55; _animationFrame = 0; break; - case 53: + case kAnimationModeSit: if (_animationState != 60 && (Player_Query_Current_Set() == kSetNR03 || Player_Query_Current_Set() == kSetNR05_NR08)) { _animationState = 60; _animationFrame = 0; diff --git a/engines/bladerunner/script/ai/mutant1.cpp b/engines/bladerunner/script/ai/mutant1.cpp index 9ecaf9d80e..eb1c0562af 100644 --- a/engines/bladerunner/script/ai/mutant1.cpp +++ b/engines/bladerunner/script/ai/mutant1.cpp @@ -83,7 +83,7 @@ bool AIScriptMutant1::Update() { case 410: if (Actor_Query_Which_Set_In(kActorMutant1) != Player_Query_Current_Set()) { - Non_Player_Actor_Combat_Mode_Off(70); + Non_Player_Actor_Combat_Mode_Off(kActorMutant1); Actor_Set_Goal_Number(kActorMutant1, 403); } break; @@ -326,28 +326,23 @@ bool AIScriptMutant1::GoalChanged(int currentGoalNumber, int newGoalNumber) { case 410: switch (Actor_Query_Which_Set_In(kActorMutant1)) { case kSetUG01: - Non_Player_Actor_Combat_Mode_On(kActorMutant1, 0, 0, 0, 11, 4, 7, 8, -1, -1, -1, 10, 300, 0); + Non_Player_Actor_Combat_Mode_On(kActorMutant1, kActorCombatStateIdle, false, kActorMcCoy, 11, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, -1, -1, -1, 10, 300, false); break; case kSetUG04: case kSetUG05: case kSetUG06: - Non_Player_Actor_Combat_Mode_On(kActorMutant1, 0, 0, 0, 10, 4, 7, 8, -1, -1, -1, 10, 300, 0); + Non_Player_Actor_Combat_Mode_On(kActorMutant1, kActorCombatStateIdle, false, kActorMcCoy, 10, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, -1, -1, -1, 10, 300, false); break; case kSetUG07: - Non_Player_Actor_Combat_Mode_On(kActorMutant1, 0, 0, 0, 12, 4, 7, 8, -1, -1, -1, 10, 300, 0); + Non_Player_Actor_Combat_Mode_On(kActorMutant1, kActorCombatStateIdle, false, kActorMcCoy, 12, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, -1, -1, -1, 10, 300, false); break; case kSetUG10: - Non_Player_Actor_Combat_Mode_On(kActorMutant1, 0, 0, 0, 14, 4, 7, 8, -1, -1, -1, 10, 300, 0); - break; - case kSetUG12: - Non_Player_Actor_Combat_Mode_On(kActorMutant1, 0, 0, 0, 14, 4, 7, 8, -1, -1, -1, 10, 300, 0); - break; case kSetUG14: - Non_Player_Actor_Combat_Mode_On(kActorMutant1, 0, 0, 0, 14, 4, 7, 8, -1, -1, -1, 10, 300, 0); + Non_Player_Actor_Combat_Mode_On(kActorMutant1, kActorCombatStateIdle, false, kActorMcCoy, 14, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, -1, -1, -1, 10, 300, false); break; } return true; diff --git a/engines/bladerunner/script/ai/mutant2.cpp b/engines/bladerunner/script/ai/mutant2.cpp index 6a51c710dd..6317311088 100644 --- a/engines/bladerunner/script/ai/mutant2.cpp +++ b/engines/bladerunner/script/ai/mutant2.cpp @@ -305,25 +305,19 @@ bool AIScriptMutant2::GoalChanged(int currentGoalNumber, int newGoalNumber) { case 410: switch (Actor_Query_Which_Set_In(kActorMutant2)) { case kSetUG01: - Non_Player_Actor_Combat_Mode_On(kActorMutant2, 0, 0, 0, 11, 4, 7, 8, -1, -1, -1, 10, 300, 0); + Non_Player_Actor_Combat_Mode_On(kActorMutant2, kActorCombatStateIdle, false, kActorMcCoy, 11, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, -1, -1, -1, 10, 300, false); break; case kSetUG04: case kSetUG05: case kSetUG06: - Non_Player_Actor_Combat_Mode_On(kActorMutant2, 0, 0, 0, 10, 4, 7, 8, -1, -1, -1, 10, 300, 0); + Non_Player_Actor_Combat_Mode_On(kActorMutant2, kActorCombatStateIdle, false, kActorMcCoy, 10, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, -1, -1, -1, 10, 300, false); break; case kSetUG10: - Non_Player_Actor_Combat_Mode_On(kActorMutant2, 0, 0, 0, 14, 4, 7, 8, -1, -1, -1, 10, 300, 0); - break; - case kSetUG12: - Non_Player_Actor_Combat_Mode_On(kActorMutant2, 0, 0, 0, 14, 4, 7, 8, -1, -1, -1, 10, 300, 0); - break; - case kSetUG14: - Non_Player_Actor_Combat_Mode_On(kActorMutant2, 0, 0, 0, 14, 4, 7, 8, -1, -1, -1, 10, 300, 0); + Non_Player_Actor_Combat_Mode_On(kActorMutant2, kActorCombatStateIdle, false, kActorMcCoy, 14, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, -1, -1, -1, 10, 300, false); break; } return true; diff --git a/engines/bladerunner/script/ai/mutant3.cpp b/engines/bladerunner/script/ai/mutant3.cpp index 76a9a0edff..db161d8d5a 100644 --- a/engines/bladerunner/script/ai/mutant3.cpp +++ b/engines/bladerunner/script/ai/mutant3.cpp @@ -316,24 +316,19 @@ bool AIScriptMutant3::GoalChanged(int currentGoalNumber, int newGoalNumber) { case 410: switch (Actor_Query_Which_Set_In(kActorMutant3)) { case kSetUG01: - Non_Player_Actor_Combat_Mode_On(kActorMutant3, 0, 1, 0, 11, 4, 7, 8, -1, -1, -1, 10, 300, 0); + Non_Player_Actor_Combat_Mode_On(kActorMutant3, kActorCombatStateIdle, false, kActorMcCoy, 11, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, -1, -1, -1, 10, 300, false); break; case kSetUG04: case kSetUG05: case kSetUG06: - Non_Player_Actor_Combat_Mode_On(kActorMutant3, 0, 1, 0, 10, 4, 7, 8, -1, -1, -1, 10, 300, 0); + Non_Player_Actor_Combat_Mode_On(kActorMutant3, kActorCombatStateIdle, false, kActorMcCoy, 10, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, -1, -1, -1, 10, 300, false); break; case kSetUG10: - Non_Player_Actor_Combat_Mode_On(kActorMutant3, 0, 1, 0, 14, 4, 7, 8, -1, -1, -1, 10, 300, 0); - break; case kSetUG12: - Non_Player_Actor_Combat_Mode_On(kActorMutant3, 0, 1, 0, 14, 4, 7, 8, -1, -1, -1, 10, 300, 0); - break; - case kSetUG14: - Non_Player_Actor_Combat_Mode_On(kActorMutant3, 0, 1, 0, 14, 4, 7, 8, -1, -1, -1, 10, 300, 0); + Non_Player_Actor_Combat_Mode_On(kActorMutant3, kActorCombatStateIdle, false, kActorMcCoy, 14, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, -1, -1, -1, 10, 300, false); break; } break; @@ -342,6 +337,7 @@ bool AIScriptMutant3::GoalChanged(int currentGoalNumber, int newGoalNumber) { AI_Movement_Track_Flush(kActorMutant3); AI_Movement_Track_Append(kActorMutant3, 39, 100); AI_Movement_Track_Repeat(kActorMutant3); + break; case 599: AI_Movement_Track_Flush(kActorMutant3); diff --git a/engines/bladerunner/script/ai/officer_grayford.cpp b/engines/bladerunner/script/ai/officer_grayford.cpp new file mode 100644 index 0000000000..a1f9b11f9b --- /dev/null +++ b/engines/bladerunner/script/ai/officer_grayford.cpp @@ -0,0 +1,1422 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can 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 "bladerunner/script/ai_script.h" + +namespace BladeRunner { + +AIScriptOfficerGrayford::AIScriptOfficerGrayford(BladeRunnerEngine *vm) : AIScriptBase(vm) { + _var1 = 0; + _var2 = 0; + _var3 = 0; +} + +void AIScriptOfficerGrayford::Initialize() { + _animationFrame = 0; + _animationState = 0; + _animationStateNext = 0; + _animationNext = 0; + + _var1 = 0; + _var2 = 0; + _var3 = 0; + + Actor_Put_In_Set(kActorOfficerGrayford, kSetFreeSlotG); + Actor_Set_At_Waypoint(kActorOfficerGrayford, 39, 0); + Actor_Set_Goal_Number(kActorOfficerGrayford, 0); +} + +bool AIScriptOfficerGrayford::Update() { + if (Global_Variable_Query(kVariableChapter) == 4 && Actor_Query_Goal_Number(kActorOfficerGrayford) < 300) { + AI_Movement_Track_Flush(kActorOfficerGrayford); + Actor_Set_Goal_Number(kActorOfficerGrayford, 300); + } else if (Global_Variable_Query(kVariableChapter) == 5 && Actor_Query_Goal_Number(kActorOfficerGrayford) < 400) { + AI_Movement_Track_Flush(kActorOfficerGrayford); + Actor_Set_Goal_Number(kActorOfficerGrayford, 400); + } else if (!Game_Flag_Query(177) + && Actor_Query_Goal_Number(kActorOfficerGrayford) > 102 + && Actor_Query_Goal_Number(kActorOfficerGrayford) < 110) { + Actor_Set_Goal_Number(kActorOfficerGrayford, 110); + } else if (Actor_Query_Goal_Number(kActorOfficerGrayford) == 0) { + Actor_Set_Goal_Number(kActorOfficerGrayford, 1); + } else if (Actor_Query_Goal_Number(kActorOfficerGrayford) == 10) { + AI_Movement_Track_Flush(kActorOfficerGrayford); + Actor_Set_Goal_Number(kActorOfficerGrayford, 0); + } else if (Actor_Query_Goal_Number(kActorOfficerGrayford) == 102) { + Actor_Set_Goal_Number(kActorOfficerGrayford, 103); + } else if (Game_Flag_Query(629)) { + AI_Movement_Track_Unpause(kActorGenwalkerA); + AI_Movement_Track_Unpause(kActorGenwalkerB); + AI_Movement_Track_Unpause(kActorGenwalkerC); + } else if (Actor_Query_Goal_Number(kActorOfficerGrayford) == 310 + && Actor_Query_Which_Set_In(kActorOfficerGrayford) != Player_Query_Current_Set()) { + Non_Player_Actor_Combat_Mode_Off(kActorOfficerGrayford); + Actor_Set_Goal_Number(kActorOfficerGrayford, 305); + } else if (Actor_Query_Goal_Number(kActorOfficerGrayford) == 599 + && Actor_Query_Which_Set_In(kActorOfficerGrayford) != Player_Query_Current_Set()) { + Actor_Set_Health(kActorOfficerGrayford, 50, 50); + Actor_Set_Goal_Number(kActorOfficerGrayford, 305); + } else if (Actor_Query_Goal_Number(kActorOfficerGrayford) == 305) { + switch (Actor_Query_Which_Set_In(kActorOfficerGrayford)) { + case kSetRC03: + if (Actor_Query_Which_Set_In(kActorOfficerGrayford) == Player_Query_Current_Set()) { + Actor_Set_Goal_Number(kActorOfficerGrayford, 310); + Non_Player_Actor_Combat_Mode_On(kActorOfficerGrayford, kActorCombatStateIdle, true, kActorMcCoy, 18, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, -1, -1, -1, 10, 300, false); + } + break; + + case kSetUG01: + if (Actor_Query_Which_Set_In(kActorOfficerGrayford) == Player_Query_Current_Set()) { + Actor_Set_Goal_Number(kActorOfficerGrayford, 310); + Non_Player_Actor_Combat_Mode_On(kActorOfficerGrayford, kActorCombatStateIdle, true, kActorMcCoy, 11, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, -1, -1, -1, 10, 300, false); + } + break; + + case kSetUG04: + case kSetUG05: + case kSetUG06: + if (Actor_Query_Which_Set_In(kActorOfficerGrayford) == Player_Query_Current_Set()) { + Actor_Set_Goal_Number(kActorOfficerGrayford, 310); + Non_Player_Actor_Combat_Mode_On(kActorOfficerGrayford, kActorCombatStateIdle, true, kActorMcCoy, 10, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, -1, -1, -1, 10, 300, false); + } + break; + + case kSetUG08: + if (Actor_Query_Which_Set_In(kActorOfficerGrayford) == Player_Query_Current_Set()) { + Actor_Set_Goal_Number(kActorOfficerGrayford, 310); + Non_Player_Actor_Combat_Mode_On(kActorOfficerGrayford, kActorCombatStateIdle, true, kActorMcCoy, 13, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, -1, -1, -1, 10, 300, false); + } + break; + + case kSetUG10: + if (Actor_Query_Which_Set_In(kActorOfficerGrayford) == Player_Query_Current_Set()) { + Actor_Set_Goal_Number(kActorOfficerGrayford, 310); + Non_Player_Actor_Combat_Mode_On(kActorOfficerGrayford, kActorCombatStateIdle, true, kActorMcCoy, 14, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, -1, -1, -1, 10, 300, false); + } + break; + + case kSetUG12: + if (Actor_Query_Which_Set_In(kActorOfficerGrayford) == Player_Query_Current_Set()) { + Actor_Set_Goal_Number(kActorOfficerGrayford, 310); + Non_Player_Actor_Combat_Mode_On(kActorOfficerGrayford, kActorCombatStateIdle, true, kActorMcCoy, 16, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, -1, -1, -1, 10, 300, false); + } + break; + + case kSetUG14: + if (Actor_Query_Which_Set_In(kActorOfficerGrayford) == Player_Query_Current_Set()) { + Actor_Set_Goal_Number(kActorOfficerGrayford, 310); + Non_Player_Actor_Combat_Mode_On(kActorOfficerGrayford, kActorCombatStateIdle, true, kActorMcCoy, 17, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, -1, -1, -1, 10, 300, false); + } + break; + + case kSetMA07: + if (Actor_Query_Which_Set_In(kActorOfficerGrayford) == Player_Query_Current_Set()) { + Actor_Set_Goal_Number(kActorOfficerGrayford, 310); + Non_Player_Actor_Combat_Mode_On(kActorOfficerGrayford, kActorCombatStateIdle, true, kActorMcCoy, 7, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, -1, -1, -1, 10, 300, false); + } + break; + + case kSetNR01: + if (Actor_Query_Which_Set_In(kActorOfficerGrayford) == Player_Query_Current_Set()) { + Actor_Set_Goal_Number(kActorOfficerGrayford, 310); + Non_Player_Actor_Combat_Mode_On(kActorOfficerGrayford, kActorCombatStateIdle, true, kActorMcCoy, 3, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, -1, -1, -1, 10, 300, false); + } + break; + + case kSetDR01_DR02_DR04: + if (Actor_Query_Which_Set_In(kActorOfficerGrayford) == Player_Query_Current_Set()) { + Actor_Set_Goal_Number(kActorOfficerGrayford, 310); + Non_Player_Actor_Combat_Mode_On(kActorOfficerGrayford, kActorCombatStateIdle, true, kActorMcCoy, 0, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, -1, -1, -1, 10, 300, false); + } + break; + + case kSetBB01: + if (Actor_Query_Which_Set_In(kActorOfficerGrayford) == Player_Query_Current_Set()) { + Actor_Set_Goal_Number(kActorOfficerGrayford, 310); + Non_Player_Actor_Combat_Mode_On(kActorOfficerGrayford, kActorCombatStateIdle, true, kActorMcCoy, 1, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, -1, -1, -1, 10, 300, false); + } + break; + + case kSetCT11: + if (Actor_Query_Which_Set_In(kActorOfficerGrayford) == Player_Query_Current_Set()) { + Actor_Set_Goal_Number(kActorOfficerGrayford, 310); + Non_Player_Actor_Combat_Mode_On(kActorOfficerGrayford, kActorCombatStateIdle, true, kActorMcCoy, 5, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, -1, -1, -1, 10, 300, false); + } + break; + + } + } + return false; +} + +void AIScriptOfficerGrayford::TimerExpired(int timer) { + if (timer == 2) { + AI_Countdown_Timer_Reset(kActorOfficerGrayford, 2); + if (Actor_Query_Goal_Number(kActorOfficerGrayford) == 104) { + Actor_Set_Goal_Number(kActorOfficerGrayford, 105); + } else if (Actor_Query_Goal_Number(kActorOfficerGrayford) == 105) { + Actor_Set_Goal_Number(kActorOfficerGrayford, 104); + } + } +} + +void AIScriptOfficerGrayford::CompletedMovementTrack() { + switch (Actor_Query_Goal_Number(kActorOfficerGrayford)) { + case 1: + Actor_Set_Goal_Number(kActorOfficerGrayford, 2); + break; + + case 2: + Actor_Set_Goal_Number(kActorOfficerGrayford, 3); + break; + + case 3: + Actor_Set_Goal_Number(kActorOfficerGrayford, 4); + break; + + case 4: + Actor_Set_Goal_Number(kActorOfficerGrayford, 5); + break; + + case 5: + Actor_Set_Goal_Number(kActorOfficerGrayford, 6); + break; + + case 6: + Actor_Set_Goal_Number(kActorOfficerGrayford, 7); + break; + + case 7: + Actor_Set_Goal_Number(kActorOfficerGrayford, 8); + break; + + case 8: + Actor_Set_Goal_Number(kActorOfficerGrayford, 9); + break; + + case 9: + Actor_Set_Goal_Number(kActorOfficerGrayford, 10); + break; + + case 104: + case 105: + if (Random_Query(0, 2)) { + Actor_Change_Animation_Mode(kActorOfficerGrayford, 43); + } else { + AI_Countdown_Timer_Reset(kActorOfficerGrayford, 2); + AI_Countdown_Timer_Start(kActorOfficerGrayford, 2, Random_Query(6, 12)); + } + Actor_Face_Waypoint(kActorOfficerGrayford, 97, true); + // return false; + break; + + case 305: + Actor_Set_Goal_Number(kActorOfficerGrayford, 306); + break; + + case 307: + Non_Player_Actor_Combat_Mode_On(kActorOfficerGrayford, kActorCombatStateIdle, true, kActorMcCoy, 12, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, 0, -1, -1, 15, 300, false); + break; + + case 308: + Actor_Change_Animation_Mode(kActorOfficerGrayford, kAnimationModeCombatIdle); + Actor_Face_Actor(kActorOfficerGrayford, kActorMcCoy, true); + break; + + } + + // return true; +} + +void AIScriptOfficerGrayford::ReceivedClue(int clueId, int fromActorId) { + //return false; +} + +void AIScriptOfficerGrayford::ClickedByPlayer() { + switch (Actor_Query_Goal_Number(kActorOfficerGrayford)) { + case 1: + AI_Movement_Track_Flush(kActorOfficerGrayford); + Actor_Set_Goal_Number(kActorOfficerGrayford, 99); + Actor_Face_Actor(kActorMcCoy, kActorOfficerGrayford, true); + Actor_Face_Actor(kActorOfficerGrayford, kActorMcCoy, true); + if (Random_Query(1, 2) == 1) { + Actor_Says(kActorMcCoy, 5075, 14); + } else { + Actor_Says(kActorMcCoy, 4515, 13); + Actor_Says(kActorOfficerGrayford, 230, 13); + } + Actor_Set_Goal_Number(kActorOfficerGrayford, 1); + break; + + case 2: + AI_Movement_Track_Flush(kActorOfficerGrayford); + Actor_Set_Goal_Number(kActorOfficerGrayford, 99); + Actor_Face_Actor(kActorMcCoy, kActorOfficerGrayford, true); + Actor_Face_Actor(kActorOfficerGrayford, kActorMcCoy, true); + if (Random_Query(1, 2) == 1) { + Actor_Says(kActorMcCoy, 5075, 14); + } else { + Actor_Says(kActorMcCoy, 4515, 13); + Actor_Says(kActorOfficerGrayford, 330, 13); + } + Actor_Set_Goal_Number(kActorOfficerGrayford, 2); + break; + + case 3: + AI_Movement_Track_Flush(kActorOfficerGrayford); + Actor_Set_Goal_Number(kActorOfficerGrayford, 99); + Actor_Face_Actor(kActorMcCoy, kActorOfficerGrayford, true); + Actor_Face_Actor(kActorOfficerGrayford, kActorMcCoy, true); + if (Random_Query(1, 2) == 1) { + Actor_Says(kActorMcCoy, 5075, 14); + } else { + Actor_Says(kActorMcCoy, 5075, 14); // bug in the original? Matches the above statement + } + Actor_Set_Goal_Number(kActorOfficerGrayford, 3); + break; + + case 4: + AI_Movement_Track_Flush(kActorOfficerGrayford); + Actor_Set_Goal_Number(kActorOfficerGrayford, 99); + Actor_Face_Actor(kActorMcCoy, kActorOfficerGrayford, true); + Actor_Face_Actor(kActorOfficerGrayford, kActorMcCoy, true); + if (Random_Query(1, 2) == 1) { + Actor_Says(kActorMcCoy, 5075, 14); + Actor_Says(kActorOfficerGrayford, 160, 13); + } else { + Actor_Says(kActorMcCoy, 4515, 13); + Actor_Says(kActorOfficerGrayford, 330, 13); + } + Actor_Set_Goal_Number(kActorOfficerGrayford, 4); + break; + + case 7: + AI_Movement_Track_Flush(kActorOfficerGrayford); + Actor_Set_Goal_Number(kActorOfficerGrayford, 99); + Actor_Face_Actor(kActorMcCoy, kActorOfficerGrayford, true); + Actor_Says(kActorMcCoy, 4515, 14); + Actor_Says(kActorOfficerGrayford, 330, 13); + Actor_Set_Goal_Number(kActorOfficerGrayford, 7); + break; + + case 8: + AI_Movement_Track_Flush(kActorOfficerGrayford); + Actor_Set_Goal_Number(kActorOfficerGrayford, 99); + Actor_Face_Actor(kActorMcCoy, kActorOfficerGrayford, true); + Actor_Face_Actor(kActorOfficerGrayford, kActorMcCoy, true); + Actor_Says(kActorMcCoy, 5075, 13); + Actor_Set_Goal_Number(kActorOfficerGrayford, 8); + break; + + case 104: + Actor_Set_Goal_Number(kActorOfficerGrayford, 199); + Actor_Face_Actor(kActorMcCoy, kActorOfficerGrayford, true); + Actor_Says(kActorMcCoy, 1005, kAnimationModeTalk); + AI_Movement_Track_Flush(kActorOfficerGrayford); + AI_Countdown_Timer_Reset(kActorOfficerGrayford, 2); + if (_animationState == 35 || _animationState == 34) { + _animationState = 37; + _animationFrame = 0; + } + Actor_Face_Actor(kActorOfficerGrayford, kActorMcCoy, true); + Actor_Says(kActorOfficerGrayford, 190, 19); + Actor_Set_Goal_Number(kActorOfficerGrayford, 104); + break; + + case 105: + Actor_Set_Goal_Number(kActorOfficerGrayford, 199); + Actor_Face_Actor(kActorMcCoy, kActorOfficerGrayford, true); + Actor_Says(kActorMcCoy, 1005, kAnimationModeTalk); + AI_Movement_Track_Flush(kActorOfficerGrayford); + AI_Countdown_Timer_Reset(kActorOfficerGrayford, 2); + if (_animationState == 35 || _animationState == 34) { + _animationState = 37; + _animationFrame = 0; + } + Actor_Face_Actor(kActorOfficerGrayford, kActorMcCoy, 1); + Actor_Says(kActorOfficerGrayford, 190, 19); + Actor_Set_Goal_Number(kActorOfficerGrayford, 105); + break; + + default: + return; //false; + break; + } + + return; //true; +} + +void AIScriptOfficerGrayford::EnteredScene(int sceneId) { + // return false; +} + +void AIScriptOfficerGrayford::OtherAgentEnteredThisScene(int otherActorId) { + // return false; +} + +void AIScriptOfficerGrayford::OtherAgentExitedThisScene(int otherActorId) { + // return false; +} + +void AIScriptOfficerGrayford::OtherAgentEnteredCombatMode(int otherActorId, int combatMode) { + // return false; +} + +void AIScriptOfficerGrayford::ShotAtAndMissed() { + // return false; +} + +bool AIScriptOfficerGrayford::ShotAtAndHit() { + if (Actor_Query_Goal_Number(kActorOfficerGrayford) == 307) + Actor_Set_Health(kActorOfficerGrayford, 50, 50); + + return false; +} + +void AIScriptOfficerGrayford::Retired(int byActorId) { + Actor_Set_Goal_Number(kActorOfficerGrayford, 599); + Game_Flag_Set(607); +} + +int AIScriptOfficerGrayford::GetFriendlinessModifierIfGetsClue(int otherActorId, int clueId) { + return 0; +} + +bool AIScriptOfficerGrayford::GoalChanged(int currentGoalNumber, int newGoalNumber) { + switch (newGoalNumber) { + case 1: + AI_Movement_Track_Flush(kActorOfficerGrayford); + AI_Movement_Track_Append(kActorOfficerGrayford, 82, Random_Query(5, 20)); + AI_Movement_Track_Repeat(kActorOfficerGrayford); + return true; + + case 2: + AI_Movement_Track_Flush(kActorOfficerGrayford); + AI_Movement_Track_Append(kActorOfficerGrayford, 76, Random_Query(10, 20)); + AI_Movement_Track_Repeat(kActorOfficerGrayford); + return true; + + case 3: + AI_Movement_Track_Flush(kActorOfficerGrayford); + AI_Movement_Track_Append(kActorOfficerGrayford, 77, Random_Query(5, 15)); + AI_Movement_Track_Repeat(kActorOfficerGrayford); + return true; + + case 4: + AI_Movement_Track_Flush(kActorOfficerGrayford); + AI_Movement_Track_Append(kActorOfficerGrayford, 78, Random_Query(5, 15)); + AI_Movement_Track_Repeat(kActorOfficerGrayford); + return true; + + case 5: + AI_Movement_Track_Flush(kActorOfficerGrayford); + AI_Movement_Track_Append(kActorOfficerGrayford, 35, 30); + AI_Movement_Track_Repeat(kActorOfficerGrayford); + return true; + + case 6: + AI_Movement_Track_Flush(kActorOfficerGrayford); + AI_Movement_Track_Append(kActorOfficerGrayford, 79, Random_Query(5, 15)); + AI_Movement_Track_Repeat(kActorOfficerGrayford); + return true; + + case 7: + AI_Movement_Track_Flush(kActorOfficerGrayford); + AI_Movement_Track_Append(kActorOfficerGrayford, 80, 1); + AI_Movement_Track_Repeat(kActorOfficerGrayford); + return true; + + case 8: + AI_Movement_Track_Flush(kActorOfficerGrayford); + AI_Movement_Track_Append(kActorOfficerGrayford, 81, Random_Query(5, 15)); + AI_Movement_Track_Repeat(kActorOfficerGrayford); + return true; + + case 9: + AI_Movement_Track_Flush(kActorOfficerGrayford); + AI_Movement_Track_Append(kActorOfficerGrayford, 82, Random_Query(5, 15)); + AI_Movement_Track_Repeat(kActorOfficerGrayford); + return true; + + case 101: + Player_Loses_Control(); + AI_Movement_Track_Flush(kActorOfficerGrayford); + Actor_Put_In_Set(kActorOfficerGrayford, kSetDR01_DR02_DR04); + Actor_Set_At_Waypoint(kActorOfficerGrayford, 110, 0); + Actor_Face_Actor(kActorMcCoy, kActorOfficerGrayford, true); + Loop_Actor_Walk_To_Waypoint(kActorOfficerGrayford, 111, 0, false, true); + + _animationState = 23; + _animationFrame = kActorMcCoy; + + Actor_Face_Actor(kActorOfficerGrayford, kActorMcCoy, true); + Actor_Face_Actor(kActorMcCoy, kActorOfficerGrayford, true); + + if (Game_Flag_Query(713)) { + Actor_Set_Goal_Number(kActorMcCoy, 500); + } else { + Actor_Says(kActorMcCoy, 960, 15); + Actor_Says(kActorMcCoy, 965, 18); + _animationState = 24; + } + return true; + + case 102: + return true; + + case 103: + Actor_Says(kActorOfficerGrayford, 120, 19); + Actor_Says_With_Pause(kActorMcCoy, 970, 0.2f, 13); + Actor_Says(kActorMcCoy, 975, 12); + + if (Actor_Clue_Query(kActorMcCoy, kClueMorajiInterview) == 1) { + Actor_Says(kActorMcCoy, 980, 16); + Actor_Says_With_Pause(kActorOfficerGrayford, 130, 0.1f, 13); + Actor_Says(kActorMcCoy, 985, 14); + Actor_Says_With_Pause(kActorMcCoy, 990, 0.0f, 17); + Actor_Says_With_Pause(kActorOfficerGrayford, 140, 1.0f, 16); + Actor_Says_With_Pause(kActorOfficerGrayford, 150, 0.0f, 17); + Actor_Says(kActorOfficerGrayford, 160, 15); + Actor_Says_With_Pause(kActorMcCoy, 995, 0.3f, 14); + } + + Player_Gains_Control(); + + if (Actor_Query_Goal_Number(kActorMoraji) == 23) { + Actor_Face_Actor(kActorOfficerGrayford, kActorMoraji, 1); + } else { + Actor_Face_Waypoint(kActorOfficerGrayford, 97, 1); + } + + Actor_Change_Animation_Mode(kActorOfficerGrayford, 43); + + if (Player_Query_Current_Scene() == 28) { + Actor_Says(kActorOfficerGrayford, 170, kAnimationModeTalk); + } + return true; + + case 104: + AI_Countdown_Timer_Reset(kActorOfficerGrayford, 2); + AI_Movement_Track_Flush(kActorOfficerGrayford); + AI_Movement_Track_Append(kActorOfficerGrayford, 112, 0); + AI_Movement_Track_Repeat(kActorOfficerGrayford); + return true; + + case 105: + AI_Countdown_Timer_Reset(kActorOfficerGrayford, 2); + AI_Movement_Track_Flush(kActorOfficerGrayford); + AI_Movement_Track_Append(kActorOfficerGrayford, 113, 0); + AI_Movement_Track_Repeat(kActorOfficerGrayford); + return true; + + case 106: + Actor_Face_Actor(kActorMcCoy, kActorOfficerGrayford, true); + Actor_Says(kActorMcCoy, 1000, 14); + AI_Movement_Track_Flush(kActorOfficerGrayford); + AI_Countdown_Timer_Reset(kActorOfficerGrayford, 2); + + if (_animationState == 35 || _animationState == 34) { + _animationState = 37; + _animationFrame = 0; + } + + Actor_Face_Actor(kActorOfficerGrayford, kActorMcCoy, true); + Actor_Says(kActorOfficerGrayford, 180, 18); + Actor_Set_Goal_Number(kActorOfficerGrayford, currentGoalNumber); + break; + + case 110: + AI_Movement_Track_Flush(kActorOfficerGrayford); + AI_Movement_Track_Append(kActorOfficerGrayford, 35, 0); + AI_Movement_Track_Repeat(kActorOfficerGrayford); + AI_Movement_Track_Flush(kActorMoraji); + AI_Movement_Track_Append(kActorMoraji, 41, 0); + AI_Movement_Track_Repeat(kActorMoraji); + return true; + + case 300: + Actor_Set_Goal_Number(kActorOfficerGrayford, 305); + return true; + + case 305: + AI_Movement_Track_Flush(kActorOfficerGrayford); + switch (Random_Query(1, 10)) { + case 1: + AI_Movement_Track_Append(kActorOfficerGrayford, 398, 15); + AI_Movement_Track_Append(kActorOfficerGrayford, 399, 0); + AI_Movement_Track_Append(kActorOfficerGrayford, 400, 0); + AI_Movement_Track_Append(kActorOfficerGrayford, 401, 0); + AI_Movement_Track_Append_With_Facing(kActorOfficerGrayford, 402, 3, 276); + AI_Movement_Track_Append(kActorOfficerGrayford, 403, 0); + AI_Movement_Track_Append(kActorOfficerGrayford, 404, 15); + AI_Movement_Track_Repeat(kActorOfficerGrayford); + Actor_Set_Goal_Number(kActorOfficerGrayford, 305); + return true; + + case 2: + AI_Movement_Track_Append(kActorOfficerGrayford, 385, 10); + AI_Movement_Track_Append(kActorOfficerGrayford, 242, 2); + AI_Movement_Track_Append(kActorOfficerGrayford, 386, 2); + AI_Movement_Track_Append(kActorOfficerGrayford, 387, 15); + AI_Movement_Track_Repeat(kActorOfficerGrayford); + Actor_Set_Goal_Number(kActorOfficerGrayford, 305); + return true; + + case 3: + AI_Movement_Track_Append(kActorOfficerGrayford, 390, 10); + AI_Movement_Track_Append(kActorOfficerGrayford, 391, 0); + AI_Movement_Track_Append(kActorOfficerGrayford, 392, 5); + AI_Movement_Track_Append(kActorOfficerGrayford, 345, 0); + AI_Movement_Track_Append(kActorOfficerGrayford, 393, 15); + AI_Movement_Track_Repeat(kActorOfficerGrayford); + Actor_Set_Goal_Number(kActorOfficerGrayford, 305); + return true; + + case 4: + AI_Movement_Track_Append(kActorOfficerGrayford, 381, 15); + AI_Movement_Track_Append(kActorOfficerGrayford, 382, 0); + AI_Movement_Track_Append(kActorOfficerGrayford, 383, 15); + AI_Movement_Track_Append(kActorOfficerGrayford, 382, 3); + AI_Movement_Track_Append(kActorOfficerGrayford, 384, 0); + AI_Movement_Track_Append(kActorOfficerGrayford, 35, 30); + AI_Movement_Track_Repeat(kActorOfficerGrayford); + Actor_Set_Goal_Number(kActorOfficerGrayford, 305); + return true; + + case 5: + AI_Movement_Track_Append(kActorOfficerGrayford, 388, 10); + AI_Movement_Track_Append(kActorOfficerGrayford, 389, 10); + AI_Movement_Track_Append(kActorOfficerGrayford, 35, 30); + AI_Movement_Track_Repeat(kActorOfficerGrayford); + Actor_Set_Goal_Number(kActorOfficerGrayford, 305); + return true; + + case 6: + AI_Movement_Track_Append(kActorOfficerGrayford, 385, 10); + AI_Movement_Track_Append(kActorOfficerGrayford, 242, 2); + AI_Movement_Track_Append(kActorOfficerGrayford, 386, 2); + AI_Movement_Track_Append(kActorOfficerGrayford, 387, 15); + AI_Movement_Track_Repeat(kActorOfficerGrayford); + Actor_Set_Goal_Number(kActorOfficerGrayford, 305); + return true; + + case 7: + AI_Movement_Track_Append(kActorOfficerGrayford, 394, 15); + AI_Movement_Track_Append(kActorOfficerGrayford, 395, 0); + AI_Movement_Track_Append(kActorOfficerGrayford, 396, 0); + AI_Movement_Track_Append(kActorOfficerGrayford, 397, 15); + AI_Movement_Track_Append(kActorOfficerGrayford, 396, 0); + AI_Movement_Track_Append(kActorOfficerGrayford, 395, 0); + AI_Movement_Track_Append(kActorOfficerGrayford, 430, 15); + AI_Movement_Track_Append(kActorOfficerGrayford, 35, 30); + AI_Movement_Track_Repeat(kActorOfficerGrayford); + Actor_Set_Goal_Number(kActorOfficerGrayford, 305); + return true; + + case 8: + switch (Random_Query(1, 7)) { + case 1: + AI_Movement_Track_Append(kActorOfficerGrayford, 302, 0); + AI_Movement_Track_Append(kActorOfficerGrayford, 407, 0); + AI_Movement_Track_Append(kActorOfficerGrayford, 408, 0); + AI_Movement_Track_Append(kActorOfficerGrayford, 35, 30); + AI_Movement_Track_Repeat(kActorOfficerGrayford); + break; // and go to case 9 below + + case 2: + AI_Movement_Track_Append(kActorOfficerGrayford, 536, 0); + AI_Movement_Track_Append(kActorOfficerGrayford, 537, 0); + AI_Movement_Track_Append(kActorOfficerGrayford, 538, 5); + AI_Movement_Track_Append(kActorOfficerGrayford, 537, 0); + AI_Movement_Track_Append(kActorOfficerGrayford, 536, 0); + AI_Movement_Track_Repeat(kActorOfficerGrayford); + break; // and go to case 9 below + + case 3: + AI_Movement_Track_Append(kActorOfficerGrayford, 296, 10); + AI_Movement_Track_Append(kActorOfficerGrayford, 409, 2); + AI_Movement_Track_Append(kActorOfficerGrayford, 296, 10); + AI_Movement_Track_Append(kActorOfficerGrayford, 35, 30); + AI_Movement_Track_Repeat(kActorOfficerGrayford); + break; // and go to case 9 below + + case 4: + AI_Movement_Track_Append(kActorOfficerGrayford, 411, 10); + AI_Movement_Track_Append(kActorOfficerGrayford, 412, 5); + AI_Movement_Track_Append(kActorOfficerGrayford, 411, 0); + AI_Movement_Track_Append(kActorOfficerGrayford, 35, 30); + AI_Movement_Track_Repeat(kActorOfficerGrayford); + break; // and go to case 9 below + + case 5: + AI_Movement_Track_Append(kActorOfficerGrayford, 413, 10); + AI_Movement_Track_Append(kActorOfficerGrayford, 414, 0); + AI_Movement_Track_Append_With_Facing(kActorOfficerGrayford, 431, 0, 1017); + AI_Movement_Track_Append(kActorOfficerGrayford, 432, 10); + AI_Movement_Track_Append(kActorOfficerGrayford, 35, 30); + AI_Movement_Track_Repeat(kActorOfficerGrayford); + break; // and go to case 9 below + + case 6: + AI_Movement_Track_Append(kActorOfficerGrayford, 415, 0); + AI_Movement_Track_Append_With_Facing(kActorOfficerGrayford, 416, 0, 620); + AI_Movement_Track_Append(kActorOfficerGrayford, 417, 0); + AI_Movement_Track_Append(kActorOfficerGrayford, 418, 0); + AI_Movement_Track_Append(kActorOfficerGrayford, 35, 30); + AI_Movement_Track_Repeat(kActorOfficerGrayford); + break; // and go to case 9 below + + case 7: + AI_Movement_Track_Append(kActorOfficerGrayford, 405, 10); + AI_Movement_Track_Append(kActorOfficerGrayford, 406, 0); + AI_Movement_Track_Append(kActorOfficerGrayford, 35, 30); + AI_Movement_Track_Repeat(kActorOfficerGrayford); + return false; + + } + // fall through + // TODO bug in the game? there should be nothing track related after AI_Movement_Track_Repeat + + case 9: + if (Random_Query(0, 1)) { + AI_Movement_Track_Append(kActorOfficerGrayford, 433, 10); + AI_Movement_Track_Append(kActorOfficerGrayford, 434, 0); + AI_Movement_Track_Append(kActorOfficerGrayford, 435, 0); + AI_Movement_Track_Append(kActorOfficerGrayford, 35, 30); + AI_Movement_Track_Repeat(kActorOfficerGrayford); + } else { + AI_Movement_Track_Append(kActorOfficerGrayford, 420, 10); + AI_Movement_Track_Append(kActorOfficerGrayford, 422, 2); + AI_Movement_Track_Append(kActorOfficerGrayford, 421, 1); + AI_Movement_Track_Append_With_Facing(kActorOfficerGrayford, 422, 4, 182); + AI_Movement_Track_Append(kActorOfficerGrayford, 420, 10); + AI_Movement_Track_Append(kActorOfficerGrayford, 35, 30); + AI_Movement_Track_Repeat(kActorOfficerGrayford); + // TODO bug in the game? Same code bellow looks like a case 10 and are from set 84 whereas upper one are from set 81 + AI_Movement_Track_Append(kActorOfficerGrayford, 310, 0); + AI_Movement_Track_Append(kActorOfficerGrayford, 307, 0); + AI_Movement_Track_Append(kActorOfficerGrayford, 309, 0); + AI_Movement_Track_Append(kActorOfficerGrayford, 310, 0); + AI_Movement_Track_Append(kActorOfficerGrayford, 35, 30); + AI_Movement_Track_Repeat(kActorOfficerGrayford); + } + return false; + + case 10: + AI_Movement_Track_Append(kActorOfficerGrayford, 310, 0); + AI_Movement_Track_Append(kActorOfficerGrayford, 307, 0); + AI_Movement_Track_Append(kActorOfficerGrayford, 309, 0); + AI_Movement_Track_Append(kActorOfficerGrayford, 310, 0); + AI_Movement_Track_Append(kActorOfficerGrayford, 35, 30); + AI_Movement_Track_Repeat(kActorOfficerGrayford); + return false; + + } + return false; + + case 306: + Actor_Set_Goal_Number(kActorOfficerGrayford, 305); + return true; + + case 307: + AI_Movement_Track_Flush(kActorOfficerGrayford); + AI_Movement_Track_Append_With_Facing(kActorOfficerGrayford, 419, 0, 512); + AI_Movement_Track_Repeat(kActorOfficerGrayford); + return true; + + case 308: + AI_Movement_Track_Flush(kActorOfficerGrayford); + AI_Movement_Track_Append_Run(kActorOfficerGrayford, 440, 0); + AI_Movement_Track_Append_Run(kActorOfficerGrayford, 441, 0); + AI_Movement_Track_Repeat(kActorOfficerGrayford); + return true; + + case 399: + AI_Movement_Track_Flush(kActorOfficerGrayford); + Actor_Put_In_Set(kActorOfficerGrayford, kSetTB02_TB03); + Actor_Set_At_XYZ(kActorOfficerGrayford, -173.89f, 0.0f, 2084.22f, 859); + Actor_Change_Animation_Mode(kActorOfficerGrayford, kAnimationModeCombatIdle); + return true; + + case 599: + _animationState = 32; + _animationFrame = Slice_Animation_Query_Number_Of_Frames(624) - 1; + return true; + + } + return false; +} + +bool AIScriptOfficerGrayford::UpdateAnimation(int *animation, int *frame) { + switch (_animationState) { + case 0: + if (!_var1) { + *animation = 625; + } + if (_var1 == 1) { + *animation = 626; + } + if (_var1 == 2) { + *animation = 627; + } + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(*animation)) { + _animationFrame = 0; + _var1 = 0; + if (!Random_Query(0, 1)) { + _var1 = Random_Query(1, 2); + } + } + break; + + case 1: + *animation = 618; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(618)) { + _animationFrame = 0; + } + break; + + case 2: + *animation = 619; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(619)) { + _animationFrame = 0; + } + break; + + case 5: + *animation = 611; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(611)) { + _animationFrame = 0; + } + break; + + case 6: + *animation = 610; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(610)) { + _animationFrame = 0; + } + break; + + case 9: + if (!_animationFrame && _var2) { + *animation = 625; + _animationState = 0; + _var1 = 0; + _var2 = 0; + } else { + *animation = 629; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(629)) { + _animationFrame = 0; + _animationState = Random_Query(9, 11); + } + } + break; + + case 10: + *animation = 630; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(630)) { + _animationFrame = 0; + _animationState = 9; + *animation = 629; + } + break; + + case 11: + *animation = 631; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(631)) { + _animationFrame = 0; + _animationState = 9; + *animation = 629; + } + break; + + case 12: + *animation = 632; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(632)) { + _animationFrame = 0; + _animationState = 9; + *animation = 629; + } + break; + + case 13: + *animation = 633; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(633)) { + _animationFrame = 0; + _animationState = 9; + *animation = 629; + } + break; + + case 14: + *animation = 634; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(634)) { + _animationFrame = 0; + _animationState = 9; + *animation = 629; + } + break; + + case 15: + *animation = 635; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(635)) { + _animationFrame = 0; + _animationState = 9; + *animation = 629; + } + break; + + case 16: + *animation = 636; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(636)) { + _animationFrame = 0; + _animationState = 9; + *animation = 629; + } + break; + + case 17: + *animation = 637; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(637)) { + _animationFrame = 0; + _animationState = 9; + *animation = 629; + } + break; + + case 18: + case 19: + *animation = 605; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(605)) { + _animationFrame = 0; + } + break; + + case 20: + *animation = 615; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(615)) { + _animationFrame = 0; + _animationState = 19; + } + break; + + case 21: + *animation = 616; + _animationFrame++; + if (_animationFrame == 11) { + Ambient_Sounds_Play_Sound(556, 25, 0, 0, 25); + } + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(*animation)) { + *animation = 625; + _animationFrame = 0; + _animationState = 0; + _var1 = 0; + if (Actor_Query_Goal_Number(kActorOfficerGrayford) == 101) { + Actor_Set_Goal_Number(kActorOfficerGrayford, 102); + } + } + break; + + case 22: + *animation = 617; + _animationFrame++; + if (_animationFrame == 3) { + int snd; + if (Random_Query(1, 2) == 1) { + snd = 9010; + } else { + snd = 9015; + } + Sound_Play_Speech_Line(kActorOfficerGrayford, snd, 75, 0, 99); + } + if (_animationFrame == 5) { + Actor_Combat_AI_Hit_Attempt(kActorOfficerGrayford); + } + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(617)) { + _animationState = 19; + _animationFrame = 0; + Actor_Change_Animation_Mode(kActorOfficerGrayford, 4); + } + break; + + case 23: + *animation = 617; + if (_animationFrame < 2) { + _animationFrame++; + } + break; + + case 24: + *animation = 617; + _animationFrame--; + if (_animationFrame < 0) { + _animationFrame = 0; + _animationState = 21; + *animation = 616; + } + break; + + case 27: + *animation = 608; + _animationFrame++; + if (_animationFrame > Slice_Animation_Query_Number_Of_Frames(608) - 1) { + _animationFrame = 0; + _animationState = 19; + *animation = 625; + Actor_Change_Animation_Mode(kActorOfficerGrayford, 4); + } + break; + + case 28: + *animation = 609; + _animationFrame++; + if (_animationFrame > Slice_Animation_Query_Number_Of_Frames(609) - 1) { + _animationFrame = 0; + _animationState = 19; + *animation = 625; + Actor_Change_Animation_Mode(kActorOfficerGrayford, 4); + } + break; + + case 29: + *animation = 622; + _animationFrame++; + if (_animationFrame > Slice_Animation_Query_Number_Of_Frames(622) - 1) { + *animation = 605; + _animationFrame = 0; + _animationState = 0; + Actor_Change_Animation_Mode(kActorOfficerGrayford, 0); + } + break; + + case 30: + *animation = 623; + _animationFrame++; + if (_animationFrame > Slice_Animation_Query_Number_Of_Frames(623) - 1) { + *animation = 605; + _animationFrame = 0; + _animationState = 0; + Actor_Change_Animation_Mode(kActorOfficerGrayford, 0); + } + break; + + case 31: + *animation = 612; + if (_animationFrame < Slice_Animation_Query_Number_Of_Frames(612) - 1) { + _animationFrame++; + } + break; + + case 32: + *animation = 624; + if (_animationFrame < Slice_Animation_Query_Number_Of_Frames(624) - 1) { + _animationFrame++; + } + break; + + case 34: + *animation = 639; + if (_var3) { + _var3--; + } else { + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(639)) { + if (Random_Query(0, 1)) { + *animation = 641; + _animationState = 37; + } else { + *animation = 638; + _animationState = 35; + } + _animationFrame = 0; + } else { + if (_animationFrame == 12) { + _var3 = Random_Query(5, 18); + } + } + } + break; + + case 35: + *animation = 638; + if (_var3) { + _var3--; + } else { + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(638)) { + if (Random_Query(0, 1)) { + *animation = 641; + _animationState = 37; + } else { + *animation = 639; + _animationState = 34; + } + _animationFrame = 0; + } else { + if (_animationFrame == 10) { + _var3 = Random_Query(5, 18); + } + } + } + break; + + case 36: + *animation = 640; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(640)) { + _animationFrame = 0; + _animationState = 34; + *animation = 639; + } + break; + + case 37: + *animation = 641; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(641)) { + *animation = 625; + _animationState = 0; + _animationFrame = 0; + + switch (Actor_Query_Goal_Number(kActorOfficerGrayford)) { + case 103: + case 104: + Actor_Set_Goal_Number(kActorOfficerGrayford, 105); + break; + + case 105: + Actor_Set_Goal_Number(kActorOfficerGrayford, 104); + break; + } + } + break; + + default: + *animation = 399; + break; + } + *frame = _animationFrame; + + return true; +} + +bool AIScriptOfficerGrayford::ChangeAnimationMode(int mode) { + switch (mode) { + case kAnimationModeIdle: + switch (_animationState) { + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + case 16: + case 17: + _var2 = 1; + break; + + case 18: + { + int tmp = _animationFrame; + Actor_Change_Animation_Mode(kActorOfficerGrayford, 4); + _animationFrame = tmp; + _animationState = 19; + break; + } + + case 19: + _animationState = 21; + _animationFrame = 0; + break; + + case 20: + case 21: + case 34: + case 35: + case 36: + case 37: + return true; + + default: + _animationState = 0; + _animationFrame = 0; + break; + } + break; + + case kAnimationModeWalk: + if (Actor_Query_Goal_Number(kActorOfficerGrayford) == 101) { + _animationState = 6; + _animationFrame = 0; + } else if (_animationState != 1) { + _animationState = 1; + _animationFrame = 0; + } + break; + + case kAnimationModeRun: + if (Actor_Query_Goal_Number(kActorOfficerGrayford) == 101) { + _animationState = 5; + _animationFrame = 0; + } else if (_animationState != 2) { + _animationState = 2; + _animationFrame = 0; + } + break; + + case kAnimationModeTalk: + if (_animationState != 36 && _animationState != 34) { + _animationState = 9; + _animationFrame = 0; + _var2 = 0; + } + break; + + case kAnimationModeCombatIdle: + switch (_animationState) { + case 5: + case 6: + case 7: + case 8: + _animationState = 19; + _animationFrame = 0; + break; + + case 19: + case 20: + case 22: + case 24: + return true; + + case 23: + _animationState = 24; + _animationFrame = 0; + break; + + default: + _animationState = 20; + _animationFrame = 0; + } + break; + + case kAnimationModeCombatAim: + _animationState = 23; + _animationFrame = 0; + break; + + case kAnimationModeCombatAttack: + _animationFrame = 0; + _animationState = 22; + break; + + case kAnimationModeCombatWalk: + _animationState = 6; + _animationFrame = 0; + break; + + case kAnimationModeCombatRun: + _animationState = 5; + _animationFrame = 0; + break; + + case 12: + _animationState = 10; + _animationFrame = 0; + _var2 = 0; + break; + + case 13: + _animationState = 11; + _animationFrame = 0; + _var2 = 0; + break; + + case 14: + _animationState = 12; + _animationFrame = 0; + _var2 = 0; + break; + + case 15: + _animationState = 13; + _animationFrame = 0; + _var2 = 0; + break; + + case 16: + _animationState = 14; + _animationFrame = 0; + _var2 = 0; + break; + + case 17: + _animationState = 15; + _animationFrame = 0; + _var2 = 0; + break; + + case 18: + _animationState = 16; + _animationFrame = 0; + _var2 = 0; + break; + + case 19: + _animationState = 17; + _animationFrame = 0; + _var2 = 0; + break; + + case kAnimationModeHit: + switch (_animationState) { + case 19: + case 20: + case 22: + case 23: + case 24: + case 25: + case 26: + if (Random_Query(0, 1)) { + _animationState = 27; + } else { + _animationState = 28; + } + _animationFrame = 0; + break; + + case 21: + if (Random_Query(0, 1)) { + _animationState = 29; + } else { + _animationState = 30; + } + _animationFrame = 0; + break; + } + break; + + case kAnimationModeCombatHit: + if (Random_Query(0, 1)) { + _animationState = 27; + } else { + _animationState = 28; + } + _animationFrame = 0; + break; + + case 43: + _animationState = 36; + _animationFrame = 0; + break; + + case kAnimationModeWalkUp: + _animationState = 3; + _animationFrame = 0; + break; + + case kAnimationModeWalkDown: + _animationState = 4; + _animationFrame = 0; + break; + + case kAnimationModeCombatWalkUp: + _animationState = 7; + _animationFrame = 0; + break; + + case kAnimationModeCombatWalkDown: + _animationState = 7; + _animationFrame = 0; + break; + + case kAnimationModeDie: + switch (_animationState) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 7: + _animationState = 20; + _animationFrame = 0; + break; + + case 5: + case 6: + return true; + + default: + _animationState = 32; + _animationFrame = 0; + break; + } + break; + + case 58: + _animationState = 18; + _animationFrame = 0; + break; + + } + + return true; +} + +void AIScriptOfficerGrayford::QueryAnimationState(int *animationState, int *animationFrame, int *animationStateNext, int *animationNext) { + *animationState = _animationState; + *animationFrame = _animationFrame; + *animationStateNext = _animationStateNext; + *animationNext = _animationNext; +} + +void AIScriptOfficerGrayford::SetAnimationState(int animationState, int animationFrame, int animationStateNext, int animationNext) { + _animationState = animationState; + _animationFrame = animationFrame; + _animationStateNext = animationStateNext; + _animationNext = animationNext; +} + +bool AIScriptOfficerGrayford::ReachedMovementTrackWaypoint(int waypointId) { + return true; +} + +void AIScriptOfficerGrayford::FledCombat() { + // return false; +} + +} // End of namespace BladeRunner diff --git a/engines/bladerunner/script/ai/officer_leary.cpp b/engines/bladerunner/script/ai/officer_leary.cpp index b18ba0869f..0adbe7c1c0 100644 --- a/engines/bladerunner/script/ai/officer_leary.cpp +++ b/engines/bladerunner/script/ai/officer_leary.cpp @@ -979,7 +979,7 @@ bool AIScriptOfficerLeary::ChangeAnimationMode(int mode) { break; } break; - case kAnimationModeCombatShoot: + case kAnimationModeCombatAttack: _animationState = 24; _animationFrame = 0; break; diff --git a/engines/bladerunner/script/ai/rajif.cpp b/engines/bladerunner/script/ai/rajif.cpp index 2ac6a5bed5..26cf41282f 100644 --- a/engines/bladerunner/script/ai/rajif.cpp +++ b/engines/bladerunner/script/ai/rajif.cpp @@ -115,7 +115,7 @@ bool AIScriptRajif::UpdateAnimation(int *animation, int *frame) { if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(751)) { _animationFrame = 0; } - } else { + } else { // bug in original. Both branches are equal *animation = 751; _animationFrame++; if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(751)) { diff --git a/engines/bladerunner/script/ai/sadik.cpp b/engines/bladerunner/script/ai/sadik.cpp new file mode 100644 index 0000000000..9fdb889b6c --- /dev/null +++ b/engines/bladerunner/script/ai/sadik.cpp @@ -0,0 +1,1039 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can 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 "bladerunner/script/ai_script.h" + +namespace BladeRunner { + +AIScriptSadik::AIScriptSadik(BladeRunnerEngine *vm) : AIScriptBase(vm) { + _flag = 0; + _var1 = 0; + _var2 = 0; + _var3 = 0; + _var4 = 1; +} + +void AIScriptSadik::Initialize() { + _animationFrame = 0; + _animationState = 0; + _animationStateNext = 0; + _animationNext = 0; + + _flag = 0; + _var1 = 0; + _var2 = 0; + _var3 = 0; + _var4 = 1; + + Actor_Put_In_Set(kActorSadik, kSetFreeSlotA); + Actor_Set_At_Waypoint(kActorSadik, 33, 0); + Actor_Set_Goal_Number(kActorSadik, 100); +} + +bool AIScriptSadik::Update() { + if (Global_Variable_Query(kVariableChapter) != 2 || Player_Query_Current_Scene() != 10 || Game_Flag_Query(391)) { + if (_var1) { + Sound_Play(_var1, 100, 0, 0, 50); + _var1 = 0; + } + if (Global_Variable_Query(kVariableChapter) == 3 && Actor_Query_Goal_Number(kActorSadik) < 200) { + Actor_Set_Goal_Number(kActorSadik, 200); + } + if (Global_Variable_Query(kVariableChapter) == 5 && Actor_Query_Goal_Number(kActorSadik) < 400) { + Actor_Set_Goal_Number(kActorSadik, 400); + } + if (Actor_Query_Goal_Number(kActorSadik) == 411) { + if (Game_Flag_Query(657)) { + Actor_Set_Goal_Number(kActorSadik, 412); + } + } + return false; + } else { + Actor_Set_Goal_Number(kActorSadik, 101); + Actor_Set_Targetable(kActorSadik, 1); + Game_Flag_Set(391); + Game_Flag_Set(406); + return true; + } +} + +void AIScriptSadik::TimerExpired(int timer) { + if (!timer) { + AI_Countdown_Timer_Reset(kActorSadik, 0); + + switch (Actor_Query_Goal_Number(kActorSadik)) { + case 302: + Actor_Set_Goal_Number(kActorSadik, 305); + break; + + case 303: + Actor_Set_Goal_Number(kActorSadik, 305); + break; + + case 307: + Actor_Set_Goal_Number(kActorSadik, 308); + break; + } + } +} + +void AIScriptSadik::CompletedMovementTrack() { + switch (Actor_Query_Goal_Number(kActorSadik)) { + case 301: + Actor_Set_Goal_Number(kActorSadik, 302); + break; + + case 101: + Actor_Set_Goal_Number(kActorSadik, 102); + break; + + case 104: + Actor_Set_Goal_Number(kActorSadik, 105); + break; + + case 105: + Actor_Set_Goal_Number(kActorSadik, 106); + break; + + default: + return; //false; + } + + return; //true; +} + +void AIScriptSadik::ReceivedClue(int clueId, int fromActorId) { + //return false; +} + +void AIScriptSadik::ClickedByPlayer() { + if (Actor_Query_Goal_Number(kActorSadik) == 599) { + Actor_Face_Actor(kActorMcCoy, kActorSadik, 1); + Actor_Says(kActorMcCoy, 8580, 16); + } +} + +void AIScriptSadik::EnteredScene(int sceneId) { + // return false; +} + +void AIScriptSadik::OtherAgentEnteredThisScene(int otherActorId) { + // return false; +} + +void AIScriptSadik::OtherAgentExitedThisScene(int otherActorId) { + // return false; +} + +void AIScriptSadik::OtherAgentEnteredCombatMode(int otherActorId, int combatMode) { + // return false; +} + +void AIScriptSadik::ShotAtAndMissed() { + if (Actor_Query_Goal_Number(kActorSadik) == 414 || Actor_Query_Goal_Number(kActorSadik) == 416) { + Game_Flag_Set(714); + if (Actor_Query_Which_Set_In(kActorSadik) != 48) { + Actor_Set_Goal_Number(kActorSadik, 418); + Scene_Exits_Disable(); + } + } +} + +bool AIScriptSadik::ShotAtAndHit() { + if (Actor_Query_Goal_Number(kActorSadik) == 301) { + if (Game_Flag_Query(48)) { + Actor_Set_Health(kActorSadik, 60, 60); + } else { + Actor_Set_Health(kActorSadik, 40, 40); + } + return true; + } else { + if (Actor_Query_Goal_Number(kActorSadik) == 414 || Actor_Query_Goal_Number(kActorSadik) == 416) { + Game_Flag_Set(714); + if (Actor_Query_Which_Set_In(kActorSadik) != 48) { + Actor_Set_Goal_Number(kActorSadik, 418); + Scene_Exits_Disable(); + } + } + return false; + } +} + +void AIScriptSadik::Retired(int byActorId) { + if ((Actor_Query_Goal_Number(kActorSadik) == 418 || Actor_Query_Goal_Number(kActorSadik) == 450) + && Actor_Query_Which_Set_In(kActorSadik) != 48) { + Scene_Exits_Enable(); + } + if (Actor_Query_In_Set(kActorSadik, kSetKP07)) { + Global_Variable_Decrement(51, 1); + Actor_Set_Goal_Number(kActorSadik, 599); + + if (!Global_Variable_Query(51)) { + Player_Loses_Control(); + Delay(2000); + Player_Set_Combat_Mode(0); + Loop_Actor_Walk_To_XYZ(kActorMcCoy, -12.0f, -41.58f, 72.0f, 0, 1, 0, 0); + Ambient_Sounds_Remove_All_Non_Looping_Sounds(1); + Ambient_Sounds_Remove_All_Looping_Sounds(1); + Game_Flag_Set(579); + Game_Flag_Reset(653); + Set_Enter(kSetKP05_KP06, kSetKP03); + return; //true; + } + } + + Actor_Set_Goal_Number(kActorSadik, 599); + + return; //false; +} + +int AIScriptSadik::GetFriendlinessModifierIfGetsClue(int otherActorId, int clueId) { + return 0; +} + +bool AIScriptSadik::GoalChanged(int currentGoalNumber, int newGoalNumber) { + switch (newGoalNumber) { + case 100: + AI_Movement_Track_Flush(kActorSadik); + AI_Movement_Track_Append(kActorSadik, 33, 0); + AI_Movement_Track_Repeat(kActorSadik); + return true; + + case 101: + AI_Movement_Track_Flush(kActorSadik); + AI_Movement_Track_Append_Run(kActorSadik, 131, 0); + AI_Movement_Track_Append_Run(kActorSadik, 132, 0); + AI_Movement_Track_Append_Run(kActorSadik, 133, 0); + AI_Movement_Track_Repeat(kActorSadik); + return true; + + case 102: + AI_Movement_Track_Flush(kActorSadik); + AI_Movement_Track_Append(kActorSadik, 313, 0); + AI_Movement_Track_Repeat(kActorSadik); + Game_Flag_Set(509); + return true; + + case 103: + Actor_Set_Immunity_To_Obstacles(kActorSadik, 1); + Actor_Face_Heading(kActorSadik, kActorMcCoy, kActorMcCoy); + _animationState = 32; + _animationFrame = -1; + Actor_Change_Animation_Mode(kActorMcCoy, 48); + return true; + + case 104: + Actor_Set_Goal_Number(kActorMcCoy, 100); + AI_Movement_Track_Flush(kActorSadik); + AI_Movement_Track_Append(kActorSadik, 314, 0); + AI_Movement_Track_Append_Run(kActorSadik, 317, 0); + AI_Movement_Track_Repeat(kActorSadik); + return true; + + case 105: + Actor_Change_Animation_Mode(kActorSadik, 62); + return true; + + case 106: + Actor_Face_Heading(kActorSadik, 100, 0); + Actor_Change_Animation_Mode(kActorSadik, 63); + Actor_Set_Goal_Number(kActorClovis, 101); + Actor_Set_Immunity_To_Obstacles(kActorSadik, 0); + return true; + + case 107: + _var1 = 0; + return false; + + case 200: + Actor_Put_In_Set(kActorSadik, kSetFreeSlotA); + Actor_Set_At_Waypoint(kActorSadik, 33, 0); + Actor_Set_Goal_Number(kActorMcCoy, 199); + return true; + + case 300: + Actor_Put_In_Set(kActorSadik, kSetUG18); + Actor_Set_At_XYZ(kActorSadik, 111.89f, 0.0f, 408.42f, 0); + Actor_Change_Animation_Mode(kActorSadik, 4); + return true; + + case 301: + Actor_Set_Targetable(kActorSadik, 1); + World_Waypoint_Set(436, 89, -356.11f, 0.0f, 652.42f); + AI_Movement_Track_Flush(kActorSadik); + AI_Movement_Track_Append_Run(kActorSadik, 436, 0); + AI_Movement_Track_Repeat(kActorSadik); + return true; + + case 302: + Actor_Set_Targetable(kActorSadik, 0); + return true; + + case 303: + AI_Countdown_Timer_Reset(kActorSadik, 0); + AI_Countdown_Timer_Start(kActorSadik, 0, 5); + return true; + + case 304: + Actor_Set_Targetable(kActorSadik, 0); + AI_Countdown_Timer_Reset(kActorSadik, 0); + return true; + + case 305: + case 306: + case 310: + return true; + + case 307: + Sound_Play(12, 100, 0, 0, 50); + AI_Countdown_Timer_Start(kActorSadik, 0, 2); + return true; + + case 308: + if (Player_Query_Current_Scene() == 102) { + Actor_Force_Stop_Walking(kActorMcCoy); + Actor_Change_Animation_Mode(kActorSadik, 6); + Sound_Play(12, 100, 0, 0, 50); + Actor_Change_Animation_Mode(kActorMcCoy, 48); + Actor_Retired_Here(kActorMcCoy, 6, 6, 1, -1); + } + return true; + + case 309: + AI_Countdown_Timer_Reset(kActorSadik, 0); + return true; + + case 400: + Actor_Set_Goal_Number(kActorSadik, 410); + return true; + + case 410: + if (Game_Flag_Query(653) == 1) { + Actor_Set_Goal_Number(kActorSadik, 414); + } else { + Actor_Set_Goal_Number(kActorSadik, 411); + } + return true; + + case 411: + Actor_Put_In_Set(kActorSadik, kSetKP05_KP06); + Actor_Set_At_XYZ(kActorSadik, -1134.0f, 0.0f, 73.45f, 398); + Actor_Set_Goal_Number(kActorClovis, 513); + Actor_Set_Goal_Number(kActorMaggie, 411); + return true; + + case 412: + Actor_Says(kActorSadik, 60, 3); + Actor_Says(kActorMcCoy, 2240, 3); + Actor_Says(kActorSadik, 70, 3); + Actor_Says(kActorSadik, 80, 3); + Actor_Says(kActorMcCoy, 2245, 3); + Actor_Says(kActorSadik, 90, 3); + Actor_Says(kActorSadik, 100, 3); + Actor_Says(kActorMcCoy, 2250, 3); + Actor_Set_Goal_Number(kActorSadik, 413); + return true; + + case 413: + Loop_Actor_Walk_To_XYZ(kActorSadik, -1062.0f, 0.0f, 219.0f, 0, 0, 1, 0); + Actor_Set_Targetable(kActorSadik, 1); + Non_Player_Actor_Combat_Mode_On(kActorSadik, kActorCombatStateIdle, true, kActorMcCoy, 9, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, 0, -1, -1, 15, 300, false); + Actor_Set_Goal_Number(kActorSadik, 450); + return true; + + case 414: + Actor_Put_In_Set(kActorSadik, kSetKP05_KP06); + Actor_Set_At_XYZ(kActorSadik, -961.0f, 0.0f, -778.0f, 150); + Actor_Set_Targetable(kActorSadik, 1); + return true; + + case 415: + Actor_Says(kActorSadik, 110, 3); + Actor_Says(kActorMcCoy, 2290, 3); + Actor_Says(kActorSadik, 310, 3); + Actor_Says(kActorMcCoy, 2300, 3); + if (Game_Flag_Query(48)) { + Actor_Says(kActorSadik, 180, 3); + Actor_Says(kActorSadik, 190, 3); + Actor_Says(kActorMcCoy, 2310, 3); + Actor_Says(kActorSadik, 200, 3); + } else { + Actor_Says(kActorSadik, 140, 3); + Actor_Says(kActorSadik, 150, 3); + Actor_Says(kActorMcCoy, 2305, 3); + Actor_Says(kActorSadik, 160, 3); + Actor_Says(kActorSadik, 170, 3); + } + Actor_Says(kActorMcCoy, 2315, 3); + Actor_Says(kActorSadik, 210, 3); + Actor_Says(kActorSadik, 220, 3); + Actor_Says(kActorSadik, 230, 3); + Actor_Says(kActorSadik, 240, 3); + Actor_Says(kActorSadik, 250, 3); + Actor_Says(kActorSadik, 260, 3); + Actor_Set_Goal_Number(kActorSadik, 416); + return true; + + case 416: + Loop_Actor_Walk_To_XYZ(kActorSadik, -961.0f, 0.0f, -778.0f, 0, 0, 0, 0); + Actor_Face_Heading(kActorSadik, 150, 0); + return true; + + case 417: + Actor_Face_Actor(kActorSadik, kActorMcCoy, 1); + Actor_Says(kActorSadik, 320, 3); + Loop_Actor_Walk_To_XYZ(kActorSadik, -857.0f, 0.0f, -703.0f, 0, 0, 1, 0); + Actor_Says(kActorMcCoy, 2330, 3); + Actor_Says(kActorSadik, 330, 3); + Actor_Says(kActorMcCoy, 2335, 3); + Actor_Says(kActorSadik, 340, 3); + Actor_Set_Goal_Number(kActorSadik, 416); + return true; + + case 418: + Game_Flag_Reset(653); + Actor_Set_Goal_Number(kActorClovis, 518); + Non_Player_Actor_Combat_Mode_On(kActorSadik, kActorCombatStateIdle, true, kActorMcCoy, 9, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, 0, -1, -1, 15, 300, false); + return true; + + case 419: + Actor_Put_In_Set(kActorSadik, kSetKP07); + Actor_Set_At_XYZ(kActorSadik, -12.0f, -41.58f, 72.0f, 0); + return true; + + case 420: + case 450: + return true; + } + return false; +} + +bool AIScriptSadik::UpdateAnimation(int *animation, int *frame) { + switch (_animationState) { + case 0: + if (_var2 == 1) { + *animation = 329; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(329)) { + *animation = 328; + _animationFrame = 0; + _var2 = 0; + } + } else if (_var2 == 0) { + *animation = 328; + if (_var3) { + _var3--; + if (!Random_Query(0, 6)) { + _var4 = -_var4; + } + } else { + _animationFrame += _var4; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(328)) { + _animationFrame = 0; + } + if (_animationFrame < 0) { + _animationFrame = Slice_Animation_Query_Number_Of_Frames(328) - 1; + } + if (!Random_Query(0, 4)) { + _var3 = 1; + } + if (!_animationFrame || _animationFrame == 8) { + _var3 = Random_Query(2, 8); + } + if (!Random_Query(0, 2)) { + if (!_animationFrame) { + _var2 = 1; + _var3 = 0; + *animation = 329; + } + } + } + } + break; + + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + switch (_animationState) { + case 1: + *animation = 323; + break; + case 2: + *animation = 324; + break; + case 3: + *animation = 317; + break; + case 4: + *animation = 318; + break; + case 6: + *animation = 340; + break; + case 5: + *animation = 339; + break; + } + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(*animation)) { + _animationFrame = 0; + } + break; + + case 7: + *animation = 312; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(312)) { + _animationFrame = 0; + } + break; + + case 8: + *animation = 313; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(313)) { + _animationFrame = 0; + _animationState = 7; + *animation = 312; + Actor_Change_Animation_Mode(kActorSadik, 4); + } + break; + + case 9: + *animation = 314; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(314)) { + _animationFrame = 0; + _animationState = 7; + *animation = 312; + Actor_Change_Animation_Mode(kActorSadik, 4); + } + break; + + case 10: + *animation = 325; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(325)) { + *animation = 328; + _animationFrame = 0; + _animationState = 0; + Actor_Change_Animation_Mode(kActorSadik, 0); + } + break; + + case 11: + *animation = 326; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(326)) { + *animation = 328; + _animationFrame = 0; + _animationState = 0; + Actor_Change_Animation_Mode(kActorSadik, 0); + } + break; + + case 12: + *animation = 315; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(315)) { + _animationFrame = 0; + _animationState = 7; + *animation = 312; + Actor_Change_Animation_Mode(kActorSadik, 4); + } + break; + + case 13: + *animation = 316; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(316)) { + _animationFrame = 0; + _animationState = 7; + *animation = 312; + Actor_Change_Animation_Mode(kActorSadik, 4); + } + break; + + case 14: + *animation = 327; + if (_animationFrame < Slice_Animation_Query_Number_Of_Frames(327) - 1) { + _animationFrame++; + } + break; + + case 15: + *animation = 327; + if (_animationFrame < Slice_Animation_Query_Number_Of_Frames(327) - 1) { + _animationFrame++; + } + break; + + case 16: + *animation = 320; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(320)) { + _animationFrame = 0; + _animationState = 7; + *animation = 312; + } + break; + + case 17: + *animation = 321; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(321)) { + *animation = 328; + _animationFrame = 0; + _animationState = 0; + } + break; + + case 18: + *animation = 322; + _animationFrame++; + if (_animationFrame == 5) { + int snd; + if (Random_Query(1, 2) == 1) { + snd = 9010; + } else { + snd = 9015; + } + Sound_Play_Speech_Line(8, snd, 75, 0, 99); + } + if (_animationFrame == 7) { + Actor_Combat_AI_Hit_Attempt(kActorSadik); + } + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(322)) { + _animationFrame = 0; + _animationState = 7; + *animation = 312; + Actor_Change_Animation_Mode(kActorSadik, 4); + } + break; + + case 19: + *animation = 331; + if (!_animationFrame && _flag) { + *animation = 328; + _animationState = 0; + _flag = 0; + } else { + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(331)) { + _animationFrame = 0; + } + } + break; + + case 20: + case 21: + case 22: + case 23: + case 24: + case 25: + case 26: + switch (_animationState) { + case 20: + *animation = 332; + break; + case 21: + *animation = 333; + break; + case 22: + *animation = 334; + break; + case 23: + *animation = 335; + break; + case 24: + *animation = 336; + break; + case 25: + *animation = 337; + break; + default: + *animation = 338; + break; + } + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(*animation)) { + _animationFrame = 0; + _animationState = 19; + *animation = 331; + } + break; + + case 27: + *animation = 330; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(330)) { + *animation = 328; + _animationFrame = 0; + _animationState = 0; + Actor_Change_Animation_Mode(kActorSadik, 0); + } + break; + + case 28: + *animation = 341; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(341)) { + *animation = 328; + _animationFrame = 0; + _animationState = 0; + } + break; + + case 29: + *animation = 342; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(342)) { + *animation = 328; + _animationFrame = 0; + _animationState = 0; + } + break; + + case 30: + *animation = 343; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(343)) { + *animation = 328; + _animationFrame = 0; + _animationState = 0; + Actor_Change_Animation_Mode(kActorSadik, 0); + } + break; + + case 31: + *animation = 344; + _animationFrame++; + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(344)) { + *animation = 328; + _animationFrame = 0; + _animationState = 0; + Actor_Change_Animation_Mode(kActorSadik, 0); + } + break; + + case 32: + *animation = 345; + _animationFrame++; + if (_animationFrame == 23) { + _var1 = 201; + } + if (_animationFrame >= 25) { + _animationFrame = 0; + _animationState = 0; + *animation = 328; + Actor_Set_Goal_Number(kActorSadik, 104); + } + break; + + case 33: + *animation = 344; + _animationFrame++; + if (Actor_Query_Goal_Number(kActorSadik) == 105) { + if (_animationFrame == 4) { + _var1 = 221; + } + if (_animationFrame == 6) { + Actor_Change_Animation_Mode(0, 21); + } + } + if (_animationFrame >= Slice_Animation_Query_Number_Of_Frames(*animation)) { + *animation = 328; + _animationFrame = 0; + _animationState = 0; + + Actor_Change_Animation_Mode(kActorSadik, 0); + if (Actor_Query_Goal_Number(kActorSadik) == 105) { + Actor_Change_Animation_Mode(kActorSadik, 63); + } + } + break; + + case 34: + *animation = 343; + _animationFrame++; + if (_animationFrame == 4) { + if (Actor_Query_Goal_Number(kActorSadik) == 105) { + Actor_Change_Animation_Mode(0, 48); + _var1 = 222; + } else { + Actor_Change_Animation_Mode(0, 68); + _var1 = 223; + } + } + + if (_animationFrame >= 15) { + *animation = 328; + _animationFrame = 0; + _animationState = 0; + Actor_Change_Animation_Mode(kActorSadik, 0); + if (Actor_Query_Goal_Number(kActorSadik) == 105) { + AI_Movement_Track_Flush(kActorSadik); + AI_Movement_Track_Append(kActorSadik, 318, 0); + AI_Movement_Track_Repeat(kActorSadik); + } else { + if (Actor_Query_Goal_Number(kActorSadik) == 106) { + Actor_Change_Animation_Mode(kActorSadik, 63); + } + } + } + break; + + default: + *animation = 406; + _animationFrame = 0; + break; + } + + *frame = _animationFrame; + + return true; +} + +bool AIScriptSadik::ChangeAnimationMode(int mode) { + Actor_Set_Frame_Rate_FPS(kActorSadik, -2); + + switch (mode) { + case 0: + switch (_animationState) { + case 19: + case 20: + case 21: + case 22: + case 23: + case 24: + case 25: + case 26: + _flag = 1; + break; + case 30: + case 31: + return 1; + default: + _animationState = 0; + _animationFrame = 0; + _var3 = 0; + break; + } + break; + + case 1: + _animationFrame = 0; + _animationState = 1; + break; + + case 2: + _animationFrame = 0; + _animationState = 2; + break; + + case 3: + _animationState = 20; + _animationFrame = 0; + break; + + case 4: + switch (_animationState) { + case 0: + _animationFrame = 0; + _animationState = 16; + break; + case 3: + case 4: + _animationState = 7; + _animationFrame = 0; + break; + case 7: + case 16: + case 18: + return true; + case 17: + _animationFrame = 0; + _animationState = 7; + break; + default: + _animationFrame = 0; + _animationState = 16; + break; + } + break; + + case 5: + case 9: + case 10: + case 11: + case 19: + case 20: + return true; + + case 6: + _animationFrame = 0; + _animationState = 18; + break; + + case 7: + _animationFrame = 0; + _animationState = 3; + break; + + case 8: + _animationFrame = 0; + _animationState = 4; + break; + + case 12: + _animationState = 20; + _animationFrame = 0; + break; + + case 13: + _animationState = 21; + _animationFrame = 0; + break; + + case 14: + _animationState = 22; + _animationFrame = 0; + break; + + case 15: + _animationState = 23; + _animationFrame = 0; + break; + + case 16: + _animationState = 24; + _animationFrame = 0; + break; + + case 17: + _animationState = 25; + _animationFrame = 0; + break; + + case 18: + _animationState = 26; + _animationFrame = 0; + break; + + case 21: + switch (_animationState) { + case 7: + case 8: + case 9: + case 16: + case 17: + case 18: + if (Random_Query(0, 1)) { + _animationState = 13; + } else { + _animationState = 12; + } + break; + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + if (Random_Query(0, 1)) { + _animationState = 11; + } else { + _animationState = 10; + } + break; + } + _animationFrame = 0; + break; + + case 22: + if (Random_Query(0, 1)) { + _animationState = 12; + } else { + _animationState = 13; + } + _animationFrame = 0; + break; + + case 23: + _animationState = 27; + _animationFrame = 0; + break; + + case 48: + _animationState = 14; + _animationFrame = 0; + break; + + case 62: + if (Actor_Query_Goal_Number(kActorSadik) != 105 && Actor_Query_Goal_Number(kActorSadik) != 106) { + _animationState = 31; + _animationFrame = 0; + } else { + _animationState = 33; + _animationFrame = 0; + } + break; + + case 63: + if (Actor_Query_Goal_Number(kActorSadik) != 105 && Actor_Query_Goal_Number(kActorSadik) != 106) { + _animationState = 30; + _animationFrame = 2; + } else { + _animationState = 34; + _animationFrame = 2; + } + break; + } + return true; +} + +void AIScriptSadik::QueryAnimationState(int *animationState, int *animationFrame, int *animationStateNext, int *animationNext) { + *animationState = _animationState; + *animationFrame = _animationFrame; + *animationStateNext = _animationStateNext; + *animationNext = _animationNext; +} + +void AIScriptSadik::SetAnimationState(int animationState, int animationFrame, int animationStateNext, int animationNext) { + _animationState = animationState; + _animationFrame = animationFrame; + _animationStateNext = animationStateNext; + _animationNext = animationNext; +} + +bool AIScriptSadik::ReachedMovementTrackWaypoint(int waypointId) { + return true; +} + +void AIScriptSadik::FledCombat() { + // return false; +} + +} // End of namespace BladeRunner diff --git a/engines/bladerunner/script/ai/steele.cpp b/engines/bladerunner/script/ai/steele.cpp index e51a14a8ca..ff2afa1ec3 100644 --- a/engines/bladerunner/script/ai/steele.cpp +++ b/engines/bladerunner/script/ai/steele.cpp @@ -419,7 +419,7 @@ bool AIScriptSteele::ShotAtAndHit() { Actor_Set_Goal_Number(kActorSteele, 271); if (/* !a1 && */ Actor_Query_In_Set(kActorSteele, kSetHF06)) - Non_Player_Actor_Combat_Mode_On(1, 3, 1, 0, 15, 4, 7, 8, 0, 0, 100, 25, 300, 0); + Non_Player_Actor_Combat_Mode_On(kActorSteele, kActorCombatStateUncover, true, kActorMcCoy, 15, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, 0, 0, 100, 25, 300, false); return false; } diff --git a/engines/bladerunner/script/ai/taffy_patron.cpp b/engines/bladerunner/script/ai/taffy_patron.cpp index c7fa3a7165..0bf6861d12 100644 --- a/engines/bladerunner/script/ai/taffy_patron.cpp +++ b/engines/bladerunner/script/ai/taffy_patron.cpp @@ -95,13 +95,13 @@ bool AIScriptTaffyPatron::GoalChanged(int currentGoalNumber, int newGoalNumber) case 250: Actor_Put_In_Set(kActorTaffyPatron, kSetNR01); - Actor_Set_At_XYZ(kActorTaffyPatron, -170.39999, 23.68, -850.0, 324); - Async_Actor_Walk_To_XYZ(kActorTaffyPatron, -390.0, 31.549999, -429.0, 24, 1); + Actor_Set_At_XYZ(kActorTaffyPatron, -170.4f, 23.68f, -850.0f, 324); + Async_Actor_Walk_To_XYZ(kActorTaffyPatron, -390.0f, 31.55f, -429.0f, 24, 1); return true; case 255: Actor_Put_In_Set(kActorTaffyPatron, kSetNR01); - Actor_Set_At_XYZ(kActorTaffyPatron, -170.39999, 23.68, -850.0, 324); + Actor_Set_At_XYZ(kActorTaffyPatron, -170.4f, 23.68f, -850.0f, 324); Actor_Change_Animation_Mode(kActorTaffyPatron, 48); return true; diff --git a/engines/bladerunner/script/ai/zuben.cpp b/engines/bladerunner/script/ai/zuben.cpp index 9fe6aa2f10..58cea9d39d 100644 --- a/engines/bladerunner/script/ai/zuben.cpp +++ b/engines/bladerunner/script/ai/zuben.cpp @@ -152,7 +152,7 @@ void AIScriptZuben::CompletedMovementTrack() { Set_Enter(kSetCT06, kSceneCT06); } if (Actor_Query_Goal_Number(kActorZuben) == 21) { - Non_Player_Actor_Combat_Mode_On(kActorZuben, 0, 0, kActorMcCoy, 6, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, 0, 0, 100, 15, 300, 0); + Non_Player_Actor_Combat_Mode_On(kActorZuben, kActorCombatStateIdle, false, kActorMcCoy, 6, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, 0, 0, 100, 15, 300, false); } int goal = Actor_Query_Goal_Number(kActorZuben); if (goal == 200) { @@ -977,7 +977,7 @@ bool AIScriptZuben::ChangeAnimationMode(int mode) { break; } break; - case kAnimationModeCombatShoot: + case kAnimationModeCombatAttack: _animationState = 8; _animationFrame = 0; break; diff --git a/engines/bladerunner/script/ai_script.cpp b/engines/bladerunner/script/ai_script.cpp index 7b28e2db3d..b328824de1 100644 --- a/engines/bladerunner/script/ai_script.cpp +++ b/engines/bladerunner/script/ai_script.cpp @@ -22,9 +22,8 @@ #include "bladerunner/script/ai_script.h" -#include "bladerunner/bladerunner.h" - #include "bladerunner/actor.h" +#include "bladerunner/bladerunner.h" namespace BladeRunner { @@ -47,17 +46,24 @@ AIScripts::AIScripts(BladeRunnerEngine *vm, int actorCount) { _AIScripts[kActorClovis] = new AIScriptClovis(_vm); // 5 _AIScripts[kActorLucy] = new AIScriptLucy(_vm); // 6 _AIScripts[kActorIzo] = new AIScriptIzo(_vm); // 7 + _AIScripts[kActorSadik] = new AIScriptSadik(_vm); // 8 _AIScripts[kActorCrazylegs] = new AIScriptCrazylegs(_vm); // 9 + _AIScripts[kActorLuther] = new AIScriptLuther(_vm); // 10 _AIScripts[kActorGrigorian] = new AIScriptGrigorian(_vm); // 11 _AIScripts[kActorTransient] = new AIScriptTransient(_vm); // 12 _AIScripts[kActorLance] = new AIScriptLance(_vm); // 13 + _AIScripts[kActorBulletBob] = new AIScriptBulletBob(_vm); // 14 _AIScripts[kActorRunciter] = new AIScriptRunciter(_vm); // 15 _AIScripts[kActorInsectDealer] = new AIScriptInsectDealer(_vm); // 16 _AIScripts[kActorTyrellGuard] = new AIScriptTyrellGuard(_vm); // 17 + _AIScripts[kActorEarlyQ] = new AIScriptEarlyQ(_vm); // 18 _AIScripts[kActorZuben] = new AIScriptZuben(_vm); // 19 + _AIScripts[kActorHasan] = new AIScriptHasan(_vm); // 20 _AIScripts[kActorMarcus] = new AIScriptMarcus(_vm); // 21 _AIScripts[kActorMia] = new AIScriptMia(_vm); // 22 _AIScripts[kActorOfficerLeary] = new AIScriptOfficerLeary(_vm); // 23 + _AIScripts[kActorOfficerGrayford] = new AIScriptOfficerGrayford(_vm); // 24 + _AIScripts[kActorHanoi] = new AIScriptHanoi(_vm); // 25 _AIScripts[kActorBaker] = new AIScriptBaker(_vm); // 26 _AIScripts[kActorDeskClerk] = new AIScriptDeskClerk(_vm); // 27 _AIScripts[kActorHowieLee] = new AIScriptHowieLee(_vm); // 28 @@ -96,6 +102,8 @@ AIScripts::AIScripts(BladeRunnerEngine *vm, int actorCount) { _AIScripts[kActorNewscaster] = new AIScriptNewscaster(_vm); // 61 _AIScripts[kActorLeon] = new AIScriptLeon(_vm); // 62 _AIScripts[kActorMaleAnnouncer] = new AIScriptMaleAnnouncer(_vm); // 63 + _AIScripts[kActorFreeSlotA] = new AIScriptFreeSlotA(_vm); // 64 + _AIScripts[kActorFreeSlotB] = new AIScriptFreeSlotB(_vm); // 65 _AIScripts[kActorMaggie] = new AIScriptMaggie(_vm); // 66 _AIScripts[kActorGenwalkerA] = new AIScriptGenericWalkerA(_vm); // 67 _AIScripts[kActorGenwalkerB] = new AIScriptGenericWalkerB(_vm); // 68 @@ -324,4 +332,43 @@ void AIScripts::changeAnimationMode(int actor, int mode) { _inScriptCounter--; } +void AIScripts::fledCombat(int actor) { + if (actor >= _actorCount) { + return; + } + + _inScriptCounter++; + if (_AIScripts[actor]) { + _AIScripts[actor]->FledCombat(); + } + _inScriptCounter--; +} + +void AIScripts::setAnimationState(int actor, int animationState, int animationFrame, int animationStateNext, int animationNext) { + if (actor >= _actorCount) { + return; + } + + _inScriptCounter++; + if (_AIScripts[actor]) { + _AIScripts[actor]->SetAnimationState(animationState, animationFrame, animationStateNext, animationNext); + } + _inScriptCounter--; +} + + +void AIScripts::queryAnimationState(int actor, int *animationState, int *animationFrame, int *animationStateNext, int *animationNext) { + if (actor >= _actorCount) { + return; + } + + _inScriptCounter++; + if (_AIScripts[actor]) { + _AIScripts[actor]->FledCombat(); + _AIScripts[actor]->QueryAnimationState(animationState, animationFrame, animationStateNext, animationNext); + } + _inScriptCounter--; +} + + } // End of namespace BladeRunner diff --git a/engines/bladerunner/script/ai_script.h b/engines/bladerunner/script/ai_script.h index 32bbce092f..9cafa28039 100644 --- a/engines/bladerunner/script/ai_script.h +++ b/engines/bladerunner/script/ai_script.h @@ -61,7 +61,7 @@ public: virtual bool GoalChanged(int currentGoalNumber, int newGoalNumber) = 0; virtual bool UpdateAnimation(int *animation, int *frame) = 0; virtual bool ChangeAnimationMode(int mode) = 0; - virtual void QueryAnimationState(int *animationState, int *animationFrame, int *animationStateNext, int *nextAnimation) = 0; + virtual void QueryAnimationState(int *animationState, int *animationFrame, int *animationStateNext, int *animationNext) = 0; virtual void SetAnimationState(int animationState, int animationFrame, int animationStateNext, int animationNext) = 0; virtual bool ReachedMovementTrackWaypoint(int waypointId) = 0; virtual void FledCombat() = 0; @@ -189,10 +189,22 @@ DECLARE_SCRIPT(Izo) void modifyWaypoints(); END_SCRIPT +DECLARE_SCRIPT(Sadik) + int _var1; + int _var2; + int _var3; + int _var4; + bool _flag; +END_SCRIPT + DECLARE_SCRIPT(Crazylegs) bool _flag; END_SCRIPT +DECLARE_SCRIPT(Luther) + bool _flag; +END_SCRIPT + DECLARE_SCRIPT(Grigorian) int var_45CA10; int var_45CA14; @@ -204,6 +216,13 @@ END_SCRIPT DECLARE_SCRIPT(Lance) END_SCRIPT +DECLARE_SCRIPT(BulletBob) + int _var1; + int _var2; + int _var3; + int _var4; +END_SCRIPT + DECLARE_SCRIPT(Runciter) int var_45CD78; int var_45CD7C; @@ -225,6 +244,13 @@ DECLARE_SCRIPT(TyrellGuard) bool _flag1; END_SCRIPT +DECLARE_SCRIPT(EarlyQ) + int _var1; + int _var2; + int _var3; + bool _flag; +END_SCRIPT + DECLARE_SCRIPT(Zuben) int _var_45D258; int _var_45D25C; @@ -235,6 +261,15 @@ DECLARE_SCRIPT(Zuben) void dialogue(); END_SCRIPT +DECLARE_SCRIPT(Hasan) + int _var1; + int _var2; + int _var3; + int _var4; + int _var5; + int _var6; +END_SCRIPT + DECLARE_SCRIPT(Marcus) END_SCRIPT @@ -250,6 +285,19 @@ DECLARE_SCRIPT(OfficerLeary) bool sub_431420(); END_SCRIPT +DECLARE_SCRIPT(OfficerGrayford) + int _var1; + int _var2; + int _var3; +END_SCRIPT + +DECLARE_SCRIPT(Hanoi) + int _var1; + int _var2; + int _var3; + int _var4; +END_SCRIPT + DECLARE_SCRIPT(Baker) END_SCRIPT @@ -411,6 +459,24 @@ END_SCRIPT DECLARE_SCRIPT(MaleAnnouncer) END_SCRIPT +DECLARE_SCRIPT(FreeSlotA) + int _var1; + int _var2; + float _var3; + float _var4; + float _var5; + + void calcHit(); + void processGoal306(); +END_SCRIPT + +DECLARE_SCRIPT(FreeSlotB) + int _var1; + int _var2; + + void processGoal301(); +END_SCRIPT + DECLARE_SCRIPT(Maggie) int var_45F3F8; int var_45F3FC; @@ -500,8 +566,16 @@ public: bool reachedMovementTrackWaypoint(int actor, int waypointId); void updateAnimation(int actor, int *animation, int *frame); void changeAnimationMode(int actor, int mode); + void queryAnimationState(int actor, int *animationState, int *animationFrame, int *animationStateNext, int *animationNext); + void setAnimationState(int actor, int animationState, int animationFrame, int animationStateNext, int animationNext); + void fledCombat(int actor); bool isInsideScript() const { return _inScriptCounter > 0; } + + void callChangeAnimationMode(int actor, int mode) { _AIScripts[actor]->ChangeAnimationMode(mode); } + int callGetFriendlinessModifierIfGetsClue(int actor, int otherActorId, int clueId) { + return _AIScripts[actor]->GetFriendlinessModifierIfGetsClue(otherActorId, clueId); + } }; } // End of namespace BladeRunner diff --git a/engines/bladerunner/script/police_maze.cpp b/engines/bladerunner/script/police_maze.cpp new file mode 100644 index 0000000000..a85fa07451 --- /dev/null +++ b/engines/bladerunner/script/police_maze.cpp @@ -0,0 +1,603 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can 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 "bladerunner/bladerunner.h" +#include "bladerunner/game_constants.h" +#include "bladerunner/items.h" +#include "bladerunner/mouse.h" +#include "bladerunner/savefile.h" +#include "bladerunner/scene.h" +#include "bladerunner/scene_objects.h" +#include "bladerunner/script/police_maze.h" +#include "bladerunner/script/scene_script.h" + +namespace BladeRunner { + +PoliceMaze::PoliceMaze(BladeRunnerEngine *vm) : ScriptBase(vm) { + reset(); + + for (int i = 0; i < kNumMazeTracks; i++) { + _tracks[i] = new PoliceMazeTargetTrack(vm); + } +} + +PoliceMaze::~PoliceMaze() { + for (int i = 0; i < kNumMazeTracks; i++) { + delete _tracks[i]; + } + + reset(); +} + +void PoliceMaze::reset() { + _isPaused = false; + _isActive = false; + _isEnding = false; + + for (int i = 0; i < kNumMazeTracks; i++) { + _tracks[i] = 0; + } + + _pm_var1 = 0; + _pm_var2 = 0; +} + +void PoliceMaze::clear(bool isLoadingGame) { + for (int i = 0; i < kNumMazeTracks; i++) { + if (_tracks[i]->isPresent()) { + _tracks[i]->clear(isLoadingGame); + } + } +} + +void PoliceMaze::activate() { + _isActive = true; + _isEnding = false; +} + +void PoliceMaze::setPauseState(bool state) { + warning("PAUSE: %d", state); + _isPaused = state; + + uint32 t = _vm->getTotalPlayTime(); + + for (int i = 0; i < kNumMazeTracks; i++) { + _tracks[i]->setTime(t); + } +} + +void PoliceMaze::tick() { + if (_isPaused) { + return; + } + + if (_vm->_scene->getSetId() != kSetPS10_PS11_PS12_PS13) { + return; + } + + if (_isEnding) { + _isActive = false; + return; + } + + for (int i = 0; i < kNumMazeTracks; i++) { + _tracks[i]->tick(); + } + + bool notFound = true; + for (int i = 0; i < kNumMazeTracks; i++) { + if (!_tracks[i]->isPaused()) { + notFound = false; + break; + } + } + + if (notFound && _isActive && !_isEnding) { + _isActive = false; + _isEnding = true; + + if (_vm->_scene->getSceneId() == kScenePS13) { + Actor_Voice_Over(320, kActorAnsweringMachine); + } else { + Actor_Voice_Over(310, kActorAnsweringMachine); + } + } +} + +void PoliceMaze::save(SaveFileWriteStream &f) { + f.writeBool(_isPaused); + f.writeBool(_isActive); + f.writeBool(_isEnding); + for (int i = 0; i < kNumMazeTracks; ++i) { + _tracks[i]->save(f); + } +} + +void PoliceMaze::load(SaveFileReadStream &f) { + _isPaused = f.readBool(); + _isActive = f.readBool(); + _isEnding = f.readBool(); + for (int i = 0; i < kNumMazeTracks; ++i) { + _tracks[i]->load(f); + } +} + +PoliceMazeTargetTrack::PoliceMazeTargetTrack(BladeRunnerEngine *vm) : ScriptBase(vm) { + reset(); +} + +PoliceMazeTargetTrack::~PoliceMazeTargetTrack() { + reset(); +} + +void PoliceMazeTargetTrack::reset() { + _isPresent = false; + _itemId = -1; + _pointCount = 0; + _data = nullptr; + _dataIndex = 0; + _timeLeftUpdate = 0; + _timeLeftWait = 0; + _time = 0; + _isWaiting = false; + _isMoving = false; + _pointIndex = 0; + _pointTarget = 0; + _isRotating = false; + _angleTarget = 0; + _angleDelta = 0; + _isPaused = true; +} + +void PoliceMazeTargetTrack::clear(bool isLoadingGame) { + reset(); +} + +void PoliceMazeTargetTrack::add(int trackId, float startX, float startY, float startZ, float endX, float endY, float endZ, int steps, const int *instructions, bool isActive) { + _data = (const int *)instructions; + + if (true /* !GameIsLoading */) { // TODO: FIXME + _itemId = trackId; + _pointCount = steps; + _dataIndex = 0; + + double coef = 1.0f / (long double)steps; + + double coefX = (endX - startX) * coef; + double coefY = (endY - startY) * coef; + double coefZ = (endZ - startZ) * coef; + + for (int i = 0; i < steps - 1; i++) { + _points[i].x = i * coefX + startX; + _points[i].y = i * coefY + startY; + _points[i].z = i * coefZ + startZ; + } + + _points[steps - 1].x = endX; + _points[steps - 1].y = endY; + _points[steps - 1].z = endZ; + + _isPaused = !isActive; + } + _isPresent = true; +} + +bool PoliceMazeTargetTrack::tick() { + if (!_isPresent) { + return false; + } + + uint32 oldTime = _time; + _time = _vm->getTotalPlayTime(); + int32 timeDiff = _time - oldTime; + _timeLeftUpdate -= timeDiff; + + if (_timeLeftUpdate > 0) { + return false; + } + + _timeLeftUpdate = 66; + + if (_isPaused) { + return false; + } + + if (_isWaiting) { + _timeLeftWait -= timeDiff; + + if (_timeLeftWait > 0) { + return true; + } + + _isWaiting = false; + _timeLeftWait = 0; + } + + if (_vm->_items->isSpinning(_itemId)) { + return true; + } + + if (_isRotating) { + float angle = _vm->_items->getFacing(_itemId) + _angleDelta; + + if (_angleDelta > 0) { + if (angle >= _angleTarget) { + angle = _angleTarget; + _isRotating = false; + } + } else if (_angleDelta < 0) { + if (angle <= _angleTarget) { + angle = _angleTarget; + _isRotating = false; + } + } else { + _isRotating = false; + } + + _vm->_items->setFacing(_itemId, angle); + + if (_isRotating) + return false; + } + + bool advancePoint = false; + + if (_isMoving) { + if (_pointIndex < _pointTarget) { + _pointIndex++; + advancePoint = true; + } else if (_pointIndex > _pointTarget) { + _pointIndex--; + advancePoint = true; + } else { + _isMoving = 0; + } + } + + if (advancePoint) { + _vm->_items->setXYZ(_itemId, _points[_pointIndex]); + readdObject(_itemId); + + return true; + } + + bool cont = true; + + while (cont) { + _dataIndex++; + + debug ("ItemId %3i, pos %3i, instruction %3i", _itemId, _dataIndex - 1, _data[_dataIndex - 1]); + + switch (_data[_dataIndex - 1]) { + case kPMTIActivate: + { + int variableId = _data[_dataIndex++]; + int maxValue = _data[_dataIndex++]; + + if (Global_Variable_Query(variableId) >= maxValue) { + setPaused(); + cont = false; + } else { + cont = true; + } + break; + } + + case kPMTILeave: + if (!_vm->_items->isPoliceMazeEnemy(_itemId) && _vm->_items->isTarget(_itemId)) { + Police_Maze_Increment_Score(1); + } + break; + + case kPMTIShoot: + { + int soundId = _data[_dataIndex++]; + _dataIndex++; // second argument is not used + + if (_vm->_items->isTarget(_itemId)) { + Sound_Play(soundId, 90, 0, 0, 50); + Police_Maze_Decrement_Score(1); + Actor_Force_Stop_Walking(kActorMcCoy); + + if (Player_Query_Combat_Mode()) { + Actor_Change_Animation_Mode(kActorMcCoy, kAnimationModeCombatHit); + } else { + Actor_Change_Animation_Mode(kActorMcCoy, kAnimationModeHit); + } + + int snd; + + if (Random_Query(1, 2) == 1) { + snd = 9900; + } else { + snd = 9905; + } + Sound_Play_Speech_Line(kActorMcCoy, snd, 75, 0, 99); + + _vm->_mouse->setMouseJitterDown(); + } + + cont = false; + break; + } + + case kPMTIEnemyReset: + { + int itemId = _data[_dataIndex++]; + _vm->_items->setPoliceMazeEnemy(itemId, false); + break; + } + + case kPMTIEnemySet: + { + int itemId = _data[_dataIndex++]; + _vm->_items->setPoliceMazeEnemy(itemId, true); + break; + } + + case kPMTIFlagReset: + { + int gameFlagId = _data[_dataIndex++]; + Game_Flag_Reset(gameFlagId); + break; + } + + case kPMTIFlagSet: + { + int gameFlagId = _data[_dataIndex++]; + Game_Flag_Set(gameFlagId); + break; + } + + case kPMTIVariableDec: + { + int variableId = _data[_dataIndex++]; + Global_Variable_Decrement(variableId, 1); + break; + } + + case kPMTIVariableInc: + { + int variableId = _data[_dataIndex++]; + int maxValue = _data[_dataIndex++]; + if (Global_Variable_Query(variableId) < maxValue) { + Global_Variable_Increment(variableId, 1); + } + break; + } + + case kPMTIVariableReset: + { + int variableId = _data[_dataIndex++]; + Global_Variable_Reset(variableId); + break; + } + + case kPMTIVariableSet: + { + int variableId = _data[_dataIndex++]; + int value = _data[_dataIndex++]; + Global_Variable_Set(variableId, value); + break; + } + + case kPMTITargetSet: + { + int itemId = _data[_dataIndex++]; + int value = _data[_dataIndex++]; + _vm->_items->setIsTarget(itemId, value); + break; + } + + case kPMTI12: + { + int trackId1 = _data[_dataIndex++]; + int trackId2 = _data[_dataIndex++]; + int trackId3 = _data[_dataIndex++]; + + switch (Random_Query(1, 3)) { + case 1: + _vm->_policeMaze->_tracks[trackId1]->resetPaused(); + break; + + case 2: + _vm->_policeMaze->_tracks[trackId2]->resetPaused(); + break; + + case 3: + _vm->_policeMaze->_tracks[trackId3]->resetPaused(); + break; + } + + break; + } + + case kPMTI13: + { + int trackId1 = _data[_dataIndex++]; + int trackId2 = _data[_dataIndex++]; + + if (Random_Query(1, 2) == 1) { + _vm->_policeMaze->_tracks[trackId1]->resetPaused(); + } else { + _vm->_policeMaze->_tracks[trackId2]->resetPaused(); + } + break; + } + + case kPMTIPausedSet: + { + int trackId = _data[_dataIndex++]; + _vm->_policeMaze->_tracks[trackId]->setPaused(); + break; + } + + case kPMTIPausedReset: + { + int trackId = _data[_dataIndex++]; + _vm->_policeMaze->_tracks[trackId]->resetPaused(); + break; + } + + case kPMTIPlaySound: + { + int soundId = _data[_dataIndex++]; + int volume = _data[_dataIndex++]; + Sound_Play(soundId, volume, 0, 0, 50); + break; + } + + case kPMTIObstacleReset: + { + int itemId = _data[_dataIndex++]; + _vm->_items->setIsObstacle(itemId, 0); + break; + } + + case kPMTIObstacleSet: + { + int itemId = _data[_dataIndex++]; + _vm->_items->setIsObstacle(itemId, 1); + break; + } + + case kPMTIWaitRandom: + { + int randomMin = _data[_dataIndex++]; + int randomMax = _data[_dataIndex++]; + _timeLeftWait = Random_Query(randomMin, randomMax); + _isWaiting = true; + + cont = false; + break; + } + + case kPMTIRotate: + _angleTarget = _data[_dataIndex++]; + _angleDelta = _data[_dataIndex++]; + _isRotating = true; + + cont = false; + break; + + case kPMTIFacing: + { + int angle = _data[_dataIndex++]; + _vm->_items->setFacing(_itemId, angle); + break; + } + + case kPMTIRestart: + _dataIndex = 0; + + cont = false; + break; + + case kPMTIWait: + _timeLeftWait = _data[_dataIndex++]; + _isWaiting = true; + + cont = false; + break; + + case kPMTIMove: + _pointTarget = _data[_dataIndex++]; + _isMoving = true; + + cont = false; + break; + + case kPMTIPosition: + _pointIndex = _data[_dataIndex++]; + _isMoving = false; + _vm->_items->setXYZ(_itemId, _points[_pointIndex]); + readdObject(_itemId); + break; + + default: + return false; + } + + if (_isPaused || _isWaiting) { + cont = false; + } + } + + return true; +} + +void PoliceMazeTargetTrack::readdObject(int itemId) { + if (_vm->_sceneObjects->remove(itemId + kSceneObjectOffsetItems)) { + const BoundingBox &boundingBox = _vm->_items->getBoundingBox(itemId); + const Common::Rect &screenRect = _vm->_items->getScreenRectangle(itemId); + bool targetable = _vm->_items->isTarget(itemId); + bool obstacle = _vm->_items->isVisible(itemId); + + _vm->_sceneObjects->addItem(itemId + kSceneObjectOffsetItems, boundingBox, screenRect, targetable, obstacle); + } +} + +void PoliceMazeTargetTrack::save(SaveFileWriteStream &f) { + f.writeBool(_isPresent); + f.writeInt(_itemId); + f.writeInt(_pointCount); + f.writeInt(_dataIndex); + f.writeBool(_isWaiting); + f.writeBool(_isMoving); + f.writeInt(_pointIndex); + f.writeInt(_pointTarget); + f.writeBool(_isRotating); + f.writeInt(_angleTarget); + f.writeInt(_angleDelta); + f.writeBool(_isPaused); + + for (int i = 0; i < kNumTrackPoints; ++i) { + f.writeVector3(_points[i]); + } + + f.writeInt(_timeLeftUpdate); + f.writeInt(_timeLeftWait); +} + +void PoliceMazeTargetTrack::load(SaveFileReadStream &f) { + _isPresent = f.readBool(); + _itemId = f.readInt(); + _pointCount = f.readInt(); + _dataIndex = f.readInt(); + _isWaiting = f.readBool(); + _isMoving = f.readBool(); + _pointIndex = f.readInt(); + _pointTarget = f.readInt(); + _isRotating = f.readBool(); + _angleTarget = f.readInt(); + _angleDelta = f.readInt(); + _isPaused = f.readBool(); + + for (int i = 0; i < kNumTrackPoints; ++i) { + _points[i] = f.readVector3(); + } + + _timeLeftUpdate = f.readInt(); + _timeLeftWait = f.readInt(); +} + +} // End of namespace BladeRunner diff --git a/engines/bladerunner/script/police_maze.h b/engines/bladerunner/script/police_maze.h new file mode 100644 index 0000000000..725fbd4319 --- /dev/null +++ b/engines/bladerunner/script/police_maze.h @@ -0,0 +1,106 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef BLADERUNNER_SCRIPT_POLICE_MAZE_H +#define BLADERUNNER_SCRIPT_POLICE_MAZE_H + +#include "bladerunner/script/script.h" +#include "bladerunner/vector.h" + +namespace BladeRunner { + +enum { + kNumMazeTracks = 64, + kNumTrackPoints = 100 +}; + +class BladeRunnerEngine; +class SaveFileReadStream; +class SaveFileWriteStream; + +class PoliceMazeTargetTrack : ScriptBase { + uint32 _time; + bool _isPresent; + int _itemId; + int _pointCount; + Vector3 _points[kNumTrackPoints]; + const int *_data; + int _dataIndex; + int32 _timeLeftUpdate; + int32 _timeLeftWait; + bool _isWaiting; + int _isMoving; + int _pointIndex; + int _pointTarget; + bool _isRotating; + int _angleTarget; + int _angleDelta; + bool _isPaused; + +public: + PoliceMazeTargetTrack(BladeRunnerEngine *vm); + ~PoliceMazeTargetTrack(); + + void reset(); + void clear(bool isLoadingGame); + void add(int trackId, float startX, float startY, float startZ, float endX, float endY, float endZ, int steps, const int *instructions, bool isActive); + + bool tick(); + bool isPresent() const { return _isPresent; } + void setPaused() { _isPaused = true; } + void resetPaused() { _isPaused = false; } + bool isPaused() const { return _isPaused; } + void setTime(uint32 t) { _time = t; } + + void readdObject(int itemId); + + void save(SaveFileWriteStream &f); + void load(SaveFileReadStream &f); +}; + +class PoliceMaze : ScriptBase { + bool _isPaused; + bool _isActive; + bool _isEnding; + int _pm_var1; + int _pm_var2; + +public: + PoliceMazeTargetTrack *_tracks[kNumMazeTracks]; + +public: + PoliceMaze(BladeRunnerEngine *vm); + ~PoliceMaze(); + + void tick(); + void reset(); + void clear(bool isLoadingGame); + void setPauseState(bool state); + void activate(); + + void save(SaveFileWriteStream &f); + void load(SaveFileReadStream &f); +}; + +} // End of namespace BladeRunner + +#endif diff --git a/engines/bladerunner/script/scene/ct07.cpp b/engines/bladerunner/script/scene/ct07.cpp index 2eb7805c44..ebbe604b65 100644 --- a/engines/bladerunner/script/scene/ct07.cpp +++ b/engines/bladerunner/script/scene/ct07.cpp @@ -89,7 +89,7 @@ void SceneScriptCT07::ActorChangedGoal(int actorId, int newGoal, int oldGoal, bo void SceneScriptCT07::PlayerWalkedIn() { Player_Gains_Control(); - Non_Player_Actor_Combat_Mode_On(kActorZuben, 0, 0, kActorMcCoy, 2, 4, 7, 8, 0, 0, 100, 15, 300, 0); + Non_Player_Actor_Combat_Mode_On(kActorZuben, kActorCombatStateIdle, false, kActorMcCoy, 2, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, 0, 0, 100, 15, 300, false); Game_Flag_Set(516); Actor_Face_Actor(kActorMcCoy, kActorZuben, true); } diff --git a/engines/bladerunner/script/scene/ct11.cpp b/engines/bladerunner/script/scene/ct11.cpp index 63d114ebfb..49dd417ae1 100644 --- a/engines/bladerunner/script/scene/ct11.cpp +++ b/engines/bladerunner/script/scene/ct11.cpp @@ -54,7 +54,7 @@ void SceneScriptCT11::SceneLoaded() { Unobstacle_Object("BOX SOUTH 1", true); if (Global_Variable_Query(kVariableChapter) < 4) { if (!Game_Flag_Query(645)) { - Item_Add_To_World(115, 951, 33, 640.21002f, 30.0f, 470.0f, 512, 12, 12, false, true, false, true); + Item_Add_To_World(115, 951, 33, 640.21f, 30.0f, 470.0f, 512, 12, 12, false, true, false, true); Scene_2D_Region_Add(0, 505, 316, 513, 321); Game_Flag_Set(725); } @@ -99,15 +99,15 @@ bool SceneScriptCT11::ClickedOnItem(int itemId, bool a2) { bool SceneScriptCT11::ClickedOnExit(int exitId) { if (exitId == 0) { - if (!Loop_Actor_Walk_To_XYZ(kActorMcCoy, 121.0f, 9.6800003f, -42.0f, 0, 1, false, 0)) { + if (!Loop_Actor_Walk_To_XYZ(kActorMcCoy, 121.0f, 9.68f, -42.0f, 0, 1, false, 0)) { Game_Flag_Set(304); Set_Enter(31, kSceneCT09); } return true; } if (exitId == 1) { - if (!Loop_Actor_Walk_To_XYZ(kActorMcCoy, -300.0f, 9.6800003f, 66.0f, 0, 1, false, 0)) { - Loop_Actor_Walk_To_XYZ(kActorMcCoy, -400.0f, 9.6800003f, -70.0f, 0, 1, false, 0); + if (!Loop_Actor_Walk_To_XYZ(kActorMcCoy, -300.0f, 9.68f, 66.0f, 0, 1, false, 0)) { + Loop_Actor_Walk_To_XYZ(kActorMcCoy, -400.0f, 9.68f, -70.0f, 0, 1, false, 0); Game_Flag_Set(86); Set_Enter(4, kSceneCT12); } diff --git a/engines/bladerunner/script/scene/ct12.cpp b/engines/bladerunner/script/scene/ct12.cpp index 859b065b37..88bbd4645a 100644 --- a/engines/bladerunner/script/scene/ct12.cpp +++ b/engines/bladerunner/script/scene/ct12.cpp @@ -138,7 +138,7 @@ bool SceneScriptCT12::ClickedOnItem(int itemId, bool a2) { bool SceneScriptCT12::ClickedOnExit(int exitId) { if (exitId == 0) { - if (!Loop_Actor_Walk_To_XYZ(kActorMcCoy, -419.14999f, -6.5f, 696.94f, 0, 1, false, 0)) { + if (!Loop_Actor_Walk_To_XYZ(kActorMcCoy, -419.15f, -6.5f, 696.94f, 0, 1, false, 0)) { Ambient_Sounds_Remove_All_Non_Looping_Sounds(1); Game_Flag_Set(88); Set_Enter(4, kSceneCT01); diff --git a/engines/bladerunner/script/scene/dr02.cpp b/engines/bladerunner/script/scene/dr02.cpp index cbdb1ddcd7..c416fe2ff2 100644 --- a/engines/bladerunner/script/scene/dr02.cpp +++ b/engines/bladerunner/script/scene/dr02.cpp @@ -32,7 +32,7 @@ void SceneScriptDR02::InitializeScene() { } else if (Game_Flag_Query(264)) { Setup_Scene_Information(-1258.0f, 7.18f, -314.0f, 400); } else { - Setup_Scene_Information(168.78f, 0.16f, -775.71997f, 193); + Setup_Scene_Information(168.78f, 0.16f, -775.72f, 193); } Scene_Exit_Add_2D_Exit(0, 605, 0, 639, 479, 1); Scene_Exit_Add_2D_Exit(1, 222, 176, 279, 314, 0); diff --git a/engines/bladerunner/script/scene/dr04.cpp b/engines/bladerunner/script/scene/dr04.cpp index 21933aaae7..fd7316536d 100644 --- a/engines/bladerunner/script/scene/dr04.cpp +++ b/engines/bladerunner/script/scene/dr04.cpp @@ -247,7 +247,7 @@ void SceneScriptDR04::PlayerWalkedIn() { Loop_Actor_Walk_To_XYZ(kActorMcCoy, -851.0f, 71.64f, 647.0f, 0, 0, false, 0); Actor_Face_Heading(kActorMcCoy, 0, false); Loop_Actor_Travel_Stairs(kActorMcCoy, 7, 0, 0); - Loop_Actor_Walk_To_XYZ(kActorMcCoy, -774.85f, 7.18f, 386.67001f, 0, 0, false, 0); + Loop_Actor_Walk_To_XYZ(kActorMcCoy, -774.85f, 7.18f, 386.67f, 0, 0, false, 0); Actor_Set_Immunity_To_Obstacles(kActorMcCoy, false); Footstep_Sound_Override_Off(); } diff --git a/engines/bladerunner/script/scene/dr06.cpp b/engines/bladerunner/script/scene/dr06.cpp index ebc19bce41..393cdc3184 100644 --- a/engines/bladerunner/script/scene/dr06.cpp +++ b/engines/bladerunner/script/scene/dr06.cpp @@ -26,9 +26,9 @@ namespace BladeRunner { void SceneScriptDR06::InitializeScene() { if (Game_Flag_Query(230)) { - Setup_Scene_Information(-733.57001f, 136.60001f, -968.64001f, 0); + Setup_Scene_Information(-733.57f, 136.6f, -968.64f, 0); } else { - Setup_Scene_Information(-707.57001f, 136.60001f, -1132.64f, 472); + Setup_Scene_Information(-707.57f, 136.6f, -1132.64f, 472); } Scene_Exit_Add_2D_Exit(0, 601, 11, 639, 479, 1); if (Global_Variable_Query(kVariableChapter) > 3 && Game_Flag_Query(715)) { diff --git a/engines/bladerunner/script/scene/hf01.cpp b/engines/bladerunner/script/scene/hf01.cpp index ce36b91e31..d75c381f35 100644 --- a/engines/bladerunner/script/scene/hf01.cpp +++ b/engines/bladerunner/script/scene/hf01.cpp @@ -90,9 +90,9 @@ bool SceneScriptHF01::ClickedOn3DObject(const char *objectName, bool a2) { bool SceneScriptHF01::ClickedOnActor(int actorId) { int v1; - if (Global_Variable_Query(45) == 2) { + if (Global_Variable_Query(kVariableAffectionTowards) == 2) { v1 = kActorDektora; - } else if (Global_Variable_Query(45) == 3) { + } else if (Global_Variable_Query(kVariableAffectionTowards) == 3) { v1 = kActorLucy; } else { v1 = -1; @@ -295,15 +295,15 @@ void SceneScriptHF01::PlayerWalkedIn() { Actor_Set_At_XYZ(kActorOfficerLeary, 8.2f, 8.0f, -346.67f, 1021); Actor_Put_In_Set(kActorOfficerGrayford, 37); Actor_Set_At_XYZ(kActorOfficerGrayford, 51.21f, 8.0f, -540.78f, 796); - Non_Player_Actor_Combat_Mode_On(kActorOfficerLeary, 3, 1, kActorMcCoy, 4, 4, 7, 8, 0, 0, 0, 100, 300, 0); - Non_Player_Actor_Combat_Mode_On(kActorOfficerGrayford, 3, 1, kActorMcCoy, 4, 4, 7, 8, 0, 0, 0, 100, 300, 0); + Non_Player_Actor_Combat_Mode_On(kActorOfficerLeary, kActorCombatStateUncover, true, kActorMcCoy, 4, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, 0, 0, 0, 100, 300, false); + Non_Player_Actor_Combat_Mode_On(kActorOfficerGrayford, kActorCombatStateUncover, true, kActorMcCoy, 4, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, 0, 0, 0, 100, 300, false); } if (!Game_Flag_Query(165) && Actor_Query_Goal_Number(kActorCrazylegs) != 2) { - if (Actor_Clue_Query(kActorMcCoy, kCluePhoneCallLucy1) && Global_Variable_Query(45) == 3 && Actor_Query_Goal_Number(kActorLucy) != 599) { + if (Actor_Clue_Query(kActorMcCoy, kCluePhoneCallLucy1) && Global_Variable_Query(kVariableAffectionTowards) == 3 && Actor_Query_Goal_Number(kActorLucy) != 599) { Actor_Put_In_Set(kActorLucy, 37); Actor_Set_At_XYZ(kActorLucy, -5.0f, 8.0f, -622.0f, 419); Actor_Set_Targetable(kActorLucy, true); - } else if (Actor_Clue_Query(kActorMcCoy, kCluePhoneCallDektora1) && Global_Variable_Query(45) == 2 && Actor_Query_Goal_Number(kActorDektora) != 599) { + } else if (Actor_Clue_Query(kActorMcCoy, kCluePhoneCallDektora1) && Global_Variable_Query(kVariableAffectionTowards) == 2 && Actor_Query_Goal_Number(kActorDektora) != 599) { Actor_Put_In_Set(kActorDektora, 37); Actor_Set_At_XYZ(kActorDektora, -5.0f, 8.0f, -622.0f, 419); Actor_Set_Targetable(kActorDektora, true); diff --git a/engines/bladerunner/script/scene/hf03.cpp b/engines/bladerunner/script/scene/hf03.cpp index 09bf589bb7..e3b01df4f8 100644 --- a/engines/bladerunner/script/scene/hf03.cpp +++ b/engines/bladerunner/script/scene/hf03.cpp @@ -96,8 +96,8 @@ void SceneScriptHF03::sub_401C80() { Actor_Says(kActorLucy, 210, 13); Actor_Says(kActorMcCoy, 1655, 15); Actor_Modify_Friendliness_To_Other(kActorLucy, kActorMcCoy, Random_Query(9, 10)); - if (Actor_Query_Friendliness_To_Other(kActorLucy, kActorMcCoy) > 59 && !Global_Variable_Query(45)) { - Global_Variable_Set(45, 3); + if (Actor_Query_Friendliness_To_Other(kActorLucy, kActorMcCoy) > 59 && Global_Variable_Query(kVariableAffectionTowards) == 0) { + Global_Variable_Set(kVariableAffectionTowards, 3); Actor_Says(kActorLucy, 940, 14); Actor_Says(kActorMcCoy, 6780, 11); Actor_Says(kActorLucy, 950, 12); diff --git a/engines/bladerunner/script/scene/hf05.cpp b/engines/bladerunner/script/scene/hf05.cpp index ff497ebbe8..c96f852973 100644 --- a/engines/bladerunner/script/scene/hf05.cpp +++ b/engines/bladerunner/script/scene/hf05.cpp @@ -114,7 +114,7 @@ bool SceneScriptHF05::ClickedOn3DObject(const char *objectName, bool a2) { ADQ_Add(kActorVoiceOver, 940, -1); Ambient_Sounds_Play_Sound(147, 50, 99, 0, 0); Delay(1500); - Loop_Actor_Walk_To_XYZ(kActorMcCoy, 181.53999f, 40.630001f, 388.09f, 0, 0, true, 0); + Loop_Actor_Walk_To_XYZ(kActorMcCoy, 181.54f, 40.63f, 388.09f, 0, 0, true, 0); Actor_Face_Heading(kActorMcCoy, 0, false); Actor_Change_Animation_Mode(kActorMcCoy, 23); Actor_Clue_Lose(kActorMcCoy, kClueBomb); @@ -163,7 +163,7 @@ bool SceneScriptHF05::ClickedOnExit(int exitId) { return true; } if (exitId == 2) { - if (!Loop_Actor_Walk_To_XYZ(kActorMcCoy, 277.0f, 40.631f, 410.0f, 0, 1, false, 0) && !Game_Flag_Query(684)) { + if (!Loop_Actor_Walk_To_XYZ(kActorMcCoy, 277.0f, 40.63f, 410.0f, 0, 1, false, 0) && !Game_Flag_Query(684)) { Game_Flag_Set(529); Set_Enter(42, kSceneHF06); } @@ -483,10 +483,10 @@ void SceneScriptHF05::sub_403738() { } int SceneScriptHF05::sub_404858() { - if (Global_Variable_Query(45) == 2 && Actor_Query_Goal_Number(kActorDektora) != 599) { + if (Global_Variable_Query(kVariableAffectionTowards) == 2 && Actor_Query_Goal_Number(kActorDektora) != 599) { return kActorDektora; } - if (Global_Variable_Query(45) == 3 && Actor_Query_Goal_Number(kActorLucy) != 599) { + if (Global_Variable_Query(kVariableAffectionTowards) == 3 && Actor_Query_Goal_Number(kActorLucy) != 599) { return kActorLucy; } return -1; @@ -494,15 +494,15 @@ int SceneScriptHF05::sub_404858() { void SceneScriptHF05::sub_4042E4() { Actor_Force_Stop_Walking(kActorMcCoy); - Actor_Put_In_Set(kActorOfficerLeary, 41); - Actor_Set_At_XYZ(kActorOfficerLeary, 430.39999f, 40.630001f, -258.17999f, 300); - Actor_Put_In_Set(kActorOfficerGrayford, 41); - Actor_Set_At_XYZ(kActorOfficerGrayford, 526.40002f, 37.18f, -138.17999f, 300); + Actor_Put_In_Set(kActorOfficerLeary, kSetHF05); + Actor_Set_At_XYZ(kActorOfficerLeary, 430.4f, 40.63f, -258.18f, 300); + Actor_Put_In_Set(kActorOfficerGrayford, kSetHF05); + Actor_Set_At_XYZ(kActorOfficerGrayford, 526.4f, 37.18f, -138.18f, 300); ADQ_Flush(); ADQ_Add(kActorOfficerGrayford, 260, -1); Player_Loses_Control(); - Non_Player_Actor_Combat_Mode_On(kActorOfficerLeary, 3, 1, kActorMcCoy, 4, 4, 7, 8, 0, 0, 100, 100, 1200, 1); - return Non_Player_Actor_Combat_Mode_On(kActorOfficerGrayford, 3, 1, kActorMcCoy, 4, 4, 7, 8, 0, 0, 100, 100, 300, 1); + Non_Player_Actor_Combat_Mode_On(kActorOfficerLeary, kActorCombatStateUncover, true, kActorMcCoy, 4, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, 0, 0, 100, 100, 1200, true); + Non_Player_Actor_Combat_Mode_On(kActorOfficerGrayford, kActorCombatStateUncover, true, kActorMcCoy, 4, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, 0, 0, 100, 100, 300, true); } void SceneScriptHF05::sub_403F0C() { diff --git a/engines/bladerunner/script/scene/hf06.cpp b/engines/bladerunner/script/scene/hf06.cpp index 7a41c9eda2..49ea1d81d2 100644 --- a/engines/bladerunner/script/scene/hf06.cpp +++ b/engines/bladerunner/script/scene/hf06.cpp @@ -180,12 +180,13 @@ void SceneScriptHF06::ActorChangedGoal(int actorId, int newGoal, int oldGoal, bo void SceneScriptHF06::PlayerWalkedIn() { if (Game_Flag_Query(662)) { - int actorId; - if (Global_Variable_Query(45) == 3 && Actor_Query_Goal_Number(kActorLucy) != 599) { + int actorId = -1; + if (Global_Variable_Query(kVariableAffectionTowards) == 3 && Actor_Query_Goal_Number(kActorLucy) != 599) { actorId = kActorLucy; - } else { - actorId = Global_Variable_Query(45) == 2 && Actor_Query_Goal_Number(kActorDektora) != 599 ? kActorDektora : -1; - } + } else if (Global_Variable_Query(kVariableAffectionTowards) == 2 && Actor_Query_Goal_Number(kActorDektora) != 599) { + actorId = kActorDektora; + } + if (actorId != -1) { Actor_Put_In_Set(actorId, 42); if (Game_Flag_Query(559)) { @@ -267,7 +268,7 @@ void SceneScriptHF06::sub_401EF4() { Sound_Play(562, 50, 0, 0, 50); Game_Flag_Set(559); Scene_Exits_Disable(); - Non_Player_Actor_Combat_Mode_On(kActorSteele, 3, 1, actorId, 15, 4, 7, 8, 0, 0, 100, 10, 300, 0); + Non_Player_Actor_Combat_Mode_On(kActorSteele, kActorCombatStateUncover, true, actorId, 15, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, 0, 0, 100, 10, 300, false); } void SceneScriptHF06::sub_4023E0() { diff --git a/engines/bladerunner/script/scene/hf07.cpp b/engines/bladerunner/script/scene/hf07.cpp index e9fe2f05fd..b653cfd08a 100644 --- a/engines/bladerunner/script/scene/hf07.cpp +++ b/engines/bladerunner/script/scene/hf07.cpp @@ -142,11 +142,11 @@ void SceneScriptHF07::DialogueQueueFlushed(int a1) { } int SceneScriptHF07::sub_401864() { - if (Global_Variable_Query(45) == 2 && Actor_Query_Goal_Number(3) != 599) { - return 3; + if (Global_Variable_Query(kVariableAffectionTowards) == 2 && Actor_Query_Goal_Number(3) != 599) { + return kActorDektora; } - if (Global_Variable_Query(45) == 3 && Actor_Query_Goal_Number(6) != 599) { - return 6; + if (Global_Variable_Query(kVariableAffectionTowards) == 3 && Actor_Query_Goal_Number(6) != 599) { + return kActorLucy; } return -1; } diff --git a/engines/bladerunner/script/scene/kp05.cpp b/engines/bladerunner/script/scene/kp05.cpp index 9b080aceb0..99fa6c33fa 100644 --- a/engines/bladerunner/script/scene/kp05.cpp +++ b/engines/bladerunner/script/scene/kp05.cpp @@ -158,7 +158,7 @@ void SceneScriptKP05::PlayerWalkedIn() { Actor_Says(kActorMcCoy, 2220, 3); Actor_Says(kActorSteele, 620, 15); Actor_Says(kActorSteele, 630, 17); - Non_Player_Actor_Combat_Mode_On(kActorSteele, 0, 1, kActorMcCoy, 9, 4, 7, 8, 0, -1, -1, 20, 240, 0); + Non_Player_Actor_Combat_Mode_On(kActorSteele, kActorCombatStateIdle, true, kActorMcCoy, 9, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, 0, -1, -1, 20, 240, false); } } diff --git a/engines/bladerunner/script/scene/ma04.cpp b/engines/bladerunner/script/scene/ma04.cpp index 5f1b41e998..ead9f1946d 100644 --- a/engines/bladerunner/script/scene/ma04.cpp +++ b/engines/bladerunner/script/scene/ma04.cpp @@ -176,9 +176,9 @@ bool SceneScriptMA04::ClickedOn2DRegion(int region) { Overlay_Remove("MA04OVER"); Delay(500); if (Game_Flag_Query(653)) { - if (Global_Variable_Query(45) == 2) { + if (Global_Variable_Query(kVariableAffectionTowards) == 2) { phoneCallWithDektora(); - } else if (Global_Variable_Query(45) == 3) { + } else if (Global_Variable_Query(kVariableAffectionTowards) == 3) { phoneCallWithLucy(); } else { phoneCallWithClovis(); diff --git a/engines/bladerunner/script/scene/nr01.cpp b/engines/bladerunner/script/scene/nr01.cpp index 171cea52d8..fd7c9911d0 100644 --- a/engines/bladerunner/script/scene/nr01.cpp +++ b/engines/bladerunner/script/scene/nr01.cpp @@ -384,7 +384,7 @@ void SceneScriptNR01::PlayerWalkedIn() { if (Actor_Query_Goal_Number(kActorGordo) == 230) { Scene_Exits_Disable(); Actor_Set_Goal_Number(kActorGordo, 231); - Non_Player_Actor_Combat_Mode_On(kActorGordo, 0, 1, kActorMcCoy, 3, 4, 7, 8, -1, -1, -1, 20, 300, 0); + Non_Player_Actor_Combat_Mode_On(kActorGordo, kActorCombatStateIdle, true, kActorMcCoy, 3, kAnimationModeCombatIdle, kAnimationModeCombatWalk, kAnimationModeCombatRun, -1, -1, -1, 20, 300, false); } } else if (Game_Flag_Query(545)) { Game_Flag_Reset(545); diff --git a/engines/bladerunner/script/scene/nr04.cpp b/engines/bladerunner/script/scene/nr04.cpp index 18583f0d75..00aee0b4ad 100644 --- a/engines/bladerunner/script/scene/nr04.cpp +++ b/engines/bladerunner/script/scene/nr04.cpp @@ -187,7 +187,7 @@ void SceneScriptNR04::ActorChangedGoal(int actorId, int newGoal, int oldGoal, bo Delay(2500); Actor_Says(kActorEarlyQ, 290, 3); sub_401DB0(); - //return true; + //return true; break; case 213: Actor_Clue_Acquire(kActorMcCoy, kClueDektorasDressingRoom, 0, kActorEarlyQ); diff --git a/engines/bladerunner/script/scene/nr09.cpp b/engines/bladerunner/script/scene/nr09.cpp index fd4faeccb0..7f786e17c8 100644 --- a/engines/bladerunner/script/scene/nr09.cpp +++ b/engines/bladerunner/script/scene/nr09.cpp @@ -107,7 +107,7 @@ void SceneScriptNR09::ActorChangedGoal(int actorId, int newGoal, int oldGoal, bo void SceneScriptNR09::PlayerWalkedIn() { if (Game_Flag_Query(614)) { - Loop_Actor_Walk_To_XYZ(kActorMcCoy, -704.07001f, 0.35f, 623.04f, 0, 0, false, 0); + Loop_Actor_Walk_To_XYZ(kActorMcCoy, -704.07f, 0.35f, 623.04f, 0, 0, false, 0); Game_Flag_Reset(614); } //return false; diff --git a/engines/bladerunner/script/scene/nr11.cpp b/engines/bladerunner/script/scene/nr11.cpp index 95ef4b7d84..5c4f4c141c 100644 --- a/engines/bladerunner/script/scene/nr11.cpp +++ b/engines/bladerunner/script/scene/nr11.cpp @@ -140,7 +140,7 @@ bool SceneScriptNR11::ClickedOn3DObject(const char *objectName, bool a2) { } else { Actor_Says(kActorMcCoy, 3840, 18); Delay(1000); - if (Actor_Query_Friendliness_To_Other(kActorDektora, kActorMcCoy) > 59 && !Global_Variable_Query(45)) { + if (Actor_Query_Friendliness_To_Other(kActorDektora, kActorMcCoy) > 59 && Global_Variable_Query(kVariableAffectionTowards) == 0) { Music_Play(21, 35, 0, 3, -1, 0, 0); } Loop_Actor_Walk_To_XYZ(kActorDektora, -135.0f, 0.33f, -267.0f, 0, 0, false, 0); @@ -164,8 +164,8 @@ bool SceneScriptNR11::ClickedOn3DObject(const char *objectName, bool a2) { Actor_Says(kActorMcCoy, 3870, 3); Actor_Says(kActorDektora, 1070, 14); Actor_Modify_Friendliness_To_Other(kActorDektora, kActorMcCoy, 5); - if (Actor_Query_Friendliness_To_Other(kActorDektora, kActorMcCoy) > 55 && !Global_Variable_Query(45)) { - Global_Variable_Set(45, 2); + if (Actor_Query_Friendliness_To_Other(kActorDektora, kActorMcCoy) > 55 && Global_Variable_Query(kVariableAffectionTowards) == 0) { + Global_Variable_Set(kVariableAffectionTowards, 2); Actor_Says(kActorDektora, 1130, 17); Actor_Says(kActorMcCoy, 6365, 12); Actor_Says(kActorDektora, 1140, 14); diff --git a/engines/bladerunner/script/scene/ps10.cpp b/engines/bladerunner/script/scene/ps10.cpp index d45a4b8e67..3957ee1c7d 100644 --- a/engines/bladerunner/script/scene/ps10.cpp +++ b/engines/bladerunner/script/scene/ps10.cpp @@ -24,8 +24,301 @@ namespace BladeRunner { +enum PoliceMazePS10Tracks { + kPoliceMazePS10Track1 = 0, + kPoliceMazePS10Track2 = 1, + kPoliceMazePS10Track3 = 2, + kPoliceMazePS10Track4 = 3, + kPoliceMazePS10Track5 = 4, + kPoliceMazePS10Track6 = 5, + kPoliceMazePS10Track7 = 6, + kPoliceMazePS10Track8 = 7, + kPoliceMazePS10Track9 = 8 +}; + +static int kPoliceMazePS10TargetCount = 20; + +static const int *getPoliceMazePS10TrackData1() { + static int trackData[] = { + kPMTIActivate, kVariablePoliceMazePS10TargetCounter, kPoliceMazePS10TargetCount, + kPMTIVariableInc, kVariablePoliceMazePS10TargetCounter, kPoliceMazePS10TargetCount, + kPMTIObstacleReset, kItemPoliceMazeTarget1, + kPMTIObstacleReset, kItemPoliceMazeTarget2, + kPMTIFacing, 989, + kPMTIPosition, 0, + kPMTITargetSet, kItemPoliceMazeTarget1, 1, + kPMTITargetSet, kItemPoliceMazeTarget2, 1, + kPMTIEnemyReset, kItemPoliceMazeTarget1, + kPMTIWaitRandom, 3000, 5000, + kPMTIObstacleSet, kItemPoliceMazeTarget1, + kPMTIPlaySound, 159, 100, + kPMTIMove, 14, + kPMTIWait, 1000, + kPMTIRotate, 740, 80, + kPMTIEnemySet, kItemPoliceMazeTarget1, + kPMTIWait, 0, + kPMTIRotate, 488, 80, + kPMTIWait, 1000, + kPMTIShoot, 27, 33, + kPMTIWait, 0, + kPMTIRotate, 740, 80, + kPMTIPausedReset, kPoliceMazePS10Track2, + kPMTIObstacleReset, kItemPoliceMazeTarget1, + kPMTIObstacleSet, kItemPoliceMazeTarget2, + kPMTIPausedSet, kPoliceMazePS10Track1, + kPMTIPosition, 0, + kPMTIRestart + }; + + return trackData; +} + +static const int *getPoliceMazePS10TrackData2() { + static int trackData[] = { + kPMTIFacing, 740, + kPMTIPosition, 0, + kPMTIEnemySet, kItemPoliceMazeTarget2, + kPMTIMove, 69, + kPMTIWait, 500, + kPMTIObstacleReset, kItemPoliceMazeTarget2, + kPMTIPausedReset, kPoliceMazePS10Track5, + kPMTIPausedSet, kPoliceMazePS10Track2, + kPMTIPosition, 0, + kPMTIRestart + }; + return trackData; +} + +static const int *getPoliceMazePS10TrackData3() { + static int trackData[] = { + kPMTIActivate, kVariablePoliceMazePS10TargetCounter, kPoliceMazePS10TargetCount, + kPMTIVariableInc, kVariablePoliceMazePS10TargetCounter, kPoliceMazePS10TargetCount, + kPMTIObstacleReset, kItemPoliceMazeTarget3, + kPMTIFacing, 993, + kPMTIPosition, 0, + kPMTIWaitRandom, 3000, 5000, + kPMTIObstacleSet, kItemPoliceMazeTarget3, + kPMTIPlaySound, 159, 100, + kPMTITargetSet, kItemPoliceMazeTarget3, 1, + kPMTIEnemyReset, kItemPoliceMazeTarget3, + kPMTIMove, 5, + kPMTIWait, 1000, + kPMTIEnemySet, kItemPoliceMazeTarget3, + kPMTIRotate, 233, 80, + kPMTIWait, 0, + kPMTIRotate, 491, 80, + kPMTIWait, 500, + kPMTIShoot, 27, 33, + kPMTIWait, 500, + kPMTIRotate, 233, 80, + kPMTIWait, 0, + kPMTIRotate, 993, 80, + kPMTIPlaySound, 34, 33, + kPMTIMove, 0, + kPMTIObstacleReset, kItemPoliceMazeTarget3, + kPMTIRestart + }; + return trackData; +} + +static const int *getPoliceMazePS10TrackData4() { + static int trackData[] = { + kPMTIActivate, kVariablePoliceMazePS10TargetCounter, kPoliceMazePS10TargetCount, + kPMTIVariableInc, kVariablePoliceMazePS10TargetCounter, kPoliceMazePS10TargetCount, + kPMTIObstacleReset, kItemPoliceMazeTarget4, + kPMTIFacing, 993, + kPMTIPosition, 0, + kPMTIWaitRandom, 3000, 6000, + kPMTIObstacleSet, kItemPoliceMazeTarget4, + kPMTIPlaySound, 159, 100, + kPMTITargetSet, kItemPoliceMazeTarget4, 1, + kPMTIEnemyReset, kItemPoliceMazeTarget4, + kPMTIMove, 34, + kPMTIWait, 500, + kPMTIRotate, 491, 80, + kPMTIMove, 0, + kPMTILeave, + kPMTIObstacleReset, kItemPoliceMazeTarget4, + kPMTIPausedReset, kPoliceMazePS10Track8, + kPMTIPausedSet, kPoliceMazePS10Track4, + kPMTIRestart + }; + return trackData; +} + +static const int *getPoliceMazePS10TrackData5() { + static int trackData[] = { + kPMTIActivate, kVariablePoliceMazePS10TargetCounter, kPoliceMazePS10TargetCount, + kPMTIVariableInc, kVariablePoliceMazePS10TargetCounter, kPoliceMazePS10TargetCount, + kPMTIObstacleReset, kItemPoliceMazeTarget5, + kPMTIFacing, 0, + kPMTIPosition, 0, + kPMTIWaitRandom, 4000, 6000, + kPMTIObstacleSet, kItemPoliceMazeTarget5, + kPMTIPlaySound, 159, 100, + kPMTITargetSet, kItemPoliceMazeTarget5, 1, + kPMTIEnemyReset, kItemPoliceMazeTarget5, + kPMTIMove, 5, + kPMTIWait, 1000, + kPMTIRotate, 512, 100, + kPMTIWait, 2000, + kPMTIRotate, 0, -100, + kPMTIPlaySound, 34, 33, + kPMTIMove, 0, + kPMTILeave, + kPMTIObstacleReset, kItemPoliceMazeTarget5, + kPMTIPausedReset, kPoliceMazePS10Track1, + kPMTIPausedSet, kPoliceMazePS10Track5, + kPMTIRestart + }; + return trackData; +} + +static const int *getPoliceMazePS10TrackData6() { + static int trackData[] = { + kPMTIActivate, kVariablePoliceMazePS10TargetCounter, kPoliceMazePS10TargetCount, + kPMTIVariableInc, kVariablePoliceMazePS10TargetCounter, kPoliceMazePS10TargetCount, + kPMTIObstacleReset, kItemPoliceMazeTarget6, + kPMTIFacing, 999, + kPMTIPosition, 0, + kPMTIWaitRandom, 4000, 6000, + kPMTIObstacleSet, kItemPoliceMazeTarget6, + kPMTIPlaySound, 159, 100, + kPMTITargetSet, kItemPoliceMazeTarget6, 1, + kPMTIEnemyReset, kItemPoliceMazeTarget6, + kPMTIMove, 7, + kPMTIWait, 500, + kPMTIEnemySet, kItemPoliceMazeTarget6, + kPMTIRotate, 750, 80, + kPMTIWait, 0, + kPMTIRotate, 500, 80, + kPMTIWait, 1000, + kPMTIShoot, 27, 33, + kPMTIWait, 0, + kPMTIRotate, 750, 80, + kPMTIWait, 0, + kPMTIRotate, 999, 80, + kPMTIPlaySound, 34, 33, + kPMTIMove, 0, + kPMTIObstacleReset, kItemPoliceMazeTarget6, + kPMTIPausedReset, kPoliceMazePS10Track7, + kPMTIPausedReset, kPoliceMazePS10Track9, + kPMTIPausedSet, kPoliceMazePS10Track6, + kPMTIRestart + }; + return trackData; +} + +static const int *getPoliceMazePS10TrackData7() { + static int trackData[] = { + kPMTIActivate, kVariablePoliceMazePS10TargetCounter, kPoliceMazePS10TargetCount, + kPMTIVariableInc, kVariablePoliceMazePS10TargetCounter, kPoliceMazePS10TargetCount, + kPMTIObstacleReset, kItemPoliceMazeTarget7, + kPMTIFacing, 264, + kPMTIPosition, 0, + kPMTIWaitRandom, 3000, 6000, + kPMTITargetSet, kItemPoliceMazeTarget7, 1, + kPMTIEnemyReset, kItemPoliceMazeTarget7, + kPMTIObstacleSet, kItemPoliceMazeTarget7, + kPMTIMove, 89, + kPMTIWaitRandom, 4000, 8000, + kPMTIFacing, 776, + kPMTIMove, 0, + kPMTILeave, + kPMTIObstacleReset, kItemPoliceMazeTarget7, + kPMTIPausedSet, kPoliceMazePS10Track7, + kPMTIRestart + }; + return trackData; +} + +static const int *getPoliceMazePS10TrackData8() { + static int trackData[] = { + kPMTIActivate, kVariablePoliceMazePS10TargetCounter, kPoliceMazePS10TargetCount, + kPMTIVariableInc, kVariablePoliceMazePS10TargetCounter, kPoliceMazePS10TargetCount, + kPMTIObstacleReset, kItemPoliceMazeTarget8, + kPMTIFacing, 993, + kPMTIPosition, 0, + kPMTIWaitRandom, 4000, 6000, + kPMTIObstacleSet, kItemPoliceMazeTarget8, + kPMTIPlaySound, 159, 100, + kPMTITargetSet, kItemPoliceMazeTarget8, 1, + kPMTIEnemyReset, kItemPoliceMazeTarget8, + kPMTIMove, 34, + kPMTIWait, 500, + kPMTIEnemySet, kItemPoliceMazeTarget8, + kPMTIRotate, 491, 80, + kPMTIMove, 20, + kPMTIWait, 0, + kPMTIShoot, 27, 33, + kPMTIMove, 0, + kPMTIObstacleReset, kItemPoliceMazeTarget8, + kPMTIPausedReset, kPoliceMazePS10Track4, + kPMTIPausedSet, kPoliceMazePS10Track8, + kPMTIRestart + }; + return trackData; +} + +static const int *getPoliceMazePS10TrackData9() { + static int trackData[] = { + kPMTIActivate, kVariablePoliceMazePS10TargetCounter, kPoliceMazePS10TargetCount, + kPMTIVariableInc, kVariablePoliceMazePS10TargetCounter, kPoliceMazePS10TargetCount, + kPMTIObstacleReset, kItemPoliceMazeTarget9, + kPMTIFacing, 738, + kPMTIPosition, 0, + kPMTIWaitRandom, 2000, 5000, + kPMTITargetSet, kItemPoliceMazeTarget9, 1, + kPMTIEnemySet, kItemPoliceMazeTarget9, + kPMTIObstacleSet, kItemPoliceMazeTarget9, + kPMTIPlaySound, 0, 33, + kPMTIMove, 23, + kPMTIPlaySound, 0, 33, + kPMTIWait, 200, + kPMTIPlaySound, 32, 33, + kPMTIRotate, 498, 100, + kPMTIPlaySound, 0, 33, + kPMTIWait, 100, + kPMTIShoot, 27, 33, + kPMTIPlaySound, 32, 33, + kPMTIMove, 35, + kPMTIPlaySound, 32, 33, + kPMTIWait, 100, + kPMTIShoot, 27, 33, + kPMTIPlaySound, 0, 33, + kPMTIMove, 23, + kPMTIPlaySound, 32, 33, + kPMTIWait, 100, + kPMTIShoot, 27, 33, + kPMTIPlaySound, 32, 33, + kPMTIRotate, 758, 100, + kPMTIPlaySound, 32, 33, + kPMTIMove, 89, + kPMTIPlaySound, 0, 33, + kPMTIWaitRandom, 4000, 6000, + kPMTITargetSet, kItemPoliceMazeTarget9, 1, + kPMTIEnemySet, kItemPoliceMazeTarget9, + kPMTIFacing, 216, + kPMTIPlaySound, 32, 33, + kPMTIMove, 69, + kPMTIWait, 100, + kPMTIPlaySound, 32, 33, + kPMTIRotate, 498, 100, + kPMTIWait, 100, + kPMTIShoot, 27, 33, + kPMTIPlaySound, 0, 33, + kPMTIRotate, 216, 100, + kPMTIPlaySound, 32, 33, + kPMTIMove, 0, + kPMTIObstacleReset, kItemPoliceMazeTarget9, + kPMTIPausedSet, kPoliceMazePS10Track9, + kPMTIRestart + }; + return trackData; +} + void SceneScriptPS10::InitializeScene() { - Police_Maze_Set_Pause_State(1); + Police_Maze_Set_Pause_State(true); if (Game_Flag_Query(15)) { float x = World_Waypoint_Query_X(4); float y = World_Waypoint_Query_Y(4); @@ -38,30 +331,20 @@ void SceneScriptPS10::InitializeScene() { Ambient_Sounds_Remove_All_Non_Looping_Sounds(0); Ambient_Sounds_Add_Looping_Sound(387, 50, 1, 1); Ambient_Sounds_Add_Looping_Sound(54, 50, 1, 1); - Ambient_Sounds_Add_Sound(1, 10, 50, 16, 25, -100, 100, -101, -101, 0, 0); - Ambient_Sounds_Add_Sound(389, 5, 50, 16, 25, -100, 100, -101, -101, 0, 0); - Ambient_Sounds_Add_Sound(390, 6, 50, 16, 25, -100, 100, -101, -101, 0, 0); - Ambient_Sounds_Add_Sound(443, 2, 100, 14, 16, -100, 100, -101, -101, 0, 0); - Ambient_Sounds_Add_Sound(444, 2, 100, 14, 16, -100, 100, -101, -101, 0, 0); - Ambient_Sounds_Add_Sound(445, 2, 100, 14, 16, -100, 100, -101, -101, 0, 0); - Ambient_Sounds_Add_Sound(446, 2, 100, 14, 16, -100, 100, -101, -101, 0, 0); - Ambient_Sounds_Add_Sound(303, 5, 100, 17, 27, -100, 100, -101, -101, 0, 0); - Ambient_Sounds_Add_Sound(304, 5, 100, 17, 27, -100, 100, -101, -101, 0, 0); - Ambient_Sounds_Add_Sound(305, 5, 100, 17, 27, -100, 100, -101, -101, 0, 0); - Ambient_Sounds_Add_Sound(306, 5, 100, 17, 27, -100, 100, -101, -101, 0, 0); - Ambient_Sounds_Add_Sound(307, 5, 100, 17, 27, -100, 100, -101, -101, 0, 0); - Ambient_Sounds_Add_Sound(308, 5, 100, 17, 27, -100, 100, -101, -101, 0, 0); -} - -static int track_data_0[] = {-26, 10, 20, -18, 10, 20, -9, 0, -9, 1,-5, 989, -1, 0, -15, 0, 1, -15, 1, 1,-23, 0, -7, 3000, 5000, -8, 0, -10, 159, 100,-2, 14, -3, 1000, -6, 740, 80, -22, 0, -3,0, -6, 488, 80, -3, 1000, -24, 27, 33, -3, 0, -6, 740, 80, -11, 1, -9, 0, -8, 1, -12, 0, -1, 0, -4}; -static int track_data_1[] = {-5, 740, -1, 0, -22, 1, -2, 69, -3, 500, -9, 1, -11, 4, -12, 1, -1, 0, -4}; -static int track_data_2[] = {-26, 10, 20, -18, 10, 20, -9, 2, -5, 993, -1, 0, -7, 3000, 5000, -8, 2, -10, 159, 100, -15, 2, 1, -23, 2, -2, 5, -3, 1000, -22, 2, -6, 233, 80, -3, 0, -6, 491, 80, -3, 500, -24, 27, 33, -3, 500, -6, 233, 80, -3, 0, -6, 993, 80, -10, 34, 33, -2, 0, -9, 2, -4}; -static int track_data_3[] = {-26, 10, 20, -18, 10, 20, -9, 3, -5, 993, -1, 0, -7, 3000, 6000, -8, 3, -10, 159, 100, -15, 3, 1, -23, 3, -2, 34, -3, 500, -6, 491, 80, -2, 0, -25, -9, 3, -11, 7, -12, 3, -4}; -static int track_data_4[] = {-26, 10, 20, -18, 10, 20, -9, 4, -5, 0, -1, 0, -7, 4000, 6000, -8, 4, -10, 159, 100, -15, 4, 1, -23, 4, -2, 5, -3, 1000, -6, 512, 100, -3, 2000, -6, 0, -100, -10, 34, 33, -2, 0, -25, -9, 4, -11, 0, -12, 4, -4}; -static int track_data_5[] = {-26, 10, 20, -18, 10, 20, -9, 5, -5, 999, -1, 0, -7, 4000, 6000, -8, 5, -10, 159, 100, -15, 5, 1, -23, 5, -2, 7, -3, 500, -22, 5, -6, 750, 80, -3, 0, -6, 500, 80, -3, 1000, -24, 27, 33, -3, 0, -6, 750, 80, -3, 0, -6, 999, 80, -10, 34, 33, -2, 0, -9, 5, -11, 6, -11, 8, -12, 5, -4}; -static int track_data_6[] = {-26, 10, 20, -18, 10, 20, -9, 6, -5, 264, -1, 0, -7, 3000, 6000, -15, 6, 1, -23, 6, -8, 6, -2, 89, -7, 4000, 8000, -5, 776, -2, 0, -25, -9, 6, -12, 6, -4}; -static int track_data_7[] = {-26, 10, 20, -18, 10, 20, -9, 7, -5, 993, -1, 0, -7, 4000, 6000, -8, 7, -10, 159, 100, -15, 7, 1, -23, 7, -2, 34, -3, 500, -22, 7, -6, 491, 80, -2, 20, -3, 0, -24, 27, 33, -2, 0, -9, 7, -11, 3, -12, 7, -4}; -static int track_data_8[] = {-26, 10, 20, -18, 10, 20, -9, 8, -5, 738, -1, 0, -7, 2000, 5000, -15, 8, 1, -22, 8, -8, 8, -10, 0, 33, -2, 23, -10, 0, 33, -3, 200, -10, 32, 33, -6, 498, 100, -10, 0, 33, -3, 100, -24, 27, 33, -10, 32, 33, -2, 35, -10, 32, 33, -3, 100, -24, 27, 33, -10, 0, 33, -2, 23, -10, 32, 33, -3, 100, -24, 27, 33, -10, 32, 33, -6, 758, 100, -10, 32, 33, -2, 89, -10, 0, 33, -7, 4000, 6000, -15, 8, 1, -22, 8, -5, 216, -10, 32, 33, -2, 69, -3, 100, -10, 32, 33, -6, 498, 100, -3, 100, -24, 27, 33, -10, 0, 33, -6, 216, 100, -10, 32, 33, -2, 0, -9, 8, -12, 8, -4}; + Ambient_Sounds_Add_Sound( 1, 10, 50, 16, 25, -100, 100, -101, -101, 0, 0); + Ambient_Sounds_Add_Sound(389, 5, 50, 16, 25, -100, 100, -101, -101, 0, 0); + Ambient_Sounds_Add_Sound(390, 6, 50, 16, 25, -100, 100, -101, -101, 0, 0); + Ambient_Sounds_Add_Sound(443, 2, 100, 14, 16, -100, 100, -101, -101, 0, 0); + Ambient_Sounds_Add_Sound(444, 2, 100, 14, 16, -100, 100, -101, -101, 0, 0); + Ambient_Sounds_Add_Sound(445, 2, 100, 14, 16, -100, 100, -101, -101, 0, 0); + Ambient_Sounds_Add_Sound(446, 2, 100, 14, 16, -100, 100, -101, -101, 0, 0); + Ambient_Sounds_Add_Sound(303, 5, 100, 17, 27, -100, 100, -101, -101, 0, 0); + Ambient_Sounds_Add_Sound(304, 5, 100, 17, 27, -100, 100, -101, -101, 0, 0); + Ambient_Sounds_Add_Sound(305, 5, 100, 17, 27, -100, 100, -101, -101, 0, 0); + Ambient_Sounds_Add_Sound(306, 5, 100, 17, 27, -100, 100, -101, -101, 0, 0); + Ambient_Sounds_Add_Sound(307, 5, 100, 17, 27, -100, 100, -101, -101, 0, 0); + Ambient_Sounds_Add_Sound(308, 5, 100, 17, 27, -100, 100, -101, -101, 0, 0); +} void SceneScriptPS10::SceneLoaded() { Obstacle_Object("PARKMETR01", true); @@ -85,26 +368,26 @@ void SceneScriptPS10::SceneLoaded() { Unclickable_Object("PARKMETR16"); Unobstacle_Object("E.SM.WIRE01", true); if (!Query_System_Currently_Loading_Game()) { - Item_Add_To_World(0, 443, 14, -240.0f, -80.74f, 145.0f, 989, 72, 36, true, false, false, true); - Item_Add_To_World(1, 443, 14, -240.0f, -8.74f, 145.0f, 740, 72, 36, true, false, false, true); - Item_Add_To_World(2, 445, 14, -165.0f, 111.53f, -10.0f, 993, 72, 36, true, false, false, true); - Item_Add_To_World(3, 447, 14, -125.0f, 160.0f, -10.0f, 993, 72, 36, true, false, false, true); - Item_Add_To_World(4, 441, 14, -246.71f, 205.51f, -20.0f, 0, 72, 36, true, false, false, true); - Item_Add_To_World(5, 445, 14, -27.69f, -86.92f, 434.0f, 999, 72, 36, true, false, false, true); - Item_Add_To_World(6, 441, 14, -347.15f, 7.68f, -20.0f, 264, 72, 36, true, false, false, true); - Item_Add_To_World(7, 449, 14, -51.0f, 160.0f, -10.0f, 993, 72, 36, true, false, false, true); - Item_Add_To_World(8, 445, 14, 39.0f, 9.16f, -20.0f, 738, 72, 36, true, false, false, true); + Item_Add_To_World(kItemPoliceMazeTarget1, 443, 14, -240.0f, -80.74f, 145.0f, 989, 72, 36, true, false, false, true); + Item_Add_To_World(kItemPoliceMazeTarget2, 443, 14, -240.0f, -8.74f, 145.0f, 740, 72, 36, true, false, false, true); + Item_Add_To_World(kItemPoliceMazeTarget3, 445, 14, -165.0f, 111.53f, -10.0f, 993, 72, 36, true, false, false, true); + Item_Add_To_World(kItemPoliceMazeTarget4, 447, 14, -125.0f, 160.0f, -10.0f, 993, 72, 36, true, false, false, true); + Item_Add_To_World(kItemPoliceMazeTarget5, 441, 14, -246.71f, 205.51f, -20.0f, 0, 72, 36, true, false, false, true); + Item_Add_To_World(kItemPoliceMazeTarget6, 445, 14, -27.69f, -86.92f, 434.0f, 999, 72, 36, true, false, false, true); + Item_Add_To_World(kItemPoliceMazeTarget7, 441, 14, -347.15f, 7.68f, -20.0f, 264, 72, 36, true, false, false, true); + Item_Add_To_World(kItemPoliceMazeTarget8, 449, 14, -51.0f, 160.0f, -10.0f, 993, 72, 36, true, false, false, true); + Item_Add_To_World(kItemPoliceMazeTarget9, 445, 14, 39.0f, 9.16f, -20.0f, 738, 72, 36, true, false, false, true); } - Police_Maze_Target_Track_Add(0, -240.0f, -80.74f, 145.0f, -240.0f, -8.74f, 145.0f, 15, track_data_0, false); - Police_Maze_Target_Track_Add(1, -240.0f, -8.74f, 145.0f, -450.0f, -8.74f, 145.0f, 70, track_data_1, false); - Police_Maze_Target_Track_Add(2, -165.0f, 111.53f, -10.0f, -165.0f, 167.53f, -10.0f, 6, track_data_2, true); - Police_Maze_Target_Track_Add(3, -125.0f, 160.0f, -10.0f, -51.0f, 160.0f, -10.0f, 35, track_data_3, false); - Police_Maze_Target_Track_Add(4, -246.71f, 205.51f, -20.0f, -246.71f, 241.51f, -20.0f, 6, track_data_4, true); - Police_Maze_Target_Track_Add(5, -27.69f, -86.92f, 434.0f, -27.69f, -18.92f, 434.0f, 8, track_data_5, true); - Police_Maze_Target_Track_Add(6, -347.15f, 7.68f, -20.0f, 39.0f, 9.16f, -20.0f, 90, track_data_6, false); - Police_Maze_Target_Track_Add(7, -51.0f, 160.0f, -10.0f, -125.0f, 160.0f, -10.0f, 35, track_data_7, true); - Police_Maze_Target_Track_Add(8, 39.0f, 9.16f, -20.0f, -347.15f, 7.68f, -20.0f, 90, track_data_8, false); + Police_Maze_Target_Track_Add(kItemPoliceMazeTarget1, -240.0f, -80.74f, 145.0f, -240.0f, -8.74f, 145.0f, 15, getPoliceMazePS10TrackData1(), false); + Police_Maze_Target_Track_Add(kItemPoliceMazeTarget2, -240.0f, -8.74f, 145.0f, -450.0f, -8.74f, 145.0f, 70, getPoliceMazePS10TrackData2(), false); + Police_Maze_Target_Track_Add(kItemPoliceMazeTarget3, -165.0f, 111.53f, -10.0f, -165.0f, 167.53f, -10.0f, 6, getPoliceMazePS10TrackData3(), true); + Police_Maze_Target_Track_Add(kItemPoliceMazeTarget4, -125.0f, 160.0f, -10.0f, -51.0f, 160.0f, -10.0f, 35, getPoliceMazePS10TrackData4(), false); + Police_Maze_Target_Track_Add(kItemPoliceMazeTarget5, -246.71f, 205.51f, -20.0f, -246.71f, 241.51f, -20.0f, 6, getPoliceMazePS10TrackData5(), true); + Police_Maze_Target_Track_Add(kItemPoliceMazeTarget6, -27.69f, -86.92f, 434.0f, -27.69f, -18.92f, 434.0f, 8, getPoliceMazePS10TrackData6(), true); + Police_Maze_Target_Track_Add(kItemPoliceMazeTarget7, -347.15f, 7.68f, -20.0f, 39.0f, 9.16f, -20.0f, 90, getPoliceMazePS10TrackData7(), false); + Police_Maze_Target_Track_Add(kItemPoliceMazeTarget8, -51.0f, 160.0f, -10.0f, -125.0f, 160.0f, -10.0f, 35, getPoliceMazePS10TrackData8(), true); + Police_Maze_Target_Track_Add(kItemPoliceMazeTarget9, 39.0f, 9.16f, -20.0f, -347.15f, 7.68f, -20.0f, 90, getPoliceMazePS10TrackData9(), false); Preload(441); Preload(442); Preload(443); @@ -132,13 +415,13 @@ bool SceneScriptPS10::ClickedOnActor(int actorId) { bool SceneScriptPS10::ClickedOnItem(int itemId, bool a2) { if (Player_Query_Combat_Mode()) { switch (itemId) { - case 3: + case kItemPoliceMazeTarget4: Sound_Play(4, 50, 0, 0, 50); break; - case 4: + case kItemPoliceMazeTarget5: Sound_Play(555, 50, 0, 0, 50); break; - case 6: + case kItemPoliceMazeTarget7: Sound_Play(555, 50, 0, 0, 50); break; default: @@ -146,34 +429,34 @@ bool SceneScriptPS10::ClickedOnItem(int itemId, bool a2) { break; } Item_Spin_In_World(itemId); - if (itemId == 0) { - Item_Flag_As_Non_Target(0); - Item_Flag_As_Non_Target(1); + if (itemId == kItemPoliceMazeTarget1) { + Item_Flag_As_Non_Target(kItemPoliceMazeTarget1); + Item_Flag_As_Non_Target(kItemPoliceMazeTarget2); } - if (itemId == 1) { - Item_Flag_As_Non_Target(0); - Item_Flag_As_Non_Target(1); + if (itemId == kItemPoliceMazeTarget2) { + Item_Flag_As_Non_Target(kItemPoliceMazeTarget1); + Item_Flag_As_Non_Target(kItemPoliceMazeTarget2); } - if (itemId == 2) { - Item_Flag_As_Non_Target(2); + if (itemId == kItemPoliceMazeTarget3) { + Item_Flag_As_Non_Target(kItemPoliceMazeTarget3); } - if (itemId == 3) { - Item_Flag_As_Non_Target(3); + if (itemId == kItemPoliceMazeTarget4) { + Item_Flag_As_Non_Target(kItemPoliceMazeTarget4); } - if (itemId == 4) { - Item_Flag_As_Non_Target(4); + if (itemId == kItemPoliceMazeTarget5) { + Item_Flag_As_Non_Target(kItemPoliceMazeTarget5); } - if (itemId == 5) { - Item_Flag_As_Non_Target(5); + if (itemId == kItemPoliceMazeTarget6) { + Item_Flag_As_Non_Target(kItemPoliceMazeTarget6); } - if (itemId == 6) { - Item_Flag_As_Non_Target(6); + if (itemId == kItemPoliceMazeTarget7) { + Item_Flag_As_Non_Target(kItemPoliceMazeTarget7); } - if (itemId == 7) { - Item_Flag_As_Non_Target(7); + if (itemId == kItemPoliceMazeTarget8) { + Item_Flag_As_Non_Target(kItemPoliceMazeTarget8); } - if (itemId == 8) { - Item_Flag_As_Non_Target(8); + if (itemId == kItemPoliceMazeTarget9) { + Item_Flag_As_Non_Target(kItemPoliceMazeTarget9); } else { Item_Flag_As_Non_Target(itemId); } @@ -185,12 +468,12 @@ bool SceneScriptPS10::ClickedOnItem(int itemId, bool a2) { bool SceneScriptPS10::ClickedOnExit(int exitId) { if (exitId == 1) { - if (!Loop_Actor_Walk_To_Waypoint(kActorMcCoy, 6, 12, 1, false)) { + if (!Loop_Actor_Walk_To_Waypoint(kActorMcCoy, 6, 12, true, false)) { Game_Flag_Set(14); - sub_402238(); - Global_Variable_Decrement(9, 20 - Global_Variable_Query(10)); - Global_Variable_Set(10, 20); - Set_Enter(14, kScenePS11); + removeTargets(); + Global_Variable_Decrement(kVariablePoliceMazeScore, kPoliceMazePS10TargetCount - Global_Variable_Query(kVariablePoliceMazePS10TargetCounter)); + Global_Variable_Set(kVariablePoliceMazePS10TargetCounter, kPoliceMazePS10TargetCount); + Set_Enter(kSetPS10_PS11_PS12_PS13, kScenePS11); } return true; } @@ -210,18 +493,18 @@ void SceneScriptPS10::ActorChangedGoal(int actorId, int newGoal, int oldGoal, bo void SceneScriptPS10::PlayerWalkedIn() { if (Game_Flag_Query(15)) { - Loop_Actor_Walk_To_XYZ(kActorMcCoy, -352.09f, -9.23f, 267.95f, 0, 0, true, 0); - Police_Maze_Set_Pause_State(0); + Loop_Actor_Walk_To_XYZ(kActorMcCoy, -352.09f, -9.23f, 267.95f, 0, false, true, 0); + Police_Maze_Set_Pause_State(false); Game_Flag_Reset(15); //return true; return; } else { Player_Set_Combat_Mode(true); - Loop_Actor_Walk_To_Waypoint(kActorMcCoy, 5, 0, 0, true); - Actor_Says(kActorAnsweringMachine, 280, 3); - Actor_Says(kActorAnsweringMachine, 290, 3); - Actor_Says(kActorAnsweringMachine, 300, 3); - Police_Maze_Set_Pause_State(0); + Loop_Actor_Walk_To_Waypoint(kActorMcCoy, 5, 0, false, true); + Actor_Says(kActorAnsweringMachine, 280, kAnimationModeTalk); + Actor_Says(kActorAnsweringMachine, 290, kAnimationModeTalk); + Actor_Says(kActorAnsweringMachine, 300, kAnimationModeTalk); + Police_Maze_Set_Pause_State(false); //return true; return; } @@ -233,16 +516,16 @@ void SceneScriptPS10::PlayerWalkedOut() { void SceneScriptPS10::DialogueQueueFlushed(int a1) { } -void SceneScriptPS10::sub_402238() { - Item_Remove_From_World(0); - Item_Remove_From_World(1); - Item_Remove_From_World(2); - Item_Remove_From_World(3); - Item_Remove_From_World(4); - Item_Remove_From_World(5); - Item_Remove_From_World(6); - Item_Remove_From_World(7); - Item_Remove_From_World(8); +void SceneScriptPS10::removeTargets() { + Item_Remove_From_World(kItemPoliceMazeTarget1); + Item_Remove_From_World(kItemPoliceMazeTarget2); + Item_Remove_From_World(kItemPoliceMazeTarget3); + Item_Remove_From_World(kItemPoliceMazeTarget4); + Item_Remove_From_World(kItemPoliceMazeTarget5); + Item_Remove_From_World(kItemPoliceMazeTarget6); + Item_Remove_From_World(kItemPoliceMazeTarget7); + Item_Remove_From_World(kItemPoliceMazeTarget8); + Item_Remove_From_World(kItemPoliceMazeTarget9); } } // End of namespace BladeRunner diff --git a/engines/bladerunner/script/scene/ps11.cpp b/engines/bladerunner/script/scene/ps11.cpp index 1aa227b022..e6311b3ca1 100644 --- a/engines/bladerunner/script/scene/ps11.cpp +++ b/engines/bladerunner/script/scene/ps11.cpp @@ -254,7 +254,7 @@ void SceneScriptPS11::ActorChangedGoal(int actorId, int newGoal, int oldGoal, bo } void SceneScriptPS11::PlayerWalkedIn() { - Police_Maze_Set_Pause_State(0); + Police_Maze_Set_Pause_State(false); } void SceneScriptPS11::PlayerWalkedOut() { diff --git a/engines/bladerunner/script/scene/ps12.cpp b/engines/bladerunner/script/scene/ps12.cpp index 7db642891e..d4d771a94f 100644 --- a/engines/bladerunner/script/scene/ps12.cpp +++ b/engines/bladerunner/script/scene/ps12.cpp @@ -25,7 +25,7 @@ namespace BladeRunner { void SceneScriptPS12::InitializeScene() { - Police_Maze_Set_Pause_State(1); + Police_Maze_Set_Pause_State(true); if (Game_Flag_Query(16)) { Scene_Loop_Start_Special(0, 0, 0); Scene_Loop_Set_Default(1); @@ -96,9 +96,9 @@ void SceneScriptPS12::SceneLoaded() { } Police_Maze_Target_Track_Add(29, -691.8f, -9.06f, 587.67f, -649.11f, -9.06f, 587.71f, 6, track_data_29, true); Police_Maze_Target_Track_Add(30, -679.6f, -45.4f, 721.05f, -679.6f, -1.4f, 721.05f, 6, track_data_30, true); - Police_Maze_Target_Track_Add(31, -414.04f, -8.98f, 711.917f, -459.54f, -8.99f, 707.81f, 6, track_data_31, false); + Police_Maze_Target_Track_Add(31, -414.04f, -8.98f, 711.91f, -459.54f, -8.99f, 707.81f, 6, track_data_31, false); Police_Maze_Target_Track_Add(32, -440.0f, -8.97f, 1137.0f, -430.0f, -8.97f, 921.0f, 6, track_data_32, false); - Police_Maze_Target_Track_Add(33, -764.92f, -0.84f, 950.21997f, -722.92f, -0.84f, 950.22f, 6, track_data_33, false); + Police_Maze_Target_Track_Add(33, -764.92f, -0.84f, 950.22f, -722.92f, -0.84f, 950.22f, 6, track_data_33, false); Police_Maze_Target_Track_Add(34, -696.0f, -5.7f, 1185.0f, -635.0f, -5.7f, 1185.0f, 20, track_data_34, false); Police_Maze_Target_Track_Add(35, -635.0f, -5.7f, 1165.0f, -620.0f, -8.63f, 1366.0f, 10, track_data_35, false); Police_Maze_Target_Track_Add(36, -620.0f, -8.63f, 1366.0f, -595.0f, -8.63f, 1366.0f, 10, track_data_36, false); @@ -270,7 +270,7 @@ void SceneScriptPS12::PlayerWalkedIn() { Loop_Actor_Walk_To_XYZ(kActorMcCoy, -546.0f, -9.06f, 570.0f, 0, 1, false, 0); Game_Flag_Reset(16); } - Police_Maze_Set_Pause_State(0); + Police_Maze_Set_Pause_State(false); } void SceneScriptPS12::PlayerWalkedOut() { diff --git a/engines/bladerunner/script/scene/ps13.cpp b/engines/bladerunner/script/scene/ps13.cpp index 83f99faf4f..f525d48137 100644 --- a/engines/bladerunner/script/scene/ps13.cpp +++ b/engines/bladerunner/script/scene/ps13.cpp @@ -25,7 +25,7 @@ namespace BladeRunner { void SceneScriptPS13::InitializeScene() { - Police_Maze_Set_Pause_State(1); + Police_Maze_Set_Pause_State(true); if (Game_Flag_Query(18)) { Scene_Loop_Start_Special(0, 0, 0); Scene_Loop_Set_Default(1); @@ -99,7 +99,7 @@ void SceneScriptPS13::SceneLoaded() { Item_Add_To_World(51, 443, 14, -24.0f, 102.0f, 1625.0f, 823, 72, 36, true, false, false, true); Item_Add_To_World(52, 449, 14, 180.0f, -72.7f, 1605.0f, 305, 72, 36, true, false, false, true); Item_Add_To_World(53, 443, 14, 127.79f, 14.56f, 1703.03f, 356, 72, 36, true, false, false, true); - Item_Add_To_World(54, 443, 14, 136.37f, -6.84f, 1425.4301f, 512, 72, 36, true, false, false, true); + Item_Add_To_World(54, 443, 14, 136.37f, -6.84f, 1425.43f, 512, 72, 36, true, false, false, true); Item_Add_To_World(55, 441, 14, 77.83f, -79.8f, 1520.5f, 327, 72, 36, true, false, false, true); Item_Add_To_World(56, 441, 14, 77.83f, -7.8f, 1520.5f, 327, 72, 36, true, false, false, true); Item_Add_To_World(57, 443, 14, -88.0f, -8.8f, 1520.5f, 327, 72, 36, true, false, false, true); @@ -115,7 +115,7 @@ void SceneScriptPS13::SceneLoaded() { Police_Maze_Target_Track_Add(51, -24.0f, 102.0f, 1625.0f, -24.0f, 138.0f, 1625.0f, 10, track_data_51, false); Police_Maze_Target_Track_Add(52, 180.0f, -72.7f, 1605.0f, 180.0f, -0.7f, 1605.0f, 10, track_data_52, false); Police_Maze_Target_Track_Add(53, 127.79f, 14.56f, 1703.03f, -56.07f, 1.89f, 1589.04f, 6, track_data_53, false); - Police_Maze_Target_Track_Add(54, 136.37f, -6.84f, 1425.4301f, 117.55f, -6.84f, 1442.09f, 4, track_data_54, false); + Police_Maze_Target_Track_Add(54, 136.37f, -6.84f, 1425.43f, 117.55f, -6.84f, 1442.09f, 4, track_data_54, false); Police_Maze_Target_Track_Add(55, 77.83f, -79.8f, 1520.5f, 77.83f, -7.8f, 1520.5f, 15, track_data_55, false); Police_Maze_Target_Track_Add(56, 77.83f, -7.8f, 1520.5f, -88.0f, -8.8f, 1520.5f, 15, track_data_56, false); Police_Maze_Target_Track_Add(57, -88.0f, -8.8f, 1520.5f, -88.0f, -80.8f, 1520.5f, 15, track_data_57, false); @@ -255,7 +255,7 @@ void SceneScriptPS13::ActorChangedGoal(int actorId, int newGoal, int oldGoal, bo } void SceneScriptPS13::PlayerWalkedIn() { - Police_Maze_Set_Pause_State(0); + Police_Maze_Set_Pause_State(false); } void SceneScriptPS13::PlayerWalkedOut() { diff --git a/engines/bladerunner/script/scene/rc01.cpp b/engines/bladerunner/script/scene/rc01.cpp index 867e3bd661..db89807022 100644 --- a/engines/bladerunner/script/scene/rc01.cpp +++ b/engines/bladerunner/script/scene/rc01.cpp @@ -74,6 +74,8 @@ void SceneScriptRC01::InitializeScene() { // Global_Variable_Set(kVariableChapter, 2); // Chapter_Enter(2, kSetRC03, kSceneRC03); + Set_Enter(14, 73); + #endif if (!Game_Flag_Query(kFlagIntroPlayed)) { diff --git a/engines/bladerunner/script/scene/tb05.cpp b/engines/bladerunner/script/scene/tb05.cpp index b3299292f1..a7797414d6 100644 --- a/engines/bladerunner/script/scene/tb05.cpp +++ b/engines/bladerunner/script/scene/tb05.cpp @@ -52,10 +52,10 @@ void SceneScriptTB05::SceneLoaded() { Clickable_Object("MONITOR05"); Unclickable_Object("SMUDGE_GLASS01"); if (!Actor_Clue_Query(kActorMcCoy, kClueDragonflyEarring)) { - Item_Add_To_World(76, 940, 72, 76.160004f, 147.36f, -235.14999f, 0, 6, 6, false, true, false, true); + Item_Add_To_World(76, 940, 72, 76.16f, 147.36f, -235.15f, 0, 6, 6, false, true, false, true); } if (!Actor_Clue_Query(kActorMcCoy, kClueTyrellSalesPamphlet1) && !Actor_Clue_Query(kActorMcCoy, kClueTyrellSalesPamphlet2) && (Game_Flag_Query(kFlagGordoIsReplicant) || Game_Flag_Query(kFlagLucyIsReplicant))) { - Item_Add_To_World(119, 972, 72, 129.00999f, 147.12f, -162.98f, 0, 8, 8, false, true, false, true); + Item_Add_To_World(119, 972, 72, 129.01f, 147.12f, -162.98f, 0, 8, 8, false, true, false, true); } } diff --git a/engines/bladerunner/script/scene/ug02.cpp b/engines/bladerunner/script/scene/ug02.cpp index c6b96c65a5..f8f1e84578 100644 --- a/engines/bladerunner/script/scene/ug02.cpp +++ b/engines/bladerunner/script/scene/ug02.cpp @@ -153,9 +153,9 @@ bool SceneScriptUG02::ClickedOnExit(int exitId) { Loop_Actor_Travel_Stairs(kActorMcCoy, 4, 0, 0); Footstep_Sound_Override_Off(); int v3 = Player_Query_Combat_Mode(); - Loop_Actor_Walk_To_XYZ(kActorMcCoy, -96.57f, 74.870003f, -271.28f, 0, 0, v3, 0); + Loop_Actor_Walk_To_XYZ(kActorMcCoy, -96.57f, 74.87f, -271.28f, 0, 0, v3, 0); int v4 = Player_Query_Combat_Mode(); - Loop_Actor_Walk_To_XYZ(kActorMcCoy, -95.0f, 74.870003f, -503.0f, 0, 0, v4, 0); + Loop_Actor_Walk_To_XYZ(kActorMcCoy, -95.0f, 74.87f, -503.0f, 0, 0, v4, 0); Game_Flag_Set(315); Set_Enter(74, kSceneUG01); } diff --git a/engines/bladerunner/script/scene/ug05.cpp b/engines/bladerunner/script/scene/ug05.cpp index c3996dca37..3348ebbfb6 100644 --- a/engines/bladerunner/script/scene/ug05.cpp +++ b/engines/bladerunner/script/scene/ug05.cpp @@ -222,10 +222,10 @@ void SceneScriptUG05::DialogueQueueFlushed(int a1) { } int SceneScriptUG05::sub_4021B0() { - if (Global_Variable_Query(45) == 2 && Actor_Query_Goal_Number(kActorDektora) != 599) { + if (Global_Variable_Query(kVariableAffectionTowards) == 2 && Actor_Query_Goal_Number(kActorDektora) != 599) { return kActorDektora; } - if (Global_Variable_Query(45) == 3 && Actor_Query_Goal_Number(kActorLucy) != 599) { + if (Global_Variable_Query(kVariableAffectionTowards) == 3 && Actor_Query_Goal_Number(kActorLucy) != 599) { return kActorLucy; } return -1; diff --git a/engines/bladerunner/script/scene/ug07.cpp b/engines/bladerunner/script/scene/ug07.cpp index 9a586407fc..4a256b02c7 100644 --- a/engines/bladerunner/script/scene/ug07.cpp +++ b/engines/bladerunner/script/scene/ug07.cpp @@ -119,7 +119,7 @@ bool SceneScriptUG07::ClickedOnExit(int exitId) { Actor_Set_At_XYZ(kActorClovis, 118.02f, -12.21f, -154.0f, 768); Player_Set_Combat_Mode(true); Actor_Face_Actor(kActorMcCoy, kActorClovis, true); - Loop_Actor_Walk_To_XYZ(kActorClovis, 98.019997f, -12.21f, -154.0f, 0, 0, false, 0); + Loop_Actor_Walk_To_XYZ(kActorClovis, 98.02f, -12.21f, -154.0f, 0, 0, false, 0); Actor_Face_Actor(kActorClovis, kActorMcCoy, true); Actor_Set_Goal_Number(kActorMcCoy, 301); Actor_Face_Heading(kActorMcCoy, 0, true); diff --git a/engines/bladerunner/script/scene/ug15.cpp b/engines/bladerunner/script/scene/ug15.cpp index 8477e5b46f..3503992457 100644 --- a/engines/bladerunner/script/scene/ug15.cpp +++ b/engines/bladerunner/script/scene/ug15.cpp @@ -26,9 +26,9 @@ namespace BladeRunner { void SceneScriptUG15::InitializeScene() { if (Game_Flag_Query(353)) { - Setup_Scene_Information(-25.0f, 26.309999f, -434.0f, 520); + Setup_Scene_Information(-25.0f, 26.31f, -434.0f, 520); } else if (Game_Flag_Query(153)) { - Setup_Scene_Information(-17.0f, 26.309999f, -346.0f, 711); + Setup_Scene_Information(-17.0f, 26.31f, -346.0f, 711); } else if (Game_Flag_Query(355)) { Setup_Scene_Information(-18.0f, 48.07f, 62.0f, 650); } else { diff --git a/engines/bladerunner/script/scene/ug18.cpp b/engines/bladerunner/script/scene/ug18.cpp index cafe42654a..05ef4559fb 100644 --- a/engines/bladerunner/script/scene/ug18.cpp +++ b/engines/bladerunner/script/scene/ug18.cpp @@ -110,7 +110,7 @@ bool SceneScriptUG18::ClickedOnItem(int itemId, bool a2) { bool SceneScriptUG18::ClickedOnExit(int exitId) { if (exitId == 0) { - if (!Loop_Actor_Walk_To_XYZ(kActorMcCoy, -684.712f, 0.0f, 171.59f, 0, 1, false, 0)) { + if (!Loop_Actor_Walk_To_XYZ(kActorMcCoy, -684.71f, 0.0f, 171.59f, 0, 1, false, 0)) { Ambient_Sounds_Remove_All_Non_Looping_Sounds(1); Ambient_Sounds_Remove_All_Looping_Sounds(1); Game_Flag_Set(435); @@ -323,9 +323,8 @@ void SceneScriptUG18::sub_402734() { } void SceneScriptUG18::sub_402DE8() { - if (Player_Query_Agenda()) { - if (Global_Variable_Query(45) > 1 || Player_Query_Agenda() == 2) { + if (Global_Variable_Query(kVariableAffectionTowards) > 1 || Player_Query_Agenda() == 2) { sub_403114(); } else { sub_402F8C(); diff --git a/engines/bladerunner/script/scene_script.cpp b/engines/bladerunner/script/scene_script.cpp index 01fe3e3b1e..ff43853044 100644 --- a/engines/bladerunner/script/scene_script.cpp +++ b/engines/bladerunner/script/scene_script.cpp @@ -161,8 +161,10 @@ void SceneScript::initializeScene() { } void SceneScript::sceneLoaded() { + _vm->_sceneIsLoading = true; _inScriptCounter++; _currentScript->SceneLoaded(); + _vm->_sceneIsLoading = false; _inScriptCounter--; } diff --git a/engines/bladerunner/script/scene_script.h b/engines/bladerunner/script/scene_script.h index 54047ed8a8..51cac52db6 100644 --- a/engines/bladerunner/script/scene_script.h +++ b/engines/bladerunner/script/scene_script.h @@ -385,7 +385,7 @@ DECLARE_SCRIPT(PS09) END_SCRIPT DECLARE_SCRIPT(PS10) - void sub_402238(); + void removeTargets(); END_SCRIPT DECLARE_SCRIPT(PS11) diff --git a/engines/bladerunner/script/script.cpp b/engines/bladerunner/script/script.cpp index 2aca6db9d3..3c68717116 100644 --- a/engines/bladerunner/script/script.cpp +++ b/engines/bladerunner/script/script.cpp @@ -45,6 +45,7 @@ #include "bladerunner/set_effects.h" #include "bladerunner/scene.h" #include "bladerunner/scene_objects.h" +#include "bladerunner/script/police_maze.h" #include "bladerunner/slice_animations.h" #include "bladerunner/slice_renderer.h" #include "bladerunner/suspects_database.h" @@ -52,6 +53,7 @@ #include "bladerunner/ui/elevator.h" #include "bladerunner/ui/esper.h" #include "bladerunner/ui/kia.h" +#include "bladerunner/ui/scores.h" #include "bladerunner/ui/spinner.h" #include "bladerunner/ui/vk.h" #include "bladerunner/vector.h" @@ -130,7 +132,7 @@ void ScriptBase::Actor_Face_Heading(int actorId, int heading, bool animate) { } int ScriptBase::Actor_Query_Friendliness_To_Other(int actorId, int otherActorId) { - return _vm->_actors[actorId]->_friendlinessToOther[otherActorId]; + return _vm->_actors[actorId]->getFriendlinessToOther(otherActorId); } void ScriptBase::Actor_Modify_Friendliness_To_Other(int actorId, int otherActorId, signed int change) { @@ -158,27 +160,27 @@ void ScriptBase::Actor_Set_Combat_Aggressiveness(int actorId, int combatAggressi } int ScriptBase::Actor_Query_Current_HP(int actorId) { - return _vm->_actors[actorId]->_currentHP; + return _vm->_actors[actorId]->getCurrentHP(); } int ScriptBase::Actor_Query_Max_HP(int actorId) { - return _vm->_actors[actorId]->_maxHP; + return _vm->_actors[actorId]->getMaxHP(); } int ScriptBase::Actor_Query_Combat_Aggressiveness(int actorId) { - return _vm->_actors[actorId]->_combatAggressiveness; + return _vm->_actors[actorId]->getCombatAggressiveness(); } int ScriptBase::Actor_Query_Honesty(int actorId) { - return _vm->_actors[actorId]->_honesty; + return _vm->_actors[actorId]->getHonesty(); } int ScriptBase::Actor_Query_Intelligence(int actorId) { - return _vm->_actors[actorId]->_intelligence; + return _vm->_actors[actorId]->getIntelligence(); } int ScriptBase::Actor_Query_Stability(int actorId) { - return _vm->_actors[actorId]->_stability; + return _vm->_actors[actorId]->getStability(); } void ScriptBase::Actor_Modify_Current_HP(int actorId, signed int change) { @@ -218,8 +220,8 @@ void ScriptBase::Actor_Combat_AI_Hit_Attempt(int actorId) { _vm->_actors[actorId]->_combatInfo->hitAttempt(); } -void ScriptBase::Non_Player_Actor_Combat_Mode_On(int actorId, int a2, int a3, int otherActorId, int a5, int animationModeCombatIdle, int animationModeCombatWalk, int animationModeCombatRun, int a9, int a10, int a11, int a12, int a13, int a14) { - _vm->_actors[actorId]->combatModeOn(a2, a3, otherActorId, a5, animationModeCombatIdle, animationModeCombatWalk, animationModeCombatRun, a9, a10, a11, a12, a13, a14); +void ScriptBase::Non_Player_Actor_Combat_Mode_On(int actorId, int initialState, bool rangedAttack, int enemyId, int waypointType, int animationModeCombatIdle, int animationModeCombatWalk, int animationModeCombatRun, int fleeRatio, int coverRatio, int actionRatio, int damage, int range, bool unstoppable) { + _vm->_actors[actorId]->combatModeOn(initialState, rangedAttack, enemyId, waypointType, animationModeCombatIdle, animationModeCombatWalk, animationModeCombatRun, fleeRatio, coverRatio, actionRatio, damage, range, unstoppable); } void ScriptBase::Non_Player_Actor_Combat_Mode_Off(int actorId) { @@ -680,15 +682,20 @@ void ScriptBase::Item_Remove_From_World(int itemId) { } void ScriptBase::Item_Spin_In_World(int itemId) { - warning("Item_Spin_In_World(%d)", itemId); + _vm->_items->spinInWorld(itemId); + if (_vm->_items->isPoliceMazeEnemy(itemId)) { + Police_Maze_Increment_Score(1); + } else { + Police_Maze_Decrement_Score(1); + } } void ScriptBase::Item_Flag_As_Target(int itemId) { - warning("Item_Flag_As_Target(%d)", itemId); + _vm->_items->setIsTarget(itemId, true); } void ScriptBase::Item_Flag_As_Non_Target(int itemId) { - warning("Item_Flag_As_Non_Target(%d)", itemId); + _vm->_items->setIsTarget(itemId, false); } void ScriptBase::Item_Pickup_Spin_Effect(int animationId, int x, int y) { @@ -723,8 +730,9 @@ int ScriptBase::Animation_Skip_To_Frame() { void ScriptBase::Delay(int miliseconds) { Player_Loses_Control(); int endTime = _vm->getTotalPlayTime() + miliseconds; - while ((int)_vm->getTotalPlayTime() < endTime) + while ((int)_vm->getTotalPlayTime() < endTime) { _vm->gameTick(); + } Player_Gains_Control(); } @@ -825,8 +833,7 @@ int ScriptBase::Random_Query(int min, int max) { } void ScriptBase::Sound_Play(int id, int volume, int panFrom, int panTo, int priority) { - const char *name = _vm->_gameInfo->getSfxTrack(id); - _vm->_audioPlayer->playAud(name, volume, panFrom, panTo, priority); + _vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(id), volume, panFrom, panTo, priority); } void ScriptBase::Sound_Play_Speech_Line(int actorId, int sentenceId, int volume, int a4, int priority) { @@ -892,8 +899,7 @@ void ScriptBase::Footstep_Sound_Override_Off() { } bool ScriptBase::Music_Play(int musicId, int volume, int pan, int timeFadeIn, int timePlay, int loop, int timeFadeOut) { - const char *musicName = _vm->_gameInfo->getMusicTrack(musicId); - return _vm->_music->play(musicName, volume, pan, timeFadeIn, timePlay, loop, timeFadeOut); + return _vm->_music->play(_vm->_gameInfo->getMusicTrack(musicId), volume, pan, timeFadeIn, timePlay, loop, timeFadeOut); } void ScriptBase::Music_Adjust(int volume, int pan, int delay) { @@ -1075,31 +1081,56 @@ float ScriptBase::World_Waypoint_Query_Z(int waypointId) { return _vm->_waypoints->getZ(waypointId); } -void ScriptBase::Combat_Cover_Waypoint_Set_Data(int combatCoverId, int type, int setId, int sceneId, float x, float y, float z) { - //TODO - warning("Combat_Cover_Waypoint_Set_Data(%d, %d, %d, %d, %f, %f, %f)", combatCoverId, type, setId, sceneId, x, y, z); +void ScriptBase::Combat_Cover_Waypoint_Set_Data(int coverWaypointId, int type, int setId, int sceneId, float x, float y, float z) { + assert(coverWaypointId < (int)_vm->_combat->_coverWaypoints.size()); + + _vm->_combat->_coverWaypoints[coverWaypointId].type = type; + _vm->_combat->_coverWaypoints[coverWaypointId].setId = setId; + _vm->_combat->_coverWaypoints[coverWaypointId].sceneId = sceneId; + _vm->_combat->_coverWaypoints[coverWaypointId].position.x = x; + _vm->_combat->_coverWaypoints[coverWaypointId].position.y = y; + _vm->_combat->_coverWaypoints[coverWaypointId].position.z = z; } -void ScriptBase::Combat_Flee_Waypoint_Set_Data(int combatFleeWaypointId, int type, int setId, int sceneId, float x, float y, float z, int a8) { - //TODO - warning("Combat_Cover_Waypoint_Set_Data(%d, %d, %d, %d, %f, %f, %f, %d)", combatFleeWaypointId, type, setId, sceneId, x, y, z, a8); +void ScriptBase::Combat_Flee_Waypoint_Set_Data(int fleeWaypointId, int type, int setId, int sceneId, float x, float y, float z, int a8) { + assert(fleeWaypointId < (int)_vm->_combat->_fleeWaypoints.size()); + + _vm->_combat->_fleeWaypoints[fleeWaypointId].type = type; + _vm->_combat->_fleeWaypoints[fleeWaypointId].setId = setId; + _vm->_combat->_fleeWaypoints[fleeWaypointId].sceneId = sceneId; + _vm->_combat->_fleeWaypoints[fleeWaypointId].position.x = x; + _vm->_combat->_fleeWaypoints[fleeWaypointId].position.y = y; + _vm->_combat->_fleeWaypoints[fleeWaypointId].position.z = z; + _vm->_combat->_fleeWaypoints[fleeWaypointId].field7 = a8; } -void ScriptBase::Police_Maze_Target_Track_Add(int itemId, float startX, float startY, float startZ, float endX, float endY, float endZ, int steps, signed int data[], bool a10) { - //TODO - warning("Police_Maze_Target_Track_Add(%d, %f, %f, %f, %f, %f, %f, %d, %p, %d)", itemId, startX, startY, startZ, endX, endY, endZ, steps, (void *)data, a10); +void ScriptBase::Police_Maze_Target_Track_Add(int itemId, float startX, float startY, float startZ, float endX, float endY, float endZ, int steps, const int* instructions, bool isActive) { + _vm->_policeMaze->_tracks[itemId]->add(itemId, startX, startY, startZ, endX, endY, endZ, steps, instructions, isActive); + _vm->_policeMaze->activate(); +} + +int ScriptBase::Police_Maze_Query_Score() { + return Global_Variable_Query(kVariablePoliceMazeScore); +} +void ScriptBase::Police_Maze_Zero_Score() { + Global_Variable_Reset(kVariablePoliceMazeScore); } -// ScriptBase::Police_Maze_Query_Score -// ScriptBase::Police_Maze_Zero_Score -// ScriptBase::Police_Maze_Increment_Score -// ScriptBase::Police_Maze_Decrement_Score -// ScriptBase::Police_Maze_Set_Score +void ScriptBase::Police_Maze_Increment_Score(int delta) { + Global_Variable_Set(kVariablePoliceMazeScore, Global_Variable_Query(kVariablePoliceMazeScore) + delta); +} -void ScriptBase::Police_Maze_Set_Pause_State(int a1) { - //TODO - warning("Police_Maze_Set_Pause_State(%d)", a1); +void ScriptBase::Police_Maze_Decrement_Score(int delta) { + Global_Variable_Set(kVariablePoliceMazeScore, Global_Variable_Query(kVariablePoliceMazeScore) - delta); +} + +void ScriptBase::Police_Maze_Set_Score(int value) { + Global_Variable_Set(kVariablePoliceMazeScore, value); +} + +void ScriptBase::Police_Maze_Set_Pause_State(bool state) { + _vm->_policeMaze->setPauseState(state); } void ScriptBase::CDB_Set_Crime(int clueId, int crimeId) { @@ -1181,18 +1212,15 @@ int ScriptBase::Elevator_Activate(int elevatorId) { } void ScriptBase::View_Score_Board() { - //TODO - warning("View_Score_Board()"); + _vm->_scores->open(); } -int ScriptBase::Query_Score(int a0) { - warning("Query_Score(%d)", a0); - - return 0; +int ScriptBase::Query_Score(int index) { + return _vm->_scores->query(index); } -void ScriptBase::Set_Score(int a0, int a1) { - warning("Set_Score(%d, %d)", a0, a1); +void ScriptBase::Set_Score(int index, int value) { + _vm->_scores->set(index, value); } void ScriptBase::Give_McCoy_Ammo(int ammoType, int ammo) { @@ -1221,10 +1249,8 @@ bool ScriptBase::Query_System_Currently_Loading_Game() { void ScriptBase::Actor_Retired_Here(int actorId, int width, int height, int retired, int retiredByActorId) { Actor *actor = _vm->_actors[actorId]; - Vector3 actorPosition; - actor->getXYZ(&actorPosition.x, &actorPosition.y, &actorPosition.z); actor->retire(retired, width, height, retiredByActorId); - actor->setAtXYZ(actorPosition, actor->getFacing(), true, false, true); + actor->setAtXYZ(actor->getXYZ(), actor->getFacing(), true, false, true); _vm->_sceneObjects->setRetired(actorId + kSceneObjectOffsetActors, true); } diff --git a/engines/bladerunner/script/script.h b/engines/bladerunner/script/script.h index 7faf3886b2..09c372d009 100644 --- a/engines/bladerunner/script/script.h +++ b/engines/bladerunner/script/script.h @@ -23,16 +23,18 @@ #ifndef BLADERUNNER_SCRIPT_H #define BLADERUNNER_SCRIPT_H -#include "common/str.h" - #include "bladerunner/bladerunner.h" #include "bladerunner/game_constants.h" +#include "common/str.h" + namespace BladeRunner { class BladeRunnerEngine; class ScriptBase { +friend class SceneScript; + protected: BladeRunnerEngine *_vm; @@ -78,7 +80,7 @@ protected: void Actor_Set_Flag_Damage_Anim_If_Moving(int actorId, bool value); bool Actor_Query_Flag_Damage_Anim_If_Moving(int actorId); void Actor_Combat_AI_Hit_Attempt(int actorId); - void Non_Player_Actor_Combat_Mode_On(int actorId, int a2, int a3, int otherActorId, int a5, int animationModeCombatIdle, int animationModeCombatWalk, int animationModeCombatRun, int a9, int a10, int a11, int a12, int a13, int a14); + void Non_Player_Actor_Combat_Mode_On(int actorId, int initialState, bool rangedAttack, int enemyId, int waypointType, int animationModeCombatIdle, int animationModeCombatWalk, int animationModeCombatRun, int fleeRatio, int coverRatio, int actionRatio, int damage, int range, bool unstoppable); void Non_Player_Actor_Combat_Mode_Off(int actorId); void Actor_Set_Health(int actorId, int hp, int maxHp); void Actor_Set_Targetable(int actorId, bool targetable); @@ -205,15 +207,15 @@ protected: float World_Waypoint_Query_X(int waypointId); float World_Waypoint_Query_Y(int waypointId); float World_Waypoint_Query_Z(int waypointId); - void Combat_Cover_Waypoint_Set_Data(int combatCoverId, int a2, int setId, int a4, float x, float y, float z); - void Combat_Flee_Waypoint_Set_Data(int combatFleeWaypointId, int a2, int setId, int a4, float x, float y, float z, int a8); - void Police_Maze_Target_Track_Add(int itemId, float startX, float startY, float startZ, float endX, float endY, float endZ, int steps, signed int data[], bool a10); - // Police_Maze_Query_Score - // Police_Maze_Zero_Score - // Police_Maze_Increment_Score - // Police_Maze_Decrement_Score - // Police_Maze_Set_Score - void Police_Maze_Set_Pause_State(int a1); + void Combat_Cover_Waypoint_Set_Data(int coverWaypointId, int a2, int setId, int a4, float x, float y, float z); + void Combat_Flee_Waypoint_Set_Data(int fleeWaypointId, int a2, int setId, int a4, float x, float y, float z, int a8); + void Police_Maze_Target_Track_Add(int itemId, float startX, float startY, float startZ, float endX, float endY, float endZ, int steps, const int* instructions, bool isActive); + int Police_Maze_Query_Score(); + void Police_Maze_Zero_Score(); + void Police_Maze_Increment_Score(int delta); + void Police_Maze_Decrement_Score(int delta); + void Police_Maze_Set_Score(int value); + void Police_Maze_Set_Pause_State(bool state); void CDB_Set_Crime(int clueId, int crimeId); void CDB_Set_Clue_Asset_Type(int clueId, int assetType); void SDB_Set_Actor(int suspectId, int actorId); diff --git a/engines/bladerunner/script/vk_script.cpp b/engines/bladerunner/script/vk_script.cpp index 8a7ddb26eb..eef5f5bdaf 100644 --- a/engines/bladerunner/script/vk_script.cpp +++ b/engines/bladerunner/script/vk_script.cpp @@ -1209,12 +1209,12 @@ void VKScript::askDektora(int questionId) { VK_Play_Speech_Line(kActorDektora, 1520, 0.5f); VK_Play_Speech_Line(kActorMcCoy, 7840, 0.5f); VK_Subject_Reacts(20, -1, 9, 10); - VK_Play_Speech_Line(kActorDektora, 1540, 0.80000001f); + VK_Play_Speech_Line(kActorDektora, 1540, 0.8f); VK_Play_Speech_Line(kActorDektora, 1550, 0.5f); } else { VK_Play_Speech_Line(kActorDektora, 1560, 0.5f); VK_Subject_Reacts(25, 13, -3, 0); - VK_Play_Speech_Line(kActorDektora, 1570, 0.80000001f); + VK_Play_Speech_Line(kActorDektora, 1570, 0.8f); VK_Play_Speech_Line(kActorDektora, 1580, 0.5f); } break; @@ -1234,7 +1234,7 @@ void VKScript::askDektora(int questionId) { VK_Subject_Reacts(25, -1, 9, 0); } else { VK_Subject_Reacts(25, 14, -2, 0); - VK_Play_Speech_Line(kActorDektora, 1630, 0.89999998f); + VK_Play_Speech_Line(kActorDektora, 1630, 0.9f); VK_Play_Speech_Line(kActorDektora, 1640, 0.5f); } break; @@ -1274,7 +1274,7 @@ void VKScript::askDektora(int questionId) { VK_Play_Speech_Line(kActorDektora, 1740, 0.5f); VK_Play_Speech_Line(kActorMcCoy, 7805, 0.5f); VK_Eye_Animates(2); - VK_Play_Speech_Line(kActorDektora, 1750, 0.89999998f); + VK_Play_Speech_Line(kActorDektora, 1750, 0.9f); VK_Play_Speech_Line(kActorDektora, 1760, 0.5f); break; case 7455: diff --git a/engines/bladerunner/set.cpp b/engines/bladerunner/set.cpp index fe10c189b6..ac026a4d76 100644 --- a/engines/bladerunner/set.cpp +++ b/engines/bladerunner/set.cpp @@ -25,6 +25,7 @@ #include "bladerunner/bladerunner.h" #include "bladerunner/game_constants.h" #include "bladerunner/lights.h" +#include "bladerunner/savefile.h" #include "bladerunner/scene_objects.h" #include "bladerunner/set_effects.h" #include "bladerunner/slice_renderer.h" @@ -67,8 +68,10 @@ bool Set::open(const Common::String &name) { _objectCount = s->readUint32LE(); assert(_objectCount <= 85); + char buf[20]; for (int i = 0; i < _objectCount; ++i) { - s->read(_objects[i].name, 20); + s->read(buf, sizeof(buf)); + _objects[i].name = buf; float x0, y0, z0, x1, y1, z1; x0 = s->readFloatLE(); @@ -82,6 +85,7 @@ bool Set::open(const Common::String &name) { _objects[i].isObstacle = s->readByte(); _objects[i].isClickable = s->readByte(); _objects[i].isHotMouse = 0; + _objects[i].unknown1 = 0; _objects[i].isTarget = 0; s->skip(4); @@ -94,7 +98,9 @@ bool Set::open(const Common::String &name) { for (int i = 0; i < _walkboxCount; ++i) { float x, z; - s->read(_walkboxes[i].name, 20); + s->read(buf, sizeof(buf)); + _walkboxes[i].name = buf; + _walkboxes[i].altitude = s->readFloatLE(); _walkboxes[i].vertexCount = s->readUint32LE(); @@ -129,7 +135,7 @@ bool Set::open(const Common::String &name) { void Set::addObjectsToScene(SceneObjects *sceneObjects) const { for (int i = 0; i < _objectCount; i++) { - sceneObjects->addObject(i + kSceneObjectOffsetObjects, &_objects[i].bbox, _objects[i].isClickable, _objects[i].isObstacle, _objects[i].unknown1, _objects[i].isTarget); + sceneObjects->addObject(i + kSceneObjectOffsetObjects, _objects[i].bbox, _objects[i].isClickable, _objects[i].isObstacle, _objects[i].unknown1, _objects[i].isTarget); } } @@ -207,15 +213,14 @@ int Set::findWalkbox(float x, float z) const { return result; } -int Set::findObject(const char *objectName) const { - int i; - for (i = 0; i < (int)_objectCount; i++) { - if (scumm_stricmp(objectName, _objects[i].name) == 0) { +int Set::findObject(const Common::String &objectName) const { + for (int i = 0; i < _objectCount; ++i) { + if (objectName.compareToIgnoreCase(_objects[i].name) == 0) { return i; } } - debug("Set::findObject didn't find \"%s\"", objectName); + debug("Set::findObject didn't find \"%s\"", objectName.c_str()); return -1; } @@ -256,7 +261,7 @@ void Set::objectSetIsTarget(int objectId, bool isTarget) { _objects[objectId].isTarget = isTarget; } -const char *Set::objectGetName(int objectId) const { +const Common::String &Set::objectGetName(int objectId) const { return _objects[objectId].name; } @@ -327,4 +332,74 @@ int Set::getWalkboxSoundRunLeft(int walkboxId) const { int Set::getWalkboxSoundRunRight(int walkboxId) const { return getWalkboxSoundWalkRight(walkboxId); } + +void Set::save(SaveFileWriteStream &f) { + f.writeBool(_loaded); + f.writeInt(_objectCount); + f.writeInt(_walkboxCount); + + for (int i = 0; i != _objectCount; ++i) { + f.writeStringSz(_objects[i].name, 20); + f.writeBoundingBox(_objects[i].bbox); + f.writeBool(_objects[i].isObstacle); + f.writeBool(_objects[i].isClickable); + f.writeBool(_objects[i].isHotMouse); + f.writeInt(_objects[i].unknown1); + f.writeBool(_objects[i].isTarget); + } + + for (int i = 0; i != _walkboxCount; ++i) { + f.writeStringSz(_walkboxes[i].name, 20); + f.writeFloat(_walkboxes[i].altitude); + f.writeInt(_walkboxes[i].vertexCount); + for (int j = 0; j != 8; ++j) { + f.writeVector3(_walkboxes[i].vertices[j]); + + // In BLADE.EXE vertices are a vec5 + f.writeInt(0); + f.writeInt(0); + } + } + + for (int i = 0; i != 85; ++i) { + f.writeInt(_walkboxStepSound[i]); + } + + f.writeInt(_footstepSoundOverride); +} + +void Set::load(SaveFileReadStream &f) { + _loaded = f.readBool(); + _objectCount = f.readInt(); + _walkboxCount = f.readInt(); + + for (int i = 0; i != _objectCount; ++i) { + _objects[i].name = f.readStringSz(20); + _objects[i].bbox = f.readBoundingBox(); + _objects[i].isObstacle = f.readBool(); + _objects[i].isClickable = f.readBool(); + _objects[i].isHotMouse = f.readBool(); + _objects[i].unknown1 = f.readInt(); + _objects[i].isTarget = f.readBool(); + } + + for (int i = 0; i != _walkboxCount; ++i) { + _walkboxes[i].name = f.readStringSz(20); + _walkboxes[i].altitude = f.readFloat(); + _walkboxes[i].vertexCount = f.readInt(); + for (int j = 0; j != 8; ++j) { + _walkboxes[i].vertices[j] = f.readVector3(); + + // In BLADE.EXE vertices are a vec5 + f.skip(8); + } + } + + for (int i = 0; i != 85; ++i) { + _walkboxStepSound[i] = f.readInt(); + } + + _footstepSoundOverride = f.readInt(); +} + } // End of namespace BladeRunner diff --git a/engines/bladerunner/set.h b/engines/bladerunner/set.h index a6e7e9f3df..c6c1081196 100644 --- a/engines/bladerunner/set.h +++ b/engines/bladerunner/set.h @@ -32,28 +32,30 @@ namespace BladeRunner { class BladeRunnerEngine; -class VQADecoder; +class SaveFileReadStream; +class SaveFileWriteStream; class SetEffects; class SceneObjects; +class VQADecoder; class Set { friend class Debugger; struct Object { - char name[20]; - BoundingBox bbox; - uint8 isObstacle; - uint8 isClickable; - uint8 isHotMouse; - uint8 isTarget; - uint8 unknown1; + Common::String name; + BoundingBox bbox; + uint8 isObstacle; + uint8 isClickable; + uint8 isHotMouse; + uint8 isTarget; + uint8 unknown1; }; struct Walkbox { - char name[20]; - float altitude; - int vertexCount; - Vector3 vertices[8]; + Common::String name; + float altitude; + int vertexCount; + Vector3 vertices[8]; }; BladeRunnerEngine *_vm; @@ -82,23 +84,27 @@ public: float getAltitudeAtXZ(float x, float z, bool *inWalkbox) const; int findWalkbox(float x, float z) const; - int findObject(const char *objectName) const; + int findObject(const Common::String &objectName) const; bool objectSetHotMouse(int objectId) const; bool objectGetBoundingBox(int objectId, BoundingBox *boundingBox) const; void objectSetIsClickable(int objectId, bool isClickable); void objectSetIsObstacle(int objectId, bool isObstacle); void objectSetIsTarget(int objectId, bool isTarget); - const char *objectGetName(int objectId) const; + const Common::String &objectGetName(int objectId) const; void setWalkboxStepSound(int walkboxId, int soundId); void setFoodstepSoundOverride(int soundId); void resetFoodstepSoundOverride(); + int getWalkboxSoundWalkLeft(int walkboxId) const; int getWalkboxSoundWalkRight(int walkboxId) const; int getWalkboxSoundRunLeft(int walkboxId) const; int getWalkboxSoundRunRight(int walkboxId) const; + void save(SaveFileWriteStream &f); + void load(SaveFileReadStream &f); + private: static bool isXZInWalkbox(float x, float z, const Walkbox &walkbox); }; diff --git a/engines/bladerunner/set_effects.cpp b/engines/bladerunner/set_effects.cpp index 5c7b2487a8..3d46e41e7b 100644 --- a/engines/bladerunner/set_effects.cpp +++ b/engines/bladerunner/set_effects.cpp @@ -104,7 +104,7 @@ void SetEffects::setFadeDensity(float density) { _fadeDensity = density; } -void SetEffects::setFogColor(const char *fogName, float r, float g, float b) { +void SetEffects::setFogColor(const Common::String &fogName, float r, float g, float b) { Fog *fog = findFog(fogName); if (fog == nullptr) { return; @@ -115,7 +115,7 @@ void SetEffects::setFogColor(const char *fogName, float r, float g, float b) { fog->_fogColor.b = b; } -void SetEffects::setFogDensity(const char *fogName, float density) { +void SetEffects::setFogDensity(const Common::String &fogName, float density) { Fog *fog = findFog(fogName); if (fog == nullptr) { return; @@ -151,7 +151,7 @@ void SetEffects::calculateColor(Vector3 viewPosition, Vector3 position, float *o outColor->b = outColor->b * (1.0f - _fadeDensity) + _fadeColor.b * _fadeDensity; } -Fog *SetEffects::findFog(const char *fogName) const { +Fog *SetEffects::findFog(const Common::String &fogName) const { if (!_fogs) { return nullptr; } @@ -159,7 +159,7 @@ Fog *SetEffects::findFog(const char *fogName) const { Fog *fog = _fogs; while (fog != nullptr) { - if (strcmp(fogName, fog->_name) == 0) { + if (fogName.compareTo(fog->_name) == 0) { break; } fog = fog->_next; diff --git a/engines/bladerunner/set_effects.h b/engines/bladerunner/set_effects.h index 81d7b93149..6bd139c4d6 100644 --- a/engines/bladerunner/set_effects.h +++ b/engines/bladerunner/set_effects.h @@ -53,13 +53,13 @@ public: void setFadeColor(float r, float g, float b); void setFadeDensity(float density); - void setFogColor(const char *fogName, float r, float g, float b); - void setFogDensity(const char *fogName, float density); + void setFogColor(const Common::String &fogName, float r, float g, float b); + void setFogDensity(const Common::String &fogName, float density); void calculateColor(Vector3 viewPosition, Vector3 position, float *outCoeficient, Color *outColor) const; private: - Fog *findFog(const char *fogName) const; + Fog *findFog(const Common::String &fogName) const; }; } // End of namespace BladeRunner diff --git a/engines/bladerunner/settings.cpp b/engines/bladerunner/settings.cpp index a5540fcb04..071adf6dd5 100644 --- a/engines/bladerunner/settings.cpp +++ b/engines/bladerunner/settings.cpp @@ -22,10 +22,14 @@ #include "bladerunner/settings.h" +#include "bladerunner/actor.h" #include "bladerunner/ambient_sounds.h" #include "bladerunner/bladerunner.h" #include "bladerunner/chapters.h" +#include "bladerunner/game_constants.h" +#include "bladerunner/game_info.h" #include "bladerunner/music.h" +#include "bladerunner/savefile.h" #include "bladerunner/scene.h" #include "common/debug.h" @@ -39,14 +43,23 @@ Settings::Settings(BladeRunnerEngine *vm) { _playerAgenda = 1; _chapter = 1; + _scene = -1; + _set = -1; + _unk0 = 0; _gamma = 1.0f; + _ammoType = 0; + _ammoAmounts[0] = 1; + _ammoAmounts[1] = 0; + _ammoAmounts[2] = 0; + + // The remaining fields are not reset in BLADE.EXE _chapterChanged = false; _newChapter = -1; _newScene = -1; _newSet = -1; - _startingGame = true; + _startingGame = false; _loadingGame = false; _fullHDFrames = true; @@ -83,8 +96,9 @@ bool Settings::openNewScene() { _vm->_scene->close(!_loadingGame && !_startingGame); } if (_chapterChanged) { - if (_vm->_chapters->hasOpenResources()) + if (_vm->_chapters->hasOpenResources()) { _vm->_chapters->closeResources(); + } int newChapter = _newChapter; _chapterChanged = false; @@ -103,8 +117,23 @@ bool Settings::openNewScene() { return false; } + _set = newSet; + _scene = newScene; + if (!_loadingGame && currentSet != newSet) { - // TODO: Reset actors for new set + for (int i = 0; i < (int)_vm->_gameInfo->getActorCount(); ++i) { + Actor *actor = _vm->_actors[i]; + if (i != kActorMcCoy && actor->getSetId() == currentSet) { + if (!actor->isRetired()) { + actor->stopWalking(false); + actor->movementTrackWaypointReached(); + } + if (actor->inCombat()) { + actor->setSetId(kSetFreeSlotG); + actor->combatModeOff(); + } + } + } } _loadingGame = false; @@ -171,4 +200,30 @@ void Settings::setLearyMode(bool learyMode) { _learyMode = learyMode; } +void Settings::save(SaveFileWriteStream &f) { + f.writeInt(_scene); + f.writeInt(_set); + f.writeInt(_chapter); + f.writeInt(_playerAgenda); + f.writeInt(_unk0); + f.writeInt(_difficulty); + f.writeInt(_ammoType); + for (int i = 0; i != 3; ++i) { + f.writeInt(_ammoAmounts[i]); + } +} + +void Settings::load(SaveFileReadStream &f) { + _scene = f.readInt(); + _set = f.readInt(); + _chapter = f.readInt(); + _playerAgenda = f.readInt(); + _unk0 = f.readInt(); + _difficulty = f.readInt(); + _ammoType = f.readInt(); + for (int i = 0; i != 3; ++i) { + _ammoAmounts[i] = f.readInt(); + } +} + } // End of namespace BladeRunner diff --git a/engines/bladerunner/settings.h b/engines/bladerunner/settings.h index bf2d2d3162..413786e2e5 100644 --- a/engines/bladerunner/settings.h +++ b/engines/bladerunner/settings.h @@ -26,6 +26,8 @@ namespace BladeRunner { class BladeRunnerEngine; +class SaveFileReadStream; +class SaveFileWriteStream; enum PlayerAgenda { kPlayerAgendaPolite = 0, @@ -39,6 +41,9 @@ class Settings { BladeRunnerEngine *_vm; int _chapter; + int _scene; + int _set; + int _unk0; float _gamma; bool _chapterChanged; @@ -49,6 +54,8 @@ class Settings { bool _startingGame; bool _loadingGame; + // int _unk1; + int _fullHDFrames; int _mst3k; @@ -85,6 +92,18 @@ public: return _newSet; } + int getScene() const { + return _scene; + } + + int getSet() const { + return _set; + } + + int getChapter() const { + return _chapter; + } + void setChapter(int newChapter) { _chapterChanged = true; _newChapter = newChapter; @@ -117,6 +136,9 @@ public: bool getLearyMode() const; void setLearyMode(bool learyMode); + + void save(SaveFileWriteStream &f); + void load(SaveFileReadStream &f); }; } // End of namespace BladeRunner diff --git a/engines/bladerunner/slice_animations.cpp b/engines/bladerunner/slice_animations.cpp index 181b11d9f1..8678c1726b 100644 --- a/engines/bladerunner/slice_animations.cpp +++ b/engines/bladerunner/slice_animations.cpp @@ -93,8 +93,28 @@ bool SliceAnimations::openCoreAnim() { return _coreAnimPageFile.open("COREANIM.DAT"); } -bool SliceAnimations::openHDFrames() { - return _framesPageFile.open("HDFRAMES.DAT"); +bool SliceAnimations::openFrames(int fileNumber) { + if (_framesPageFile._fileNumber == -1) { // Running for the first time, need to probe + // First, try HDFRAMES.DAT + if (_framesPageFile.open("HDFRAMES.DAT")) { + _framesPageFile._fileNumber = 0; + + return true; + } + } + + if (_framesPageFile._fileNumber == 0) // HDFRAMES.DAT + return true; + + if (_framesPageFile._fileNumber == fileNumber) + return true; + + _framesPageFile.close(); + + if (fileNumber == 1 && _framesPageFile.open("CDFRAMES.DAT")) // For Chapter1 we try both CDFRAMES.DAT and CDFRAMES1.DAT + return true; + + return _framesPageFile.open(Common::String::format("CDFRAMES%d.DAT", fileNumber)); } bool SliceAnimations::PageFile::open(const Common::String &name) { @@ -119,11 +139,17 @@ bool SliceAnimations::PageFile::open(const Common::String &name) { _pageOffsets[pageNumber] = dataOffset + i * _sliceAnimations->_pageSize; } - // debug("PageFile::Open: page file \"%s\" opened with %d pages", name.c_str(), pageCount); + debug(5, "PageFile::Open: page file \"%s\" opened with %d pages", name.c_str(), pageCount); return true; } +void SliceAnimations::PageFile::close() { + if (_file.isOpen()) { + _file.close(); + } +} + void *SliceAnimations::PageFile::loadPage(uint32 pageNumber) { if (_pageOffsets[pageNumber] == -1) return nullptr; diff --git a/engines/bladerunner/slice_animations.h b/engines/bladerunner/slice_animations.h index edc0684140..0732e596ea 100644 --- a/engines/bladerunner/slice_animations.h +++ b/engines/bladerunner/slice_animations.h @@ -64,13 +64,15 @@ class SliceAnimations { }; struct PageFile { + int _fileNumber; SliceAnimations *_sliceAnimations; Common::File _file; Common::Array<int32> _pageOffsets; - PageFile(SliceAnimations *sliceAnimations) : _sliceAnimations(sliceAnimations) {} + PageFile(SliceAnimations *sliceAnimations) : _sliceAnimations(sliceAnimations), _fileNumber(-1) {} bool open(const Common::String &name); + void close(); void *loadPage(uint32 page); }; @@ -102,7 +104,7 @@ public: bool open(const Common::String &name); bool openCoreAnim(); - bool openHDFrames(); + bool openFrames(int fileNumber); Palette &getPalette(int i) { return _palettes[i]; }; void *getFramePtr(uint32 animation, uint32 frame); diff --git a/engines/bladerunner/slice_renderer.cpp b/engines/bladerunner/slice_renderer.cpp index 15633f6581..31da697c20 100644 --- a/engines/bladerunner/slice_renderer.cpp +++ b/engines/bladerunner/slice_renderer.cpp @@ -122,7 +122,7 @@ Matrix3x2 SliceRenderer::calculateFacingRotationMatrix() { float s = sinf(dir); float c = cosf(dir); - Matrix3x2 mRotation( c, -s, 0.0f, + Matrix3x2 mRotation(c, -s, 0.0f, s, c, 0.0f); Matrix3x2 mView(_view->_sliceViewMatrix(0,0), _view->_sliceViewMatrix(0,1), 0.0f, @@ -164,29 +164,20 @@ void SliceRenderer::calculateBoundingRect() { Matrix3x2 mScale(_frameScale.x, 0.0f, 0.0f, 0.0f, _frameScale.y, 0.0f); - _modelMatrix = mProjection * (facingRotation * (mOffset * mScale)); + _mvpMatrix = mProjection * (facingRotation * (mOffset * mScale)); Vector4 startScreenVector( - _view->_viewportPosition.x + top.x / top.z * _view->_viewportPosition.z, - _view->_viewportPosition.y + top.y / top.z * _view->_viewportPosition.z, + _view->_viewportPosition.x + (top.x / top.z) * _view->_viewportPosition.z, + _view->_viewportPosition.y + (top.y / top.z) * _view->_viewportPosition.z, 1.0f / top.z, _frameSliceCount * (1.0f / top.z)); Vector4 endScreenVector( - _view->_viewportPosition.x + bottom.x / bottom.z * _view->_viewportPosition.z, - _view->_viewportPosition.y + bottom.y / bottom.z * _view->_viewportPosition.z, + _view->_viewportPosition.x + (bottom.x / bottom.z) * _view->_viewportPosition.z, + _view->_viewportPosition.y + (bottom.y / bottom.z) * _view->_viewportPosition.z, 1.0f / bottom.z, 0.0f); - _startScreenVector.x = startScreenVector.x; - _startScreenVector.y = startScreenVector.y; - _startScreenVector.z = startScreenVector.z; - _endScreenVector.x = endScreenVector.x; - _endScreenVector.y = endScreenVector.y; - _endScreenVector.z = endScreenVector.z; - _startSlice = startScreenVector.w; - _endSlice = endScreenVector.w; - Vector4 delta = endScreenVector - startScreenVector; if (delta.y == 0.0f) { @@ -197,60 +188,68 @@ void SliceRenderer::calculateBoundingRect() { * Calculate min and max Y */ - float screenMinY = 0.0f; - float screenMaxY = 479.0f; + float screenTop = 0.0f; + float screenBottom = 479.0f; - if (startScreenVector.y < screenMinY) { - if (endScreenVector.y < screenMinY) + if (startScreenVector.y < screenTop) { + if (endScreenVector.y < screenTop) { return; - - float f = (screenMinY - startScreenVector.y) / delta.y; + } + float f = (screenTop - startScreenVector.y) / delta.y; startScreenVector = startScreenVector + f * delta; - } else if (startScreenVector.y > screenMaxY) { - if (endScreenVector.y >= screenMaxY) + } else if (startScreenVector.y > screenBottom) { + if (endScreenVector.y >= screenBottom) { return; - - float f = (screenMaxY - startScreenVector.y) / delta.y; + } + float f = (screenBottom - startScreenVector.y) / delta.y; startScreenVector = startScreenVector + f * delta; } - if (endScreenVector.y < screenMinY) { - float f = (screenMinY - endScreenVector.y) / delta.y; + if (endScreenVector.y < screenTop) { + float f = (screenTop - endScreenVector.y) / delta.y; endScreenVector = endScreenVector + f * delta; - } else if (endScreenVector.y > screenMaxY) { - float f = (screenMaxY - endScreenVector.y) / delta.y; + } else if (endScreenVector.y > screenBottom) { + float f = (screenBottom - endScreenVector.y) / delta.y; endScreenVector = endScreenVector + f * delta; } - int bbox_min_y = (int)MIN(startScreenVector.y, endScreenVector.y); - int bbox_max_y = (int)MAX(startScreenVector.y, endScreenVector.y) + 1; + _screenRectangle.top = (int)MIN(startScreenVector.y, endScreenVector.y); + _screenRectangle.bottom = (int)MAX(startScreenVector.y, endScreenVector.y) + 1; /* * Calculate min and max X */ - Matrix3x2 mB6 = _modelMatrix + Vector2(startScreenVector.x, 25.5f / startScreenVector.z); - Matrix3x2 mC2 = _modelMatrix + Vector2(endScreenVector.x, 25.5f / endScreenVector.z); + Matrix3x2 mStart( + 1.0f, 0.0f, startScreenVector.x, + 0.0f, 1.0f, 25.5f / startScreenVector.z + ); - float min_x = 640.0f; - float max_x = 0.0f; + Matrix3x2 mEnd( + 1.0f, 0.0f, endScreenVector.x, + 0.0f, 1.0f, 25.5f / endScreenVector.z + ); - for (float i = 0.0f; i <= 256.0f; i += 255.0f) { - for (float j = 0.0f; j <= 256.0f; j += 255.0f) { - Vector2 v1 = mB6 * Vector2(i, j); + Matrix3x2 mStartMVP = mStart * _mvpMatrix; + Matrix3x2 mEndMVP = mEnd * _mvpMatrix; - min_x = MIN(min_x, v1.x); - max_x = MAX(max_x, v1.x); + float minX = 640.0f; + float maxX = 0.0f; - Vector2 v2 = mC2 * Vector2(i, j); + for (float i = 0.0f; i <= 256.0f; i += 255.0f) { + for (float j = 0.0f; j <= 256.0f; j += 255.0f) { + Vector2 v1 = mStartMVP * Vector2(i, j); + minX = MIN(minX, v1.x); + maxX = MAX(maxX, v1.x); - min_x = MIN(min_x, v2.x); - max_x = MAX(max_x, v2.x); + Vector2 v2 = mEndMVP * Vector2(i, j); + minX = MIN(minX, v2.x); + maxX = MAX(maxX, v2.x); } } - int bbox_min_x = CLIP((int)min_x, 0, 640); - int bbox_max_x = CLIP((int)max_x + 1, 0, 640); + _screenRectangle.left = CLIP((int)minX, 0, 640); + _screenRectangle.right = CLIP((int)maxX + 1, 0, 640); _startScreenVector.x = startScreenVector.x; _startScreenVector.y = startScreenVector.y; @@ -260,11 +259,6 @@ void SliceRenderer::calculateBoundingRect() { _endScreenVector.z = endScreenVector.z; _startSlice = startScreenVector.w; _endSlice = endScreenVector.w; - - _screenRectangle.left = bbox_min_x; - _screenRectangle.right = bbox_max_x; - _screenRectangle.top = bbox_min_y; - _screenRectangle.bottom = bbox_max_y; } void SliceRenderer::loadFrame(int animation, int frame) { @@ -285,7 +279,8 @@ void SliceRenderer::loadFrame(int animation, int frame) { } struct SliceLineIterator { - int _sliceMatrix[2][3]; + // int _sliceMatrix[2][3]; + Matrix3x2 _sliceMatrix; int _startY; int _endY; @@ -316,8 +311,9 @@ void SliceLineIterator::setup( float size = endScreenY - startScreenY; - if (size <= 0.0f || startScreenZ <= 0.0f) + if (size <= 0.0f || startScreenZ <= 0.0f) { _currentY = _endY + 1; + } _currentZ = startScreenZ; _stepZ = (endScreenZ - startScreenZ) / size; @@ -328,7 +324,7 @@ void SliceLineIterator::setup( _currentX = startScreenX; _stepX = (endScreenX - startScreenX) / size; - _field_38 = (int)((25.5f / size) * (1.0f / endScreenZ - 1.0f / startScreenZ) * 64.0); + _field_38 = (int)((25.5f / size) * (1.0f / endScreenZ - 1.0f / startScreenZ) * 64.0f); _currentY = _startY; float offsetX = _currentX; @@ -342,9 +338,7 @@ void SliceLineIterator::setup( m = scale_matrix * (translate_matrix * m); - for (int r = 0; r != 2; ++r) - for (int c = 0; c != 3; ++c) - _sliceMatrix[r][c] = m(r, c); + _sliceMatrix = m; } float SliceLineIterator::line() { @@ -364,8 +358,8 @@ void SliceLineIterator::advance() { _currentSlice += _stepSlice; _currentX += _stepX; _currentY += 1; - _sliceMatrix[0][2] += (int)(65536.0f * _stepX); - _sliceMatrix[1][2] += _field_38; + _sliceMatrix._m[0][2] += (int)(65536.0f * _stepX); + _sliceMatrix._m[1][2] += _field_38; } static void setupLookupTable(int t[256], int inc) { @@ -393,7 +387,7 @@ void SliceRenderer::drawInWorld(int animationId, int animationFrame, Vector3 pos _endScreenVector.x, _endScreenVector.y, _endScreenVector.z, _startScreenVector.x, _startScreenVector.y, _startScreenVector.z, _endSlice, _startSlice, - _modelMatrix + _mvpMatrix ); SliceRendererLights sliceRendererLights = SliceRendererLights(_lights); @@ -424,12 +418,12 @@ void SliceRenderer::drawInWorld(int animationId, int animationFrame, Vector3 pos _setEffectColor.g = setEffectColor.g * 31.0f * 65536.0f; _setEffectColor.b = setEffectColor.b * 31.0f * 65536.0f; - setupLookupTable(_m11lookup, sliceLineIterator._sliceMatrix[0][0]); - setupLookupTable(_m12lookup, sliceLineIterator._sliceMatrix[0][1]); - _m13 = sliceLineIterator._sliceMatrix[0][2]; - setupLookupTable(_m21lookup, sliceLineIterator._sliceMatrix[1][0]); - setupLookupTable(_m22lookup, sliceLineIterator._sliceMatrix[1][1]); - _m23 = sliceLineIterator._sliceMatrix[1][2]; + setupLookupTable(_m12lookup, sliceLineIterator._sliceMatrix(0, 1)); + setupLookupTable(_m11lookup, sliceLineIterator._sliceMatrix(0, 0)); + _m13 = sliceLineIterator._sliceMatrix(0, 2); + setupLookupTable(_m21lookup, sliceLineIterator._sliceMatrix(1, 0)); + setupLookupTable(_m22lookup, sliceLineIterator._sliceMatrix(1, 1)); + _m23 = sliceLineIterator._sliceMatrix(1, 2); if (_animationsShadowEnabled[_animation]) { float coeficientShadow; @@ -545,8 +539,9 @@ void SliceRenderer::drawOnScreen(int animationId, int animationFrame, int screen } void SliceRenderer::drawSlice(int slice, bool advanced, uint16 *frameLinePtr, uint16 *zbufLinePtr, int y) { - if (slice < 0 || (uint32)slice >= _frameSliceCount) + if (slice < 0 || (uint32)slice >= _frameSliceCount) { return; + } SliceAnimations::Palette &palette = _vm->_sliceAnimations->getPalette(_framePaletteIndex); @@ -714,7 +709,12 @@ void SliceRenderer::drawShadowPolygon(int transparency, Graphics::Surface &surfa yMax = CLIP(yMax, 0, 480); yMin = CLIP(yMin, 0, 480); - int ditheringFactor[] = { 0, 8, 2, 10, 12, 4, 14, 6, 3, 11, 1, 9, 15, 7, 13, 5 }; + int ditheringFactor[] = { + 0, 8, 2, 10, + 12, 4, 14, 6, + 3, 11, 1, 9, + 15, 7, 13, 5 + }; for (int y = yMin; y < yMax; ++y) { int xMin = CLIP(polygonLeft[y], 0, 640); diff --git a/engines/bladerunner/slice_renderer.h b/engines/bladerunner/slice_renderer.h index d2de61e279..2e3617162c 100644 --- a/engines/bladerunner/slice_renderer.h +++ b/engines/bladerunner/slice_renderer.h @@ -67,7 +67,7 @@ class SliceRenderer { uint32 _framePaletteIndex; uint32 _frameSliceCount; - Matrix3x2 _modelMatrix; + Matrix3x2 _mvpMatrix; Vector3 _startScreenVector; Vector3 _endScreenVector; float _startSlice; diff --git a/engines/bladerunner/text_resource.cpp b/engines/bladerunner/text_resource.cpp index 2a4840c833..8f54f8a976 100644 --- a/engines/bladerunner/text_resource.cpp +++ b/engines/bladerunner/text_resource.cpp @@ -46,11 +46,11 @@ TextResource::~TextResource() { bool TextResource::open(const Common::String &name) { assert(name.size() <= 8); - char resName[13]; - sprintf(resName, "%s.TR%s", name.c_str(), _vm->_languageCode); + Common::String resName = Common::String::format("%s.TR%s", name.c_str(), _vm->_languageCode.c_str()); Common::ScopedPtr<Common::SeekableReadStream> s(_vm->getResourceStream(resName)); - if (!s) + if (!s) { return false; + } _count = s->readUint32LE(); diff --git a/engines/bladerunner/time.cpp b/engines/bladerunner/time.cpp new file mode 100644 index 0000000000..a395a811d1 --- /dev/null +++ b/engines/bladerunner/time.cpp @@ -0,0 +1,68 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "bladerunner/time.h" + +#include "bladerunner/bladerunner.h" + +#include "common/timer.h" + +namespace BladeRunner { + +Time::Time(BladeRunnerEngine *vm) { + _vm = vm; + + _start = _vm->getTotalPlayTime(); + _pauseCount = 0; + _offset = 0; + _pauseStart = 0; +} + +int Time::current() { + int time = _vm->getTotalPlayTime() - _offset; + return time - _start; +} + +int Time::pause() { + if (_pauseCount == 0) { + _pauseStart = current(); + } + return ++_pauseCount; +} + +int Time::getPauseStart() { + return _pauseStart; +} + +int Time::unpause() { + assert(_pauseCount > 0); + if (--_pauseCount == 0) { + _offset += current() - _pauseStart; + } + return _pauseCount; +} + +bool Time::isLocked() { + return _pauseCount > 0; +} + +} // End of namespace BladeRunner diff --git a/engines/bladerunner/time.h b/engines/bladerunner/time.h new file mode 100644 index 0000000000..bda8c84d14 --- /dev/null +++ b/engines/bladerunner/time.h @@ -0,0 +1,50 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 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 BLADERUNNER_TIME_H +#define BLADERUNNER_TIME_H + +namespace BladeRunner { + +class BladeRunnerEngine; + +class Time { + BladeRunnerEngine *_vm; + + int _start; + int _pauseCount; + int _offset; + int _pauseStart; + +public: + Time(BladeRunnerEngine *vm); + + int current(); + int pause(); + int getPauseStart(); + int unpause(); + bool isLocked(); +}; + +} // End of namespace BladeRunner + +#endif diff --git a/engines/bladerunner/ui/elevator.cpp b/engines/bladerunner/ui/elevator.cpp index 3f668efd75..7a6ab3ca35 100644 --- a/engines/bladerunner/ui/elevator.cpp +++ b/engines/bladerunner/ui/elevator.cpp @@ -315,8 +315,7 @@ void Elevator::mouseOutCallback(int, void *self) { void Elevator::mouseDownCallback(int, void *self) { Elevator *elevator = ((Elevator *)self); - const char *name = elevator->_vm->_gameInfo->getSfxTrack(515); - elevator->_vm->_audioPlayer->playAud(name, 100, 0, 0, 50, 0); + elevator->_vm->_audioPlayer->playAud(elevator->_vm->_gameInfo->getSfxTrack(515), 100, 0, 0, 50, 0); } void Elevator::mouseUpCallback(int buttonId, void *self) { diff --git a/engines/bladerunner/ui/end_credits.cpp b/engines/bladerunner/ui/end_credits.cpp new file mode 100644 index 0000000000..6d964d0290 --- /dev/null +++ b/engines/bladerunner/ui/end_credits.cpp @@ -0,0 +1,170 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/system.h" +#include "common/rect.h" + +#include "audio/mixer.h" + +#include "bladerunner/bladerunner.h" +#include "bladerunner/ambient_sounds.h" +#include "bladerunner/audio_speech.h" +#include "bladerunner/font.h" +#include "bladerunner/game_info.h" +#include "bladerunner/mouse.h" +#include "bladerunner/music.h" +#include "bladerunner/text_resource.h" +#include "bladerunner/ui/end_credits.h" + +namespace BladeRunner { + +EndCredits::EndCredits(BladeRunnerEngine *vm) { + _vm = vm; +} + +EndCredits::~EndCredits() { +} + +void EndCredits::show() { + _vm->_mouse->disable(); + _vm->_mixer->stopAll(); + _vm->_ambientSounds->removeAllNonLoopingSounds(true); + _vm->_ambientSounds->removeAllLoopingSounds(4); + _vm->_audioSpeech->stopSpeech(); + + _vm->_music->play(_vm->_gameInfo->getMusicTrack(17), 100, 0, 2, -1, 0, 3); + + Font *fontBig = new Font(_vm); + fontBig->open("TAHOMA24.FON", 640, 480, -1, 0, 0); + fontBig->setSpacing(1, 0); + + Font *fontSmall = new Font(_vm); + fontSmall->open("TAHOMA18.FON", 640, 480, -1, 0, 0); + fontSmall->setSpacing(1, 0); + + TextResource *textResource = new TextResource(_vm); + textResource->open("ENDCRED"); + + int textCount = textResource->getCount(); + int *textPositions = (int *)malloc(textCount * sizeof(int)); + int y = 452; + bool small = false; + + for (int i = 0; i < textCount; i++) { + Common::String s = textResource->getText(i); + if (s.hasPrefix("^")) { + if (!small) { + y += 28; + } + small = false; + } else { + if (small) { + y += 24; + } else { + y += 28; + } + small = true; + } + if (s.hasPrefix("^")) { + textPositions[i] = y; + } else { + textPositions[i] = y + 2; + } + } + + _vm->_vqaIsPlaying = true; + _vm->_vqaStopIsRequested = false; + + double position = 0.0; + uint32 timeLast = _vm->getTotalPlayTime(); + + while (!_vm->_vqaStopIsRequested && !_vm->shouldQuit()) { + if (position >= textPositions[textCount - 1]) { + break; + } + + //soundSystem::tick(SoundSystem); + _vm->handleEvents(); + + if (!_vm->_gameIsRunning) { + timeLast = _vm->getTotalPlayTime(); + + continue; + } + + uint32 timeNow = _vm->getTotalPlayTime(); + position += (double)(timeNow - timeLast) * 0.05f; + timeLast = timeNow; + + _vm->_surfaceFront.fillRect(Common::Rect(640, 480), 0); + + for (int i = 0; i < textCount; i++) { + Common::String s = textResource->getText(i); + Font *font; + int height; + + if (s.hasPrefix("^")) { + font = fontBig; + height = 28; + s.deleteChar(0); + } else { + font = fontSmall; + height = 24; + } + + y = textPositions[i] - (int)position; + + if (y < 452 && y + height > 28) { + int x; + + if (font == fontBig) { + x = 280; + } else { + x = 270 - font->getTextWidth(s); + } + + font->draw(s, _vm->_surfaceFront, x, y); + } + } + + _vm->_surfaceFront.fillRect(Common::Rect(0, 0, 640, 28), 0); + _vm->_surfaceFront.fillRect(Common::Rect(0, 452, 640, 480), 0); + + _vm->blitToScreen(_vm->_surfaceFront); + + _vm->_system->delayMillis(10); + } + + _vm->_vqaIsPlaying = false; + _vm->_vqaStopIsRequested = false; + + free(textPositions); + delete textResource; + + delete fontSmall; + delete fontBig; + + _vm->_music->stop(0); + _vm->_mouse->enable(); +} + +} // End of namespace BladeRunner diff --git a/engines/bladerunner/ui/end_credits.h b/engines/bladerunner/ui/end_credits.h new file mode 100644 index 0000000000..39086cd796 --- /dev/null +++ b/engines/bladerunner/ui/end_credits.h @@ -0,0 +1,42 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can 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 BLADERUNNER_UI_END_CREDITS_H +#define BLADERUNNER_UI_END_CREDITS_H + +namespace BladeRunner { + +class BladeRunnerEngine; + +class EndCredits { + BladeRunnerEngine *_vm; + +public: + EndCredits(BladeRunnerEngine *vm); + ~EndCredits(); + + void show(); +}; + +} // End of namespace BladeRunner + +#endif diff --git a/engines/bladerunner/ui/kia.cpp b/engines/bladerunner/ui/kia.cpp index 86fdc9254a..756f1bd2cd 100644 --- a/engines/bladerunner/ui/kia.cpp +++ b/engines/bladerunner/ui/kia.cpp @@ -350,7 +350,7 @@ void KIA::tick() { _shapes->get(47)->draw(_vm->_surfaceFront, 182, 446); } } - _vm->_mainFont->drawColor("1.00", _vm->_surfaceFront, 438, 471, 0x1CE7); + _vm->_mainFont->drawColor("1.00", _vm->_surfaceFront, 438, 471, 0x1CE7); // TODO: 1.01 for DVD version if (!_transitionId) { _buttons->drawTooltip(_vm->_surfaceFront, mouse.x, mouse.y); } diff --git a/engines/bladerunner/ui/kia_log.cpp b/engines/bladerunner/ui/kia_log.cpp index 51b922a8f7..7f75f2e944 100644 --- a/engines/bladerunner/ui/kia_log.cpp +++ b/engines/bladerunner/ui/kia_log.cpp @@ -55,7 +55,7 @@ void KIALog::add(int type, int dataSize, const void *data) { _entries[_currentIndex].dataSize = dataSize; if (dataSize > 0) { - char *dataCopy = new char[dataSize]; + unsigned char *dataCopy = new unsigned char[dataSize]; memcpy(dataCopy, data, dataSize); _entries[_currentIndex].data = dataCopy; } else { diff --git a/engines/bladerunner/ui/kia_log.h b/engines/bladerunner/ui/kia_log.h index 4a89492817..99c834e3f0 100644 --- a/engines/bladerunner/ui/kia_log.h +++ b/engines/bladerunner/ui/kia_log.h @@ -33,7 +33,7 @@ class KIALog { struct Entry { int type; int dataSize; - const char *data; + const unsigned char *data; }; BladeRunnerEngine *_vm; diff --git a/engines/bladerunner/ui/scores.cpp b/engines/bladerunner/ui/scores.cpp new file mode 100644 index 0000000000..c4a7df778c --- /dev/null +++ b/engines/bladerunner/ui/scores.cpp @@ -0,0 +1,218 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can 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 "bladerunner/ui/scores.h" + +#include "bladerunner/bladerunner.h" +#include "bladerunner/font.h" +#include "bladerunner/savefile.h" +#include "bladerunner/scene.h" +#include "bladerunner/text_resource.h" +#include "bladerunner/vqa_player.h" + +#include "common/keyboard.h" + +namespace BladeRunner { + +Scores::Scores(BladeRunnerEngine *vm) { + _vm = vm; + + _font = nullptr; + _txtScorers = nullptr; + + reset(); +} + +Scores::~Scores() { +} + +void Scores::open() { + if (!_vm->openArchive("MODE.MIX")) { + return; + } + + _vqaPlayer = new VQAPlayer(_vm, &_vm->_surfaceBack); + + if (!_vqaPlayer->open("SCORE.VQA")) { + return; + } + + _vqaPlayer->setLoop(1, -1, 0, nullptr, nullptr); + + // TODO: Freeze game time + + _txtScorers = new TextResource(_vm); + _txtScorers->open("SCORERS"); + + _font = new Font(_vm); + _font->open("TAHOMA24.FON", 640, 480, -1, 0, 0); + _font->setSpacing(1, 0); + + fill(); + + _isOpen = true; + _isLoaded = false; +} + +bool Scores::isOpen() const { + return _isOpen; +} + +void Scores::close() { + _isOpen = false; + + delete _font; + _font = nullptr; + + delete _txtScorers; + _txtScorers = nullptr; + + if (_vqaPlayer) { + _vqaPlayer->close(); + delete _vqaPlayer; + _vqaPlayer = nullptr; + } + + _vm->closeArchive("MODE.MIX"); + + // TODO: Unfreeze game time + _vm->_scene->resume(); +} + +void Scores::set(int index, int value) { + if (value > _scores[index]) { + _scores[index] = value; + } + + _lastScoreId = index; + _lastScoreValue = value; +} + +void Scores::handleKeyDown(const Common::KeyState &kbd) { + close(); +} + +int Scores::handleMouseUp(int x, int y) { + if (_isLoaded) { + close(); + } + + _isLoaded = false; + + return false; +} + +int Scores::handleMouseDown(int x, int y) { + _isLoaded = true; + + return false; +} + +void Scores::tick() { + if (!_vm->_gameIsRunning) { + return; + } + + int frame = _vqaPlayer->update(); + assert(frame >= -1); + + // vqaPlayer renders to _surfaceBack + blit(_vm->_surfaceBack, _vm->_surfaceFront); + + _vm->_surfaceFront.hLine(200, 139, 400, 0x3e0); + _vm->_surfaceFront.hLine(200, 347, 400, 0x1f); + + _font->draw(_txtScorers->getText(7), _vm->_surfaceFront, 200, 114); + + int y = 140; + + for (int i = 0; i < 7; i++) { + _font->draw(_txtScorers->getText(_scorers[i]), _vm->_surfaceFront, 220, y); + _font->drawNumber(_scores[_scorers[i]], _vm->_surfaceFront, 360, y); + + y += 26; + } + + _font->draw(_txtScorers->getText(8), _vm->_surfaceFront, 200, 322); + _font->draw(_txtScorers->getText(_lastScoreId), _vm->_surfaceFront, 220, 348); + _font->drawNumber(_lastScoreValue, _vm->_surfaceFront, 360, 348); + + _vm->blitToScreen(_vm->_surfaceFront); +} + +void Scores::fill() { + for (int i = 0; i < 7; i++) { + _scorers[i] = i; + } + + // Network sorting using Bose-Nelson Algorithm + const byte network[32] = { // Bose-Nelson + 1,2, 3,4, 5,6, + 0,2, 3,5, 4,6, + 0,1, 4,5, 2,6, + 0,4, 1,5, + 0,3, 2,5, + 1,3, 2,4, + 2,3 + }; + + for (int i = 0; i < 32; i += 2) { + int i1 = network[i], i2 = network[i + 1]; + if (_scores[_scorers[i1]] < _scores[_scorers[i2]]) { + SWAP(_scorers[i1], _scorers[i2]); + } + } +} + +void Scores::reset() { + _isOpen = false; + _isLoaded = false; + _vqaPlayer = nullptr; + + for (int i = 0; i < 7; i++) { + _scores[i] = -80; + _scorers[i] = 0; + } + + _lastScoreId = 0; + _lastScoreValue = 0; +} + +void Scores::save(SaveFileWriteStream &f) { + for (int i = 0; i < 7; i++) { + f.writeInt(_scores[i]); + } + + f.writeInt(_lastScoreId); + f.writeInt(_lastScoreValue); +} + +void Scores::load(SaveFileReadStream &f) { + for (int i = 0; i < 7; i++) { + _scores[i] = f.readInt(); + } + + _lastScoreId = f.readInt(); + _lastScoreValue = f.readInt(); +} + +} // End of namespace BladeRunner diff --git a/engines/bladerunner/ui/scores.h b/engines/bladerunner/ui/scores.h new file mode 100644 index 0000000000..80fd409675 --- /dev/null +++ b/engines/bladerunner/ui/scores.h @@ -0,0 +1,82 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can 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 BLADERUNNER_SCORES_H +#define BLADERUNNER_SCORES_H + +#include "common/array.h" + +namespace Common { +struct KeyState; +} + +namespace BladeRunner { + +class BladeRunnerEngine; +class Font; +class Shape; +class SaveFileReadStream; +class SaveFileWriteStream; +class TextResource; +class VQAPlayer; +class UIImagePicker; + +class Scores { + BladeRunnerEngine *_vm; + bool _isOpen; + bool _isLoaded; + VQAPlayer *_vqaPlayer; + int _scores[7]; + int _scorers[7]; + + int _lastScoreId; + int _lastScoreValue; + + Font *_font; + TextResource *_txtScorers; + +public: + Scores(BladeRunnerEngine *vm); + ~Scores(); + + void open(); + bool isOpen() const; + void close(); + + int query(int index) { return _scores[index]; } + void set(int index, int value); + + void handleKeyDown(const Common::KeyState &kbd); + int handleMouseUp(int x, int y); + int handleMouseDown(int x, int y); + + void tick(); + void fill(); + + void reset(); + void save(SaveFileWriteStream &f); + void load(SaveFileReadStream &f); +}; + +} // End of namespace BladeRunner + +#endif diff --git a/engines/bladerunner/ui/spinner.cpp b/engines/bladerunner/ui/spinner.cpp index 0740715fc4..9e491f719c 100644 --- a/engines/bladerunner/ui/spinner.cpp +++ b/engines/bladerunner/ui/spinner.cpp @@ -25,6 +25,7 @@ #include "bladerunner/bladerunner.h" #include "bladerunner/game_constants.h" #include "bladerunner/mouse.h" +#include "bladerunner/savefile.h" #include "bladerunner/scene.h" #include "bladerunner/shape.h" #include "bladerunner/text_resource.h" @@ -262,6 +263,20 @@ void Spinner::resume() { _vqaPlayer->setLoop(1, -1, kLoopSetModeJustStart, nullptr, nullptr); } +void Spinner::save(SaveFileWriteStream &f) { + assert(!_isOpen); + + for (int i = 0; i != kSpinnerDestinations; ++i) { + f.writeBool(_isDestinationSelectable[i]); + } +} + +void Spinner::load(SaveFileReadStream &f) { + for (int i = 0; i != kSpinnerDestinations; ++i) { + _isDestinationSelectable[i] = f.readBool(); + } +} + const Spinner::Destination *Spinner::getDestinationsFar() { static const Destination destinations[] = { { 0, Common::Rect(220, 227, 246, 262) }, diff --git a/engines/bladerunner/ui/spinner.h b/engines/bladerunner/ui/spinner.h index b1785a57eb..d2d666e8e1 100644 --- a/engines/bladerunner/ui/spinner.h +++ b/engines/bladerunner/ui/spinner.h @@ -29,9 +29,11 @@ namespace BladeRunner { class BladeRunnerEngine; +class SaveFileReadStream; +class SaveFileWriteStream; class Shape; -class VQAPlayer; class UIImagePicker; +class VQAPlayer; class Spinner { static const int kSpinnerDestinations = 10; @@ -70,6 +72,9 @@ public: void reset(); void resume(); + void save(SaveFileWriteStream &f); + void load(SaveFileReadStream &f); + private: static void mouseUpCallback(int, void *); static const Destination *getDestinationsFar(); diff --git a/engines/bladerunner/ui/ui_scroll_box.cpp b/engines/bladerunner/ui/ui_scroll_box.cpp index 6bd4dabdd4..a030e534c5 100644 --- a/engines/bladerunner/ui/ui_scroll_box.cpp +++ b/engines/bladerunner/ui/ui_scroll_box.cpp @@ -481,7 +481,7 @@ void UIScrollBox::draw(Graphics::Surface &surface) { } if (_center) { - x = (_rect.width() - _vm->_mainFont->getTextWidth(_lines[i]->text)) / 2; + x = _rect.left + (_rect.width() - _vm->_mainFont->getTextWidth(_lines[i]->text)) / 2; } _vm->_mainFont->drawColor(_lines[i]->text, surface, x, y, color); @@ -490,8 +490,7 @@ void UIScrollBox::draw(Graphics::Surface &surface) { y2 += kLineHeight; y += kLineHeight; ++i; - } - while (i < lastLineVisible); + } while (i < lastLineVisible); } // draw scroll up button diff --git a/engines/bladerunner/vector.h b/engines/bladerunner/vector.h index f3cc1f1e6d..22ef5cd839 100644 --- a/engines/bladerunner/vector.h +++ b/engines/bladerunner/vector.h @@ -37,6 +37,14 @@ public: Vector2(float ax, float ay) : x(ax), y(ay) {} }; +inline bool operator==(const Vector2 &a, const Vector2 &b) { + return a.x == b.x && a.y == b.y; +} + +inline bool operator!=(const Vector2 &a, const Vector2 &b) { + return !(a == b); +} + class Vector3 { public: float x; @@ -132,6 +140,10 @@ inline float distance(float x1, float z1, float x2, float z2) { return int_part + frac_part; } +inline float distance(const Vector2 &v1, const Vector2 &v2) { + return distance(v1.x, v1.y, v2.x, v2.y); +} + inline float distance(const Vector3 &v1, const Vector3 &v2) { return distance(v1.x, v1.z, v2.x, v2.z); } diff --git a/engines/bladerunner/view.cpp b/engines/bladerunner/view.cpp index d304b92bb1..72c070ecc0 100644 --- a/engines/bladerunner/view.cpp +++ b/engines/bladerunner/view.cpp @@ -31,8 +31,9 @@ bool View::readVqa(Common::ReadStream *stream) { _frame = stream->readUint32LE(); float d[12]; - for (int i = 0; i != 12; ++i) + for (int i = 0; i != 12; ++i) { d[i] = stream->readFloatLE(); + } _frameViewMatrix = Matrix4x3(d); @@ -54,17 +55,11 @@ void View::setFovX(float fovX) { } void View::calculateSliceViewMatrix() { - Matrix4x3 m = _frameViewMatrix; - - m = m * rotationMatrixX(float(M_PI) / 2.0f); - - Matrix4x3 a(-1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, -1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f); - - m = a * m; - - _sliceViewMatrix = m; + Matrix4x3 mRotation = rotationMatrixX(float(M_PI) / 2.0f); + Matrix4x3 mInvert(-1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, -1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f); + _sliceViewMatrix = mInvert * (_frameViewMatrix * mRotation); } void View::calculateCameraPosition() { diff --git a/engines/bladerunner/waypoints.cpp b/engines/bladerunner/waypoints.cpp index 4d80841e50..bf2f09b8a6 100644 --- a/engines/bladerunner/waypoints.cpp +++ b/engines/bladerunner/waypoints.cpp @@ -22,6 +22,8 @@ #include "bladerunner/waypoints.h" +#include "bladerunner/savefile.h" + namespace BladeRunner { Waypoints::Waypoints(BladeRunnerEngine *vm, int count) { @@ -89,4 +91,24 @@ float Waypoints::getZ(int waypointId) const { return _waypoints[waypointId].position.z; } +void Waypoints::save(SaveFileWriteStream &f) { + f.writeInt(_count); + for (int i = 0; i < _count; ++i) { + Waypoint &w = _waypoints[i]; + f.writeInt(w.setId); + f.writeVector3(w.position); + f.writeInt(w.present); + } +} + +void Waypoints::load(SaveFileReadStream &f) { + _count = f.readInt(); + for (int i = 0; i < _count; ++i) { + Waypoint &w = _waypoints[i]; + w.setId = f.readInt(); + w.position = f.readVector3(); + w.present = f.readInt(); + } +} + } // End of namespace BladeRunner diff --git a/engines/bladerunner/waypoints.h b/engines/bladerunner/waypoints.h index 826f8f5a94..6bbe8d3db4 100644 --- a/engines/bladerunner/waypoints.h +++ b/engines/bladerunner/waypoints.h @@ -30,6 +30,9 @@ namespace BladeRunner { +class SaveFileReadStream; +class SaveFileWriteStream; + class Waypoints { friend class Debugger; @@ -55,6 +58,9 @@ public: bool set(int waypointId, int setId, Vector3 position); bool reset(int waypointId); + + void save(SaveFileWriteStream &f); + void load(SaveFileReadStream &f); }; } // End of namespace BladeRunner diff --git a/engines/cge/cge.h b/engines/cge/cge.h index d3f8a93c1d..9a8ea0dde9 100644 --- a/engines/cge/cge.h +++ b/engines/cge/cge.h @@ -56,7 +56,7 @@ class Walk; class Text; class Talk; -#define kSavegameVersion 2 +#define kSavegameVersion 3 #define kSavegameStrSize 11 #define kPocketX 174 #define kPocketY 176 @@ -99,8 +99,9 @@ struct SavegameHeader { uint8 version; Common::String saveName; Graphics::Surface *thumbnail; - int saveYear, saveMonth, saveDay; - int saveHour, saveMinutes; + int16 saveYear, saveMonth, saveDay; + int16 saveHour, saveMinutes; + uint32 playTime; }; extern const char *savegameStr; @@ -246,7 +247,7 @@ public: void mainLoop(); void handleFrame(); void saveGame(int slotNumber, const Common::String &desc); - static bool readSavegameHeader(Common::InSaveFile *in, SavegameHeader &header); + WARN_UNUSED_RESULT static bool readSavegameHeader(Common::InSaveFile *in, SavegameHeader &header, bool skipThumbnail = true); void switchMusic(); void selectPocket(int n); void expandSprite(Sprite *spr); diff --git a/engines/cge/cge_main.cpp b/engines/cge/cge_main.cpp index b60f201cb0..05461e30e1 100644 --- a/engines/cge/cge_main.cpp +++ b/engines/cge/cge_main.cpp @@ -243,9 +243,7 @@ bool CGEEngine::loadGame(int slotNumber, SavegameHeader *header, bool tiny) { return true; } - // Delete the thumbnail - saveHeader.thumbnail->free(); - delete saveHeader.thumbnail; + g_engine->setTotalPlayTime(saveHeader.playTime * 1000); } // Get in the savegame @@ -371,6 +369,8 @@ void CGEEngine::writeSavegameHeader(Common::OutSaveFile *out, SavegameHeader &he out->writeSint16LE(td.tm_mday); out->writeSint16LE(td.tm_hour); out->writeSint16LE(td.tm_min); + + out->writeUint32LE(g_engine->getTotalPlayTime() / 1000); } void CGEEngine::syncGame(Common::SeekableReadStream *readStream, Common::WriteStream *writeStream, bool tiny) { @@ -424,8 +424,16 @@ void CGEEngine::syncGame(Common::SeekableReadStream *readStream, Common::WriteSt } } -bool CGEEngine::readSavegameHeader(Common::InSaveFile *in, SavegameHeader &header) { - header.thumbnail = nullptr; +WARN_UNUSED_RESULT bool CGEEngine::readSavegameHeader(Common::InSaveFile *in, SavegameHeader &header, bool skipThumbnail) { + header.version = 0; + header.saveName.clear(); + header.thumbnail = nullptr; + header.saveYear = 0; + header.saveMonth = 0; + header.saveDay = 0; + header.saveHour = 0; + header.saveMinutes = 0; + header.playTime = 0; // Get the savegame version header.version = in->readByte(); @@ -433,23 +441,26 @@ bool CGEEngine::readSavegameHeader(Common::InSaveFile *in, SavegameHeader &heade return false; // Read in the string - header.saveName.clear(); char ch; while ((ch = (char)in->readByte()) != '\0') header.saveName += ch; // Get the thumbnail - header.thumbnail = Graphics::loadThumbnail(*in); - if (!header.thumbnail) + if (!Graphics::loadThumbnail(*in, header.thumbnail, skipThumbnail)) { return false; + } // Read in save date/time - header.saveYear = in->readSint16LE(); - header.saveMonth = in->readSint16LE(); - header.saveDay = in->readSint16LE(); - header.saveHour = in->readSint16LE(); + header.saveYear = in->readSint16LE(); + header.saveMonth = in->readSint16LE(); + header.saveDay = in->readSint16LE(); + header.saveHour = in->readSint16LE(); header.saveMinutes = in->readSint16LE(); + if (header.version >= 3) { + header.playTime = in->readUint32LE(); + } + return true; } diff --git a/engines/cge/detection.cpp b/engines/cge/detection.cpp index 482591bf50..f6399d484c 100644 --- a/engines/cge/detection.cpp +++ b/engines/cge/detection.cpp @@ -126,7 +126,7 @@ public: return "Soltys (C) 1994-1996 L.K. Avalon"; } - virtual const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const; + ADDetectedGame fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const override; virtual bool hasFeature(MetaEngineFeature f) const; virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const; virtual int getMaximumSaveSlot() const; @@ -135,13 +135,8 @@ public: virtual void removeSaveState(const char *target, int slot) const; }; -static const ADFileBasedFallback fileBasedFallback[] = { - { &gameDescriptions[0], { "vol.cat", "vol.dat", 0 } }, - { 0, { 0 } } -}; - static ADGameDescription s_fallbackDesc = { - "Soltys", + "soltys", "Unknown version", AD_ENTRY1(0, 0), // This should always be AD_ENTRY1(0, 0) in the fallback descriptor Common::UNK_LANG, @@ -150,38 +145,40 @@ static ADGameDescription s_fallbackDesc = { GUIO1(GAMEOPTION_COLOR_BLIND_DEFAULT_OFF) }; -const ADGameDescription *CGEMetaEngine::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const { - ADFilePropertiesMap filesProps; +static const ADFileBasedFallback fileBasedFallback[] = { + { &s_fallbackDesc, { "vol.cat", "vol.dat", 0 } }, + { 0, { 0 } } +}; - const ADGameDescription *game; - game = detectGameFilebased(allFiles, fslist, CGE::fileBasedFallback, &filesProps); +ADDetectedGame CGEMetaEngine::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const { + ADDetectedGame game = detectGameFilebased(allFiles, fslist, CGE::fileBasedFallback); - if (!game) - return nullptr; + if (!game.desc) + return ADDetectedGame(); SearchMan.addDirectory("CGEMetaEngine::fallbackDetect", fslist.begin()->getParent()); ResourceManager *resman; resman = new ResourceManager(); - bool result = resman->exist("CGE.SAY"); + bool sayFileFound = resman->exist("CGE.SAY"); delete resman; SearchMan.remove("CGEMetaEngine::fallbackDetect"); - if (!result) - return nullptr; + if (!sayFileFound) + return ADDetectedGame(); - reportUnknown(fslist.begin()->getParent(), filesProps); - return &s_fallbackDesc; + return game; } bool CGEMetaEngine::hasFeature(MetaEngineFeature f) const { return - (f == kSupportsListSaves) || - (f == kSupportsLoadingDuringStartup) || - (f == kSupportsDeleteSave) || - (f == kSavesSupportMetaInfo) || - (f == kSavesSupportThumbnail) || - (f == kSavesSupportCreationDate) || + (f == kSupportsListSaves) || + (f == kSupportsLoadingDuringStartup) || + (f == kSupportsDeleteSave) || + (f == kSavesSupportMetaInfo) || + (f == kSavesSupportThumbnail) || + (f == kSavesSupportCreationDate) || + (f == kSavesSupportPlayTime) || (f == kSimpleSavesNames); } @@ -221,10 +218,6 @@ SaveStateList CGEMetaEngine::listSaves(const char *target) const { // Valid savegame if (CGE::CGEEngine::readSavegameHeader(file, header)) { saveList.push_back(SaveStateDescriptor(slotNum, header.saveName)); - if (header.thumbnail) { - header.thumbnail->free(); - delete header.thumbnail; - } } } else { // Must be an original format savegame @@ -253,7 +246,7 @@ SaveStateDescriptor CGEMetaEngine::querySaveMetaInfos(const char *target, int sl f->read(buffer, kSavegameStrSize + 1); bool hasHeader = !strncmp(buffer, CGE::savegameStr, kSavegameStrSize + 1) && - CGE::CGEEngine::readSavegameHeader(f, header); + CGE::CGEEngine::readSavegameHeader(f, header, false); delete f; if (!hasHeader) { @@ -267,6 +260,10 @@ SaveStateDescriptor CGEMetaEngine::querySaveMetaInfos(const char *target, int sl desc.setSaveDate(header.saveYear, header.saveMonth, header.saveDay); desc.setSaveTime(header.saveHour, header.saveMinutes); + if (header.playTime) { + desc.setPlayTime(header.playTime * 1000); + } + // Slot 0 is used for the 'automatic save on exit' save in Soltys, thus // we prevent it from being deleted or overwritten by accident. desc.setDeletableFlag(slot != 0); diff --git a/engines/cge/vga13h.cpp b/engines/cge/vga13h.cpp index 4d3a103663..a7e065fe01 100644 --- a/engines/cge/vga13h.cpp +++ b/engines/cge/vga13h.cpp @@ -707,7 +707,7 @@ uint8 Vga::closest(Dac *pal, const uint8 colR, const uint8 colG, const uint8 col uint16 D = ((r > R) ? (r - R) : (R - r)) + ((g > G) ? (g - G) : (G - g)) + ((b > B) ? (b - B) : (B - b)) + - ((l > L) ? (l - L) : (L - l)) * 10 ; + ((l > L) ? (l - L) : (L - l)) * 10; if (D < dif) { found = i; diff --git a/engines/cge2/cge2.h b/engines/cge2/cge2.h index 18f919b5eb..1fa23ad6f7 100644 --- a/engines/cge2/cge2.h +++ b/engines/cge2/cge2.h @@ -32,12 +32,13 @@ #include "common/savefile.h" #include "common/serializer.h" #include "engines/engine.h" -#include "engines/advancedDetector.h" #include "common/system.h" #include "cge2/fileio.h" #include "cge2/console.h" #include "audio/mixer.h" +struct ADGameDescription; + namespace CGE2 { class Vga; @@ -105,7 +106,7 @@ struct SavegameHeader; #define kQuitText 201 #define kNoQuitText 202 -#define kSavegameVersion 1 +#define kSavegameVersion 2 #define kSavegameStrSize 12 #define kSavegameStr "SCUMMVM_CGE2" @@ -115,8 +116,9 @@ struct SavegameHeader { uint8 version; Common::String saveName; Graphics::Surface *thumbnail; - int saveYear, saveMonth, saveDay; - int saveHour, saveMinutes; + int16 saveYear, saveMonth, saveDay; + int16 saveHour, saveMinutes; + uint32 playTime; }; enum ColorBank { kCBRel, kCBStd, kCBSay, kCBInf, kCBMnu, kCBWar }; @@ -161,7 +163,7 @@ public: virtual Common::Error loadGameState(int slot); virtual Common::Error run(); - static bool readSavegameHeader(Common::InSaveFile *in, SavegameHeader &header); + WARN_UNUSED_RESULT static bool readSavegameHeader(Common::InSaveFile *in, SavegameHeader &header, bool skipThumbnail = true); GUI::Debugger *getDebugger() { return _console; diff --git a/engines/cge2/detection.cpp b/engines/cge2/detection.cpp index d05dfffedc..ec6925ac74 100644 --- a/engines/cge2/detection.cpp +++ b/engines/cge2/detection.cpp @@ -62,6 +62,16 @@ static const ADGameDescription gameDescriptions[] = { }, { + "sfinx", "Freeware v1.1", + { + {"vol.cat", 0, "aa402aed24a72c53a4d1211c456b79dd", 129024}, + {"vol.dat", 0, "5966ac26d91d664714349669f9dd09b5", 34180367}, + AD_LISTEND + }, + Common::PL_POL, Common::kPlatformDOS, ADGF_NO_FLAGS, GUIO1(GAMEOPTION_COLOR_BLIND_DEFAULT_OFF) + }, + + { "sfinx", "Freeware v0.3", { {"vol.cat", 0, "f158e469dccbebc5a632eb848df89779", 129024}, @@ -122,7 +132,7 @@ public: return "Sfinx (C) 1994-1997 Janus B. Wisniewski and L.K. Avalon"; } - virtual const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const; + ADDetectedGame fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const override; virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const; virtual bool hasFeature(MetaEngineFeature f) const; virtual int getMaximumSaveSlot() const; @@ -131,13 +141,8 @@ public: virtual void removeSaveState(const char *target, int slot) const; }; -static const ADFileBasedFallback fileBasedFallback[] = { - { &gameDescriptions[0], { "vol.cat", "vol.dat", 0 } }, - { 0, { 0 } } -}; - static ADGameDescription s_fallbackDesc = { - "Sfinx", + "sfinx", "Unknown version", AD_ENTRY1(0, 0), // This should always be AD_ENTRY1(0, 0) in the fallback descriptor Common::UNK_LANG, @@ -146,30 +151,31 @@ static ADGameDescription s_fallbackDesc = { GUIO1(GAMEOPTION_COLOR_BLIND_DEFAULT_OFF) }; +static const ADFileBasedFallback fileBasedFallback[] = { + { &s_fallbackDesc, { "vol.cat", "vol.dat", 0 } }, + { 0, { 0 } } +}; + // This fallback detection looks identical to the one used for CGE. In fact, the difference resides // in the ResourceManager which handles a different archive format. The rest of the detection is identical. -const ADGameDescription *CGE2MetaEngine::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const { - ADFilePropertiesMap filesProps; - - const ADGameDescription *game; - game = detectGameFilebased(allFiles, fslist, CGE2::fileBasedFallback, &filesProps); +ADDetectedGame CGE2MetaEngine::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const { + ADDetectedGame game = detectGameFilebased(allFiles, fslist, CGE2::fileBasedFallback); - if (!game) - return 0; + if (!game.desc) + return ADDetectedGame(); SearchMan.addDirectory("CGE2MetaEngine::fallbackDetect", fslist.begin()->getParent()); ResourceManager *resman; resman = new ResourceManager(); - bool result = resman->exist("CGE.SAY"); + bool sayFileFound = resman->exist("CGE.SAY"); delete resman; SearchMan.remove("CGE2MetaEngine::fallbackDetect"); - if (!result) - return 0; + if (!sayFileFound) + return ADDetectedGame(); - reportUnknown(fslist.begin()->getParent(), filesProps); - return &s_fallbackDesc; + return game; } bool CGE2MetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const { @@ -185,6 +191,7 @@ bool CGE2MetaEngine::hasFeature(MetaEngineFeature f) const { (f == kSavesSupportMetaInfo) || (f == kSavesSupportThumbnail) || (f == kSavesSupportCreationDate) || + (f == kSavesSupportPlayTime) || (f == kSupportsListSaves) || (f == kSupportsLoadingDuringStartup) || (f == kSimpleSavesNames); @@ -221,10 +228,6 @@ SaveStateList CGE2MetaEngine::listSaves(const char *target) const { // Valid savegame if (CGE2::CGE2Engine::readSavegameHeader(file, header)) { saveList.push_back(SaveStateDescriptor(slotNum, header.saveName)); - if (header.thumbnail) { - header.thumbnail->free(); - delete header.thumbnail; - } } } else { // Must be an original format savegame @@ -253,7 +256,7 @@ SaveStateDescriptor CGE2MetaEngine::querySaveMetaInfos(const char *target, int s f->read(buffer, kSavegameStrSize + 1); bool hasHeader = !strncmp(buffer, kSavegameStr, kSavegameStrSize + 1) && - CGE2::CGE2Engine::readSavegameHeader(f, header); + CGE2::CGE2Engine::readSavegameHeader(f, header, false); delete f; if (!hasHeader) { @@ -267,6 +270,10 @@ SaveStateDescriptor CGE2MetaEngine::querySaveMetaInfos(const char *target, int s desc.setSaveDate(header.saveYear, header.saveMonth, header.saveDay); desc.setSaveTime(header.saveHour, header.saveMinutes); + if (header.playTime) { + desc.setPlayTime(header.playTime * 1000); + } + // Slot 0 is used for the 'automatic save on exit' save in Soltys, thus // we prevent it from being deleted or overwritten by accident. desc.setDeletableFlag(slot != 0); diff --git a/engines/cge2/events.cpp b/engines/cge2/events.cpp index 2dac04a0a5..b5c452745b 100644 --- a/engines/cge2/events.cpp +++ b/engines/cge2/events.cpp @@ -29,7 +29,6 @@ #include "common/config-manager.h" #include "common/events.h" #include "common/translation.h" -#include "engines/advancedDetector.h" #include "cge2/events.h" #include "cge2/text.h" #include "cge2/cge2_main.h" diff --git a/engines/cge2/saveload.cpp b/engines/cge2/saveload.cpp index cd0be84567..a8120c1017 100644 --- a/engines/cge2/saveload.cpp +++ b/engines/cge2/saveload.cpp @@ -118,9 +118,7 @@ bool CGE2Engine::loadGame(int slotNumber) { return false; } - // Delete the thumbnail - saveHeader.thumbnail->free(); - delete saveHeader.thumbnail; + g_engine->setTotalPlayTime(saveHeader.playTime * 1000); } resetGame(); @@ -178,10 +176,20 @@ void CGE2Engine::writeSavegameHeader(Common::OutSaveFile *out, SavegameHeader &h out->writeSint16LE(td.tm_mday); out->writeSint16LE(td.tm_hour); out->writeSint16LE(td.tm_min); + + out->writeUint32LE(g_engine->getTotalPlayTime() / 1000); } -bool CGE2Engine::readSavegameHeader(Common::InSaveFile *in, SavegameHeader &header) { - header.thumbnail = nullptr; +WARN_UNUSED_RESULT bool CGE2Engine::readSavegameHeader(Common::InSaveFile *in, SavegameHeader &header, bool skipThumbnail) { + header.version = 0; + header.saveName.clear(); + header.thumbnail = nullptr; + header.saveYear = 0; + header.saveMonth = 0; + header.saveDay = 0; + header.saveHour = 0; + header.saveMinutes = 0; + header.playTime = 0; // Get the savegame version header.version = in->readByte(); @@ -189,23 +197,27 @@ bool CGE2Engine::readSavegameHeader(Common::InSaveFile *in, SavegameHeader &head return false; // Read in the string - header.saveName.clear(); char ch; while ((ch = (char)in->readByte()) != '\0') header.saveName += ch; // Get the thumbnail - header.thumbnail = Graphics::loadThumbnail(*in); - if (!header.thumbnail) + if (!Graphics::loadThumbnail(*in, header.thumbnail, skipThumbnail)) { return false; + } // Read in save date/time - header.saveYear = in->readSint16LE(); - header.saveMonth = in->readSint16LE(); - header.saveDay = in->readSint16LE(); - header.saveHour = in->readSint16LE(); + header.saveYear = in->readSint16LE(); + header.saveMonth = in->readSint16LE(); + header.saveDay = in->readSint16LE(); + header.saveHour = in->readSint16LE(); header.saveMinutes = in->readSint16LE(); + if (header.version >= 2) { + header.playTime = in->readUint32LE(); + } + + return true; } diff --git a/engines/cge2/vga13h.cpp b/engines/cge2/vga13h.cpp index 8b0d8b6c77..8b8acb6ade 100644 --- a/engines/cge2/vga13h.cpp +++ b/engines/cge2/vga13h.cpp @@ -938,13 +938,13 @@ uint8 Vga::closest(Dac *pal, const uint8 colR, const uint8 colG, const uint8 col uint16 D = ((r > R) ? (r - R) : (R - r)) + ((g > G) ? (g - G) : (G - g)) + ((b > B) ? (b - B) : (B - b)) + - ((l > L) ? (l - L) : (L - l)) * 10 ; + ((l > L) ? (l - L) : (L - l)) * 10; if (D < dif) { found = i; dif = D; if (D == 0) - break; // exact! + break; // exact! } } return found; diff --git a/engines/chewy/chewy.cpp b/engines/chewy/chewy.cpp index 75c2a8d51b..81f8c7d03a 100644 --- a/engines/chewy/chewy.cpp +++ b/engines/chewy/chewy.cpp @@ -94,7 +94,7 @@ Common::Error ChewyEngine::run() { }*/ //_graphics->playVideo(0); - + _scene->change(0); //_sound->playSpeech(1); //_sound->playSound(1); diff --git a/engines/chewy/console.cpp b/engines/chewy/console.cpp index 65c4a681d6..af62bf017b 100644 --- a/engines/chewy/console.cpp +++ b/engines/chewy/console.cpp @@ -113,7 +113,7 @@ bool Console::Cmd_DrawImage(int argc, const char **argv) { Common::String filename = argv[1]; int resNum = atoi(argv[2]); - + _vm->_graphics->drawImage(filename, resNum); return false; diff --git a/engines/chewy/detection.cpp b/engines/chewy/detection.cpp index f6f66efba0..7b7f29f9e8 100644 --- a/engines/chewy/detection.cpp +++ b/engines/chewy/detection.cpp @@ -70,7 +70,7 @@ static const ChewyGameDescription gameDescriptions[] = { GUIO1(GUIO_NONE) }, }, - + { // Chewy - ESC von F5 - German // Master version 1.1 (CHEWY.EXE - offset 0x8AB28) @@ -87,7 +87,7 @@ static const ChewyGameDescription gameDescriptions[] = { GUIO1(GUIO_NONE) }, }, - + { // Chewy - ESC von F5 - German // Master version 1.0 (CHEWY.EXE - offset 0x8AB10) diff --git a/engines/chewy/scene.cpp b/engines/chewy/scene.cpp index 11bb4b38cd..5398a8da12 100644 --- a/engines/chewy/scene.cpp +++ b/engines/chewy/scene.cpp @@ -138,7 +138,7 @@ void Scene::change(uint scene) { _curScene = scene; _vm->_cursor->setCursor(0); _vm->_cursor->showCursor(); - + loadSceneInfo(); draw(); } @@ -320,14 +320,14 @@ void Scene::loadSceneInfo() { _sceneInfo->roomInfo.picNum = indexFile.readByte(); _sceneInfo->roomInfo.autoMoveCount = indexFile.readByte(); _sceneInfo->roomInfo.loadTaf = indexFile.readByte(); - + _sceneInfo->roomInfo.tafName = ""; for (int i = 0; i < 14; i++) _sceneInfo->roomInfo.tafName += indexFile.readByte(); _sceneInfo->roomInfo.zoomFactor = indexFile.readByte(); indexFile.readByte(); // padding - + for (int i = 0; i < MAX_AUTOMOVE; i++) { _sceneInfo->autoMove[i].x = indexFile.readSint16LE(); _sceneInfo->autoMove[i].y = indexFile.readSint16LE(); diff --git a/engines/cine/detection.cpp b/engines/cine/detection.cpp index 6c8b4a676d..f1636c902b 100644 --- a/engines/cine/detection.cpp +++ b/engines/cine/detection.cpp @@ -84,7 +84,7 @@ public: _guiOptions = GUIO2(GUIO_NOSPEECH, GAMEOPTION_ORIGINAL_SAVELOAD); } - virtual GameDescriptor findGame(const char *gameId) const { + PlainGameDescriptor findGame(const char *gameId) const override { return Engines::findGameID(gameId, _gameIds, obsoleteGameIDsTable); } diff --git a/engines/cine/sound.cpp b/engines/cine/sound.cpp index a8b4c085ff..70ce8fec98 100644 --- a/engines/cine/sound.cpp +++ b/engines/cine/sound.cpp @@ -299,11 +299,7 @@ void AdLibSoundDriver::setUpdateCallback(UpdateCallback upCb, void *ref) { void AdLibSoundDriver::setupChannel(int channel, const byte *data, int instrument, int volume) { assert(channel < 4); if (data) { - if (volume > 80) { - volume = 80; - } else if (volume < 0) { - volume = 0; - } + volume = CLIP(volume, 0, 80); volume += volume / 4; _channelsVolumeTable[channel] = volume; diff --git a/engines/composer/detection.cpp b/engines/composer/detection.cpp index dd1ce2751c..c90dfe5131 100644 --- a/engines/composer/detection.cpp +++ b/engines/composer/detection.cpp @@ -511,10 +511,9 @@ SaveStateList ComposerMetaEngine::listSaves(const char *target) const { Common::SaveFileManager *saveFileMan = g_system->getSavefileManager(); Common::StringArray filenames; Common::String saveDesc; - Common::String pattern = Common::String::format("%s.??", target); + Common::String pattern = Common::String::format("%s.##", target); filenames = saveFileMan->listSavefiles(pattern); - sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..) SaveStateList saveList; for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) { @@ -531,6 +530,7 @@ SaveStateList ComposerMetaEngine::listSaves(const char *target) const { } } + Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator()); return saveList; } diff --git a/engines/cruise/detection.cpp b/engines/cruise/detection.cpp index 6f5d236173..3dfd414fd2 100644 --- a/engines/cruise/detection.cpp +++ b/engines/cruise/detection.cpp @@ -241,9 +241,8 @@ SaveStateList CruiseMetaEngine::listSaves(const char *target) const { Common::InSaveFile *in = saveFileMan->openForLoading(*file); if (in) { Cruise::CruiseSavegameHeader header; - Cruise::readSavegameHeader(in, header); - saveList.push_back(SaveStateDescriptor(slotNum, header.saveName)); - delete header.thumbnail; + if (Cruise::readSavegameHeader(in, header)) + saveList.push_back(SaveStateDescriptor(slotNum, header.saveName)); delete in; } } @@ -264,7 +263,11 @@ SaveStateDescriptor CruiseMetaEngine::querySaveMetaInfos(const char *target, int if (f) { Cruise::CruiseSavegameHeader header; - Cruise::readSavegameHeader(f, header); + if (!Cruise::readSavegameHeader(f, header, false)) { + delete f; + return SaveStateDescriptor(); + } + delete f; // Create the return descriptor diff --git a/engines/cruise/saveload.cpp b/engines/cruise/saveload.cpp index a62648df08..7aaeb06905 100644 --- a/engines/cruise/saveload.cpp +++ b/engines/cruise/saveload.cpp @@ -43,9 +43,8 @@ struct overlayRestoreTemporary { overlayRestoreTemporary ovlRestoreData[90]; -bool readSavegameHeader(Common::InSaveFile *in, CruiseSavegameHeader &header) { +WARN_UNUSED_RESULT bool readSavegameHeader(Common::InSaveFile *in, CruiseSavegameHeader &header, bool skipThumbnail) { char saveIdentBuffer[6]; - header.thumbnail = NULL; // Validate the header Id in->read(saveIdentBuffer, 6); @@ -62,9 +61,9 @@ bool readSavegameHeader(Common::InSaveFile *in, CruiseSavegameHeader &header) { while ((ch = (char)in->readByte()) != '\0') header.saveName += ch; // Get the thumbnail - header.thumbnail = Graphics::loadThumbnail(*in); - if (!header.thumbnail) + if (!Graphics::loadThumbnail(*in, header.thumbnail, skipThumbnail)) { return false; + } return true; } @@ -827,8 +826,10 @@ Common::Error loadSavegameData(int saveGameIdx) { // Skip over the savegame header CruiseSavegameHeader header; - readSavegameHeader(f, header); - delete header.thumbnail; + if (!readSavegameHeader(f, header)) { + delete f; + return Common::kReadingFailed; + } // Synchronise the remaining data of the savegame Common::Serializer s(f, NULL); diff --git a/engines/cruise/saveload.h b/engines/cruise/saveload.h index 6fb1f4b545..c92f0e9da5 100644 --- a/engines/cruise/saveload.h +++ b/engines/cruise/saveload.h @@ -38,7 +38,7 @@ struct CruiseSavegameHeader { Common::Error saveSavegameData(int saveGameIdx, const Common::String &saveName); Common::Error loadSavegameData(int saveGameIdx); -bool readSavegameHeader(Common::InSaveFile *in, CruiseSavegameHeader &header); +WARN_UNUSED_RESULT bool readSavegameHeader(Common::InSaveFile *in, CruiseSavegameHeader &header, bool skipThumbnail = true); void initVars(); } // End of namespace Cruise diff --git a/engines/cruise/sound.cpp b/engines/cruise/sound.cpp index 57dcfcea8f..8d6ca571d1 100644 --- a/engines/cruise/sound.cpp +++ b/engines/cruise/sound.cpp @@ -334,11 +334,7 @@ void AdLibSoundDriver::syncSounds() { void AdLibSoundDriver::adjustVolume(int channel, int volume) { _channelsVolumeTable[channel].original = volume; - if (volume > 80) { - volume = 80; - } else if (volume < 0) { - volume = 0; - } + volume = CLIP(volume, 0, 80); volume += volume / 4; // The higher possible value for volume is 100 diff --git a/engines/cryo/cryo.h b/engines/cryo/cryo.h index 515849ffea..9a8bc913ba 100644 --- a/engines/cryo/cryo.h +++ b/engines/cryo/cryo.h @@ -25,7 +25,6 @@ #include "common/scummsys.h" #include "common/config-manager.h" -#include "engines/advancedDetector.h" #include "common/debug.h" #include "common/debug-channels.h" #include "common/error.h" @@ -39,6 +38,8 @@ #include "cryo/video.h" #include "cryo/debugger.h" +struct ADGameDescription; + namespace Cryo { class Console; diff --git a/engines/cryo/eden.cpp b/engines/cryo/eden.cpp index 103d9fde9e..c4a76a47a2 100644 --- a/engines/cryo/eden.cpp +++ b/engines/cryo/eden.cpp @@ -26,7 +26,6 @@ #include "common/debug.h" #include "common/debug-channels.h" #include "common/error.h" -#include "gui/EventRecorder.h" #include "common/file.h" #include "common/savefile.h" #include "common/fs.h" @@ -2321,7 +2320,7 @@ void EdenGame::my_bulle() { } else if (c >= 0x80 && c < 0x90) SysBeep(1); else if (c >= 0x90 && c < 0xA0) { - while (*textPtr++ != 0xFF) ; + while (*textPtr++ != 0xFF) {} textPtr--; } else if (c >= 0xA0 && c < 0xC0) _globals->_textToken1 = c & 0xF; @@ -2333,7 +2332,7 @@ void EdenGame::my_bulle() { #ifdef FAKE_DOS_VERSION _globals->_textWidthLimit = c1 + 160; #else - _globals->_textWidthLimit = c1 + _subtitlesXCenter; //TODO: signed? 160 in pc ver + _globals->_textWidthLimit = c1 + _subtitlesXCenter; // TODO: signed? 160 in pc ver #endif else { byte c2 = *textPtr++; diff --git a/engines/cryo/sound.cpp b/engines/cryo/sound.cpp index 68f067588f..95bf39eca9 100644 --- a/engines/cryo/sound.cpp +++ b/engines/cryo/sound.cpp @@ -1,113 +1,113 @@ -/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can 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 "cryo/sound.h"
-#include "audio/audiostream.h"
-#include "audio/mixer.h"
-#include "audio/decoders/raw.h"
-
-namespace Cryo {
-
-CSoundChannel::CSoundChannel(Audio::Mixer *mixer, unsigned int sampleRate, bool stereo, bool is16bits) : _mixer(mixer), _sampleRate(sampleRate), _stereo(stereo) {
- _bufferFlags = is16bits ? (Audio::FLAG_LITTLE_ENDIAN | Audio::FLAG_16BITS) : Audio::FLAG_UNSIGNED;
- if (stereo)
- _bufferFlags |= Audio::FLAG_STEREO;
- _audioStream = nullptr;
- _volumeLeft = _volumeRight = Audio::Mixer::kMaxChannelVolume;
-}
-
-CSoundChannel::~CSoundChannel() {
- stop();
- if (_audioStream)
- delete _audioStream;
-}
-
-void CSoundChannel::queueBuffer(byte *buffer, unsigned int size, bool playNow, bool playQueue, bool buffering) {
- if (playNow)
- stop();
-
- if (!buffer || !size)
- return;
-
- if (!_audioStream)
- _audioStream = Audio::makeQueuingAudioStream(_sampleRate, _stereo);
-
- if (buffering) {
- byte *localBuffer = (byte*)malloc(size);
- memcpy(localBuffer, buffer, size);
- _audioStream->queueBuffer(localBuffer, size, DisposeAfterUse::YES, _bufferFlags);
- } else
- _audioStream->queueBuffer(buffer, size, DisposeAfterUse::NO, _bufferFlags);
- if (playNow || playQueue)
- play();
-}
-
-void CSoundChannel::play() {
- if (!_audioStream)
- return;
- if (!_mixer->isSoundHandleActive(_soundHandle)) {
- _mixer->playStream(Audio::Mixer::kSFXSoundType, &_soundHandle, _audioStream, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO);
- applyVolumeChange();
- }
-}
-
-void CSoundChannel::stop() {
- if (_mixer->isSoundHandleActive(_soundHandle))
- _mixer->stopHandle(_soundHandle);
-
- if (_audioStream) {
- _audioStream->finish();
- delete _audioStream;
- _audioStream = nullptr;
- }
-}
-
-unsigned int CSoundChannel::numQueued() {
- return _audioStream ? _audioStream->numQueuedStreams() : 0;
-}
-
-unsigned int CSoundChannel::getVolume() {
- return (_volumeRight + _volumeLeft) / 2;
-}
-
-void CSoundChannel::setVolume(unsigned int volumeLeft, unsigned int volumeRight) {
- _volumeLeft = volumeLeft;
- _volumeRight = volumeRight;
- applyVolumeChange();
-}
-
-void CSoundChannel::setVolumeLeft(unsigned int volume) {
- setVolume(volume, _volumeRight);
-}
-
-void CSoundChannel::setVolumeRight(unsigned int volume) {
- setVolume(_volumeLeft, volume);
-}
-
-void CSoundChannel::applyVolumeChange() {
- unsigned int volume = (_volumeRight + _volumeLeft) / 2;
- int balance = (signed int)(_volumeRight - _volumeLeft) / 2;
- _mixer->setChannelVolume(_soundHandle, volume);
- _mixer->setChannelBalance(_soundHandle, balance);
-}
-
-}
+/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can 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 "cryo/sound.h" +#include "audio/audiostream.h" +#include "audio/mixer.h" +#include "audio/decoders/raw.h" + +namespace Cryo { + +CSoundChannel::CSoundChannel(Audio::Mixer *mixer, unsigned int sampleRate, bool stereo, bool is16bits) : _mixer(mixer), _sampleRate(sampleRate), _stereo(stereo) { + _bufferFlags = is16bits ? (Audio::FLAG_LITTLE_ENDIAN | Audio::FLAG_16BITS) : Audio::FLAG_UNSIGNED; + if (stereo) + _bufferFlags |= Audio::FLAG_STEREO; + _audioStream = nullptr; + _volumeLeft = _volumeRight = Audio::Mixer::kMaxChannelVolume; +} + +CSoundChannel::~CSoundChannel() { + stop(); + if (_audioStream) + delete _audioStream; +} + +void CSoundChannel::queueBuffer(byte *buffer, unsigned int size, bool playNow, bool playQueue, bool buffering) { + if (playNow) + stop(); + + if (!buffer || !size) + return; + + if (!_audioStream) + _audioStream = Audio::makeQueuingAudioStream(_sampleRate, _stereo); + + if (buffering) { + byte *localBuffer = (byte*)malloc(size); + memcpy(localBuffer, buffer, size); + _audioStream->queueBuffer(localBuffer, size, DisposeAfterUse::YES, _bufferFlags); + } else + _audioStream->queueBuffer(buffer, size, DisposeAfterUse::NO, _bufferFlags); + if (playNow || playQueue) + play(); +} + +void CSoundChannel::play() { + if (!_audioStream) + return; + if (!_mixer->isSoundHandleActive(_soundHandle)) { + _mixer->playStream(Audio::Mixer::kSFXSoundType, &_soundHandle, _audioStream, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO); + applyVolumeChange(); + } +} + +void CSoundChannel::stop() { + if (_mixer->isSoundHandleActive(_soundHandle)) + _mixer->stopHandle(_soundHandle); + + if (_audioStream) { + _audioStream->finish(); + delete _audioStream; + _audioStream = nullptr; + } +} + +unsigned int CSoundChannel::numQueued() { + return _audioStream ? _audioStream->numQueuedStreams() : 0; +} + +unsigned int CSoundChannel::getVolume() { + return (_volumeRight + _volumeLeft) / 2; +} + +void CSoundChannel::setVolume(unsigned int volumeLeft, unsigned int volumeRight) { + _volumeLeft = volumeLeft; + _volumeRight = volumeRight; + applyVolumeChange(); +} + +void CSoundChannel::setVolumeLeft(unsigned int volume) { + setVolume(volume, _volumeRight); +} + +void CSoundChannel::setVolumeRight(unsigned int volume) { + setVolume(_volumeLeft, volume); +} + +void CSoundChannel::applyVolumeChange() { + unsigned int volume = (_volumeRight + _volumeLeft) / 2; + int balance = (signed int)(_volumeRight - _volumeLeft) / 2; + _mixer->setChannelVolume(_soundHandle, volume); + _mixer->setChannelBalance(_soundHandle, balance); +} + +} diff --git a/engines/cryo/sound.h b/engines/cryo/sound.h index 72232cc4f1..ad5312f527 100644 --- a/engines/cryo/sound.h +++ b/engines/cryo/sound.h @@ -1,70 +1,70 @@ -/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#pragma once
-
-#include "audio/audiostream.h"
-#include "audio/mixer.h"
-#include "audio/decoders/raw.h"
-
-#include "cryo/cryolib.h"
-
-namespace Cryo {
-
-class CryoEngine;
-
-class CSoundChannel {
-private:
- Audio::Mixer *_mixer;
- Audio::QueuingAudioStream *_audioStream;
- Audio::SoundHandle _soundHandle;
- unsigned int _sampleRate;
- bool _stereo;
- unsigned int _bufferFlags;
-
- void applyVolumeChange();
-
-public:
- CSoundChannel(Audio::Mixer *mixer, unsigned int sampleRate, bool stereo, bool is16bits = false);
- ~CSoundChannel();
-
- // Queue a new buffer, cancel any previously queued buffers if playNow is set
- void queueBuffer(byte *buffer, unsigned int size, bool playNow = false, bool playQueue = true, bool buffering = true);
-
- // Play any queued buffers
- void play();
-
- // Stop playing and purge play queue
- void stop();
-
- // How many buffers in queue (including currently playing one)
- unsigned int numQueued();
-
- // Volume control
- int _volumeLeft, _volumeRight;
- unsigned int getVolume();
- void setVolume(unsigned int volumeLeft, unsigned int volumeRight);
- void setVolumeLeft(unsigned int volume);
- void setVolumeRight(unsigned int volume);
-};
-
-}
+/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can 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. + * + */ + +#pragma once + +#include "audio/audiostream.h" +#include "audio/mixer.h" +#include "audio/decoders/raw.h" + +#include "cryo/cryolib.h" + +namespace Cryo { + +class CryoEngine; + +class CSoundChannel { +private: + Audio::Mixer *_mixer; + Audio::QueuingAudioStream *_audioStream; + Audio::SoundHandle _soundHandle; + unsigned int _sampleRate; + bool _stereo; + unsigned int _bufferFlags; + + void applyVolumeChange(); + +public: + CSoundChannel(Audio::Mixer *mixer, unsigned int sampleRate, bool stereo, bool is16bits = false); + ~CSoundChannel(); + + // Queue a new buffer, cancel any previously queued buffers if playNow is set + void queueBuffer(byte *buffer, unsigned int size, bool playNow = false, bool playQueue = true, bool buffering = true); + + // Play any queued buffers + void play(); + + // Stop playing and purge play queue + void stop(); + + // How many buffers in queue (including currently playing one) + unsigned int numQueued(); + + // Volume control + int _volumeLeft, _volumeRight; + unsigned int getVolume(); + void setVolume(unsigned int volumeLeft, unsigned int volumeRight); + void setVolumeLeft(unsigned int volume); + void setVolumeRight(unsigned int volume); +}; + +} diff --git a/engines/cryo/video.cpp b/engines/cryo/video.cpp index fe242425e5..fe8afb7ce8 100644 --- a/engines/cryo/video.cpp +++ b/engines/cryo/video.cpp @@ -102,7 +102,7 @@ void HnmPlayer::waitLoop() { _nextFrameTime = _expectedFrameTime - _timeDrift; if (_useSoundSync && _vm->_timerTicks > 1000.0 + _nextFrameTime) _useSound = false; - while (_vm->_timerTicks < _nextFrameTime) ; // waste time + while (_vm->_timerTicks < _nextFrameTime) {} // waste time _timeDrift = _vm->_timerTicks - _nextFrameTime; } diff --git a/engines/director/detection.cpp b/engines/director/detection.cpp index 16d838fcca..9d293846bc 100644 --- a/engines/director/detection.cpp +++ b/engines/director/detection.cpp @@ -112,7 +112,7 @@ public: return "Macromedia Director (C) Macromedia"; } - const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const; + ADDetectedGame fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const override; virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const; }; @@ -141,7 +141,7 @@ static Director::DirectorGameDescription s_fallbackDesc = { static char s_fallbackFileNameBuffer[51]; -const ADGameDescription *DirectorMetaEngine::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const { +ADDetectedGame DirectorMetaEngine::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const { // TODO: Handle Mac fallback // reset fallback description @@ -230,10 +230,10 @@ const ADGameDescription *DirectorMetaEngine::fallbackDetect(const FileMap &allFi warning("Director fallback detection D%d", desc->version); - return (ADGameDescription *)desc; + return ADDetectedGame(&desc->desc); } - return 0; + return ADDetectedGame(); } #if PLUGIN_ENABLED_DYNAMIC(DIRECTOR) diff --git a/engines/director/frame.cpp b/engines/director/frame.cpp index 1b3b59e1ef..e42444dc00 100644 --- a/engines/director/frame.cpp +++ b/engines/director/frame.cpp @@ -241,7 +241,7 @@ void Frame::readChannels(Common::ReadStreamEndian *stream) { sprite._width = stream->readUint16(); stream->readUint16(); stream->readUint16(); - + } if (sprite._castId) { diff --git a/engines/director/lingo/lingo-gr.cpp b/engines/director/lingo/lingo-gr.cpp index b8d9749052..c908b2e591 100644 --- a/engines/director/lingo/lingo-gr.cpp +++ b/engines/director/lingo/lingo-gr.cpp @@ -1596,7 +1596,7 @@ int yydebug; # define YYMAXDEPTH 10000 #endif - + #if YYERROR_VERBOSE @@ -1807,7 +1807,7 @@ yysyntax_error (char *yyresult, int yystate, int yychar) } } #endif /* YYERROR_VERBOSE */ - + /*-----------------------------------------------. | Release the memory associated to this symbol. | @@ -1839,7 +1839,7 @@ yydestruct (yymsg, yytype, yyvaluep) break; } } - + /* Prevent warnings from -Wmissing-prototypes. */ @@ -1896,7 +1896,7 @@ yyparse () #endif #endif { - + int yystate; int yyn; int yyresult; diff --git a/engines/director/lingo/lingo-lex.cpp b/engines/director/lingo/lingo-lex.cpp index 7b0398367b..de7005275d 100644 --- a/engines/director/lingo/lingo-lex.cpp +++ b/engines/director/lingo/lingo-lex.cpp @@ -168,7 +168,7 @@ extern FILE *yyin, *yyout; #define EOB_ACT_LAST_MATCH 2 #define YY_LESS_LINENO(n) - + /* Return all but the first "n" matched characters back to the input stream. */ #define yyless(n) \ do \ @@ -225,7 +225,7 @@ struct yy_buffer_state int yy_bs_lineno; /**< The line count. */ int yy_bs_column; /**< The column count. */ - + /* Whether to try to fill the input buffer when we reach the * end of it. */ @@ -963,7 +963,7 @@ YY_DECL register yy_state_type yy_current_state; register char *yy_cp, *yy_bp; register int yy_act; - + #line 85 "engines/director/lingo/lingo-lex.l" @@ -1859,7 +1859,7 @@ static int yy_get_next_buffer (void) { register yy_state_type yy_current_state; register char *yy_cp; - + yy_current_state = (yy_start); yy_current_state += YY_AT_BOL(); @@ -1920,7 +1920,7 @@ static int yy_get_next_buffer (void) { int c; - + *(yy_c_buf_p) = (yy_hold_char); if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) @@ -1994,7 +1994,7 @@ static int yy_get_next_buffer (void) */ void yyrestart (FILE * input_file ) { - + if ( ! YY_CURRENT_BUFFER ){ yyensure_buffer_stack (); YY_CURRENT_BUFFER_LVALUE = @@ -2011,7 +2011,7 @@ static int yy_get_next_buffer (void) */ void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ) { - + /* TODO. We should be able to replace this entire function body * with * yypop_buffer_state(); @@ -2057,7 +2057,7 @@ static void yy_load_buffer_state (void) YY_BUFFER_STATE yy_create_buffer (FILE * file, int size ) { YY_BUFFER_STATE b; - + b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); @@ -2084,7 +2084,7 @@ static void yy_load_buffer_state (void) */ void yy_delete_buffer (YY_BUFFER_STATE b ) { - + if ( ! b ) return; @@ -2100,7 +2100,7 @@ static void yy_load_buffer_state (void) #ifndef __cplusplus extern int isatty (int ); #endif /* __cplusplus */ - + /* Initializes or reinitializes a buffer. * This function is sometimes called more than once on the same buffer, * such as during a yyrestart() or at EOF. @@ -2109,7 +2109,7 @@ extern int isatty (int ); { int oerrno = errno; - + yy_flush_buffer(b ); b->yy_input_file = file; @@ -2125,7 +2125,7 @@ extern int isatty (int ); } b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; - + errno = oerrno; } @@ -2214,7 +2214,7 @@ void yypop_buffer_state (void) static void yyensure_buffer_stack (void) { yy_size_t num_to_alloc; - + if (!(yy_buffer_stack)) { /* First allocation is just for 2 elements, since we don't know if this @@ -2227,9 +2227,9 @@ static void yyensure_buffer_stack (void) ); if ( ! (yy_buffer_stack) ) YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); - + memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); - + (yy_buffer_stack_max) = num_to_alloc; (yy_buffer_stack_top) = 0; return; @@ -2263,7 +2263,7 @@ static void yyensure_buffer_stack (void) YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size ) { YY_BUFFER_STATE b; - + if ( size < 2 || base[size-2] != YY_END_OF_BUFFER_CHAR || base[size-1] != YY_END_OF_BUFFER_CHAR ) @@ -2299,7 +2299,7 @@ YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size ) */ YY_BUFFER_STATE yy_scan_string (yyconst char * yystr ) { - + return yy_scan_bytes(yystr,strlen(yystr) ); } @@ -2315,7 +2315,7 @@ YY_BUFFER_STATE yy_scan_bytes (yyconst char * yybytes, yy_size_t _yybytes_len YY_BUFFER_STATE b; char *buf; yy_size_t n, i; - + /* Get memory for full buffer, including space for trailing EOB's. */ n = _yybytes_len + 2; buf = (char *) yyalloc(n ); @@ -2373,7 +2373,7 @@ static void yy_fatal_error (yyconst char* msg ) */ int yyget_lineno (void) { - + return yylineno; } @@ -2416,7 +2416,7 @@ char *yyget_text (void) */ void yyset_lineno (int line_number ) { - + yylineno = line_number; } @@ -2477,7 +2477,7 @@ static int yy_init_globals (void) /* yylex_destroy is for both reentrant and non-reentrant scanners. */ int yylex_destroy (void) { - + /* Pop the buffer stack, destroying each element. */ while(YY_CURRENT_BUFFER){ yy_delete_buffer(YY_CURRENT_BUFFER ); diff --git a/engines/director/resource.cpp b/engines/director/resource.cpp index e748583146..22b33e7bcc 100644 --- a/engines/director/resource.cpp +++ b/engines/director/resource.cpp @@ -176,7 +176,7 @@ void DirectorEngine::loadEXEv4(Common::SeekableReadStream *stream) { void DirectorEngine::loadEXEv5(Common::SeekableReadStream *stream) { uint32 ver = stream->readUint32LE(); - + if (ver != MKTAG('P', 'J', '9', '5')) error("Invalid projector tag found in v5 EXE [%s]", tag2str(ver)); diff --git a/engines/dm/dm.h b/engines/dm/dm.h index 16307778ba..e330cb6be7 100644 --- a/engines/dm/dm.h +++ b/engines/dm/dm.h @@ -332,7 +332,7 @@ public: Thing _thingParty; // @ C0xFFFF_THING_PARTY }; -bool readSaveGameHeader(Common::InSaveFile *in, SaveGameHeader *header); +WARN_UNUSED_RESULT bool readSaveGameHeader(Common::InSaveFile *in, SaveGameHeader *header, bool skipThumbnail = true); } // End of namespace DM diff --git a/engines/dm/dungeonman.cpp b/engines/dm/dungeonman.cpp index ab35477183..7d7b80685e 100644 --- a/engines/dm/dungeonman.cpp +++ b/engines/dm/dungeonman.cpp @@ -1465,6 +1465,7 @@ Thing DungeonMan::getDiscardThing(uint16 thingType) { case kDMThingTypeGroup: if (((Group *)squareThingData)->getDoNotDiscard()) continue; + // fall through case kDMThingTypeProjectile: setCurrentMap(mapIndex); if (thingType == kDMThingTypeGroup) { diff --git a/engines/dm/gfx.cpp b/engines/dm/gfx.cpp index 7f31b29575..f75e7eb170 100644 --- a/engines/dm/gfx.cpp +++ b/engines/dm/gfx.cpp @@ -2162,22 +2162,20 @@ void DisplayMan::drawSquareD0L(Direction dir, int16 posX, int16 posY) { uint16 squareAspect[5]; _vm->_dungeonMan->setSquareAspect(squareAspect, dir, posX, posY); switch (squareAspect[kDMSquareAspectElement]) { - case kDMElementTypeWall: - drawWallSetBitmap(bitmapWallSetWallD0L, _frameWalls163[kDMViewSquareD0L]); - break; + case kDMElementTypeStairsSide: + drawFloorPitOrStairsBitmap(_stairsNativeBitmapIndexSideD0L, frameStairsSideD0L); + return; + case kDMElementTypePit: + drawFloorPitOrStairsBitmap(squareAspect[kDMSquareAspectPitInvisible] ? kDMGraphicIdxFloorPitInvisibleD0L : kDMGraphicIdxFloorPitD0L, frameFloorPitD0L); + // fall through case kDMElementTypeCorridor: - case kDMElementTypeTeleporter: case kDMElementTypeDoorSide: + case kDMElementTypeTeleporter: drawObjectsCreaturesProjectilesExplosions(Thing(squareAspect[kDMSquareAspectFirstGroupOrObject]), dir, posX, posY, kDMViewSquareD0L, kDMCellOrderBackRight); break; - case kDMElementTypePit: - drawFloorPitOrStairsBitmap(squareAspect[kDMSquareAspectPitInvisible] ? kDMGraphicIdxFloorPitInvisibleD0L : kDMGraphicIdxFloorPitD0L, frameFloorPitD0L); - case kDMElementTypeStairsSide: - if (squareAspect[kDMSquareAspectStairsUp]) - drawFloorPitOrStairsBitmap(_stairsNativeBitmapIndexSideD0L, frameStairsSideD0L); - break; - default: - break; + case kDMElementTypeWall: + drawWallSetBitmap(bitmapWallSetWallD0L, _frameWalls163[kDMViewSquareD0L]); + return; } drawCeilingPit(kDMGraphicIdxCeilingPitD0L, &frameCeilingPitD0L, posX, posY, false); @@ -2200,6 +2198,7 @@ void DisplayMan::drawSquareD0R(Direction dir, int16 posX, int16 posY) { case kDMElementTypePit: drawFloorPitOrStairsBitmapFlippedHorizontally(squareAspect[kDMSquareAspectPitInvisible] ? kDMGraphicIdxFloorPitInvisibleD0L : kDMGraphicIdxFloorPitD0L, frameFloorPitD0R); + // fall through case kDMElementTypeCorridor: case kDMElementTypeDoorSide: case kDMElementTypeTeleporter: diff --git a/engines/dm/group.cpp b/engines/dm/group.cpp index dfdcdc017f..f36a8ddc31 100644 --- a/engines/dm/group.cpp +++ b/engines/dm/group.cpp @@ -1518,6 +1518,7 @@ bool GroupMan::isCreatureAttacking(Group *group, int16 mapX, int16 mapY, uint16 projectileThing = _vm->_thingExplPoisonCloud; break; } + // fall through case kDMCreatureTypeDemon: case kDMCreatureTypeRedDragon: projectileThing = _vm->_thingExplFireBall; diff --git a/engines/dm/loadsave.cpp b/engines/dm/loadsave.cpp index 3d8f76c33f..864a726367 100644 --- a/engines/dm/loadsave.cpp +++ b/engines/dm/loadsave.cpp @@ -70,7 +70,10 @@ LoadgameResult DMEngine::loadgame(int16 slot) { file = saveFileManager->openForLoading(fileName); SaveGameHeader header; - readSaveGameHeader(file, &header); + if (!readSaveGameHeader(file, &header)) { + delete file; + return kDMLoadgameFailure; + } warning("MISSING CODE: missing check for matching format and platform in save in f435_loadgame"); @@ -397,7 +400,7 @@ bool DMEngine::writeCompleteSaveFile(int16 saveSlot, Common::String& saveDescrip return true; } -bool readSaveGameHeader(Common::InSaveFile *in, SaveGameHeader *header) { +WARN_UNUSED_RESULT bool readSaveGameHeader(Common::InSaveFile *in, SaveGameHeader *header, bool skipThumbnail) { uint32 id = in->readUint32BE(); // Check if it's a valid ScummVM savegame @@ -419,7 +422,11 @@ bool readSaveGameHeader(Common::InSaveFile *in, SaveGameHeader *header) { header->_descr.setDescription(saveName); // Get the thumbnail - header->_descr.setThumbnail(Graphics::loadThumbnail(*in)); + Graphics::Surface *thumbnail; + if (!Graphics::loadThumbnail(*in, thumbnail, skipThumbnail)) { + return false; + } + header->_descr.setThumbnail(thumbnail); uint32 saveDate = in->readUint32BE(); uint16 saveTime = in->readUint16BE(); diff --git a/engines/dm/menus.cpp b/engines/dm/menus.cpp index 1600f589e9..f547bbe934 100644 --- a/engines/dm/menus.cpp +++ b/engines/dm/menus.cpp @@ -733,9 +733,9 @@ Spell *MenuMan::getSpellFromSymbols(byte *symbols) { if (*(symbols + 1)) { int16 bitShiftCount = 24; int32 curSymbols = 0; - do + do { curSymbols |= (long)*symbols++ << bitShiftCount; - while (*symbols && ((bitShiftCount -= 8) >= 0)); + } while (*symbols && ((bitShiftCount -= 8) >= 0)); Spell *curSpell = SpellsArray; int16 spellIndex = 25; while (spellIndex--) { @@ -1136,6 +1136,7 @@ bool MenuMan::isActionPerformed(uint16 champIndex, int16 actionIndex) { _vm->_sound->requestPlay(kDMSoundIndexWoodenThudAttackTrolinAntmanStoneGolem, dungeon._partyMapX, dungeon._partyMapY, kDMSoundModePlayOneTickLater); break; } + // fall through case kDMActionDisrupt: case kDMActionJab: case kDMActionParry: diff --git a/engines/dm/projexpl.cpp b/engines/dm/projexpl.cpp index e8b0f4a143..0738cf64ba 100644 --- a/engines/dm/projexpl.cpp +++ b/engines/dm/projexpl.cpp @@ -503,10 +503,10 @@ void ProjExpl::processEvent25(TimelineEvent *event) { case 0xFF82: if (!(attack >>= 1)) break; + // fall through case 0xFF80: if (curSquareType == kDMElementTypeDoor) _vm->_groupMan->groupIsDoorDestoryedByAttack(mapX, mapY, attack, true, 0); - break; case 0xFF83: if ((groupThing != _vm->_thingEndOfList) && getFlag(creatureInfo->_attributes, kDMCreatureMaskNonMaterial)) { diff --git a/engines/draci/detection.cpp b/engines/draci/detection.cpp index 65427bd8cd..9a76e3890c 100644 --- a/engines/draci/detection.cpp +++ b/engines/draci/detection.cpp @@ -132,10 +132,6 @@ SaveStateList DraciMetaEngine::listSaves(const char *target) const { Draci::DraciSavegameHeader header; if (Draci::readSavegameHeader(in, header)) { saveList.push_back(SaveStateDescriptor(slotNum, header.saveName)); - if (header.thumbnail) { - header.thumbnail->free(); - delete header.thumbnail; - } } delete in; } @@ -157,7 +153,11 @@ SaveStateDescriptor DraciMetaEngine::querySaveMetaInfos(const char *target, int if (f) { Draci::DraciSavegameHeader header; - Draci::readSavegameHeader(f, header); + if (!Draci::readSavegameHeader(f, header, false)) { + delete f; + return SaveStateDescriptor(); + } + delete f; // Create the return descriptor diff --git a/engines/draci/game.cpp b/engines/draci/game.cpp index 4fbf2d31ea..af57b1eb37 100644 --- a/engines/draci/game.cpp +++ b/engines/draci/game.cpp @@ -1594,7 +1594,7 @@ Game::~Game() { delete[] _items; } -void Game::DoSync(Common::Serializer &s, uint8 saveVersion) { +void Game::synchronize(Common::Serializer &s, uint8 saveVersion) { s.syncAsUint16LE(_currentRoom._roomNum); for (uint i = 0; i < _info._numObjects; ++i) { diff --git a/engines/draci/game.h b/engines/draci/game.h index 53a472a552..1e3cca9b9d 100644 --- a/engines/draci/game.h +++ b/engines/draci/game.h @@ -330,7 +330,7 @@ public: void setEnableSpeedText(bool value) { _enableSpeedText = value; } bool getEnableSpeedText() const { return _enableSpeedText; } - void DoSync(Common::Serializer &s, uint8 saveVersion); + void synchronize(Common::Serializer &s, uint8 saveVersion); private: void updateOrdinaryCursor(); diff --git a/engines/draci/saveload.cpp b/engines/draci/saveload.cpp index 3e7f8651c1..83c64b5725 100644 --- a/engines/draci/saveload.cpp +++ b/engines/draci/saveload.cpp @@ -35,9 +35,8 @@ namespace Draci { static const char *const draciIdentString = "DRACI"; -bool readSavegameHeader(Common::InSaveFile *in, DraciSavegameHeader &header) { +WARN_UNUSED_RESULT bool readSavegameHeader(Common::InSaveFile *in, DraciSavegameHeader &header, bool skipThumbnail) { char saveIdentBuffer[6]; - header.thumbnail = NULL; // Validate the header Id in->read(saveIdentBuffer, 6); @@ -59,9 +58,9 @@ bool readSavegameHeader(Common::InSaveFile *in, DraciSavegameHeader &header) { header.playtime = in->readUint32LE(); // Get the thumbnail - header.thumbnail = Graphics::loadThumbnail(*in); - if (!header.thumbnail) + if (!Graphics::loadThumbnail(*in, header.thumbnail, skipThumbnail)) { return false; + } return true; } @@ -107,7 +106,7 @@ Common::Error saveSavegameData(int saveGameIdx, const Common::String &saveName, } else { // Create the remainder of the savegame Common::Serializer s(NULL, f); - vm._game->DoSync(s, header.version); + vm._game->synchronize(s, header.version); f->finalize(); delete f; @@ -130,10 +129,6 @@ Common::Error loadSavegameData(int saveGameIdx, DraciEngine *vm) { if (!readSavegameHeader(f, header)) { return Common::kNoGameDataFoundError; } - if (header.thumbnail) { - header.thumbnail->free(); - delete header.thumbnail; - } // Pre-processing vm->_game->rememberRoomNumAsPrevious(); @@ -141,7 +136,7 @@ Common::Error loadSavegameData(int saveGameIdx, DraciEngine *vm) { // Synchronise the remaining data of the savegame Common::Serializer s(f, NULL); - vm->_game->DoSync(s, header.version); + vm->_game->synchronize(s, header.version); delete f; // Post-processing diff --git a/engines/draci/saveload.h b/engines/draci/saveload.h index 6f951a3409..bceaebb468 100644 --- a/engines/draci/saveload.h +++ b/engines/draci/saveload.h @@ -42,7 +42,7 @@ struct DraciSavegameHeader { class DraciEngine; -bool readSavegameHeader(Common::InSaveFile *in, DraciSavegameHeader &header); +WARN_UNUSED_RESULT bool readSavegameHeader(Common::InSaveFile *in, DraciSavegameHeader &header, bool skipThumbnail = true); void writeSavegameHeader(Common::OutSaveFile *out, const DraciSavegameHeader &header); Common::Error saveSavegameData(int saveGameIdx, const Common::String &saveName, DraciEngine &vm); Common::Error loadSavegameData(int saveGameIdx, DraciEngine *vm); diff --git a/engines/drascula/detection.cpp b/engines/drascula/detection.cpp index 5f453839e7..a1f2f3e117 100644 --- a/engines/drascula/detection.cpp +++ b/engines/drascula/detection.cpp @@ -430,7 +430,11 @@ SaveStateDescriptor DrasculaMetaEngine::querySaveMetaInfos(const char *target, i return SaveStateDescriptor(); } - Graphics::Surface *const thumbnail = Graphics::loadThumbnail(*in); + Graphics::Surface *thumbnail; + if (!Graphics::loadThumbnail(*in, thumbnail)) { + delete in; + return SaveStateDescriptor(); + } desc.setThumbnail(thumbnail); delete in; diff --git a/engines/dreamweb/detection.cpp b/engines/dreamweb/detection.cpp index 11966047b0..36af0c3811 100644 --- a/engines/dreamweb/detection.cpp +++ b/engines/dreamweb/detection.cpp @@ -198,7 +198,12 @@ SaveStateDescriptor DreamWebMetaEngine::querySaveMetaInfos(const char *target, i uint32 saveDate = in->readUint32LE(); uint32 saveTime = in->readUint32LE(); uint32 playTime = in->readUint32LE(); - Graphics::Surface *thumbnail = Graphics::loadThumbnail(*in); + Graphics::Surface *thumbnail; + if (!Graphics::loadThumbnail(*in, thumbnail)) { + warning("Missing or broken thumbnail - skipping"); + delete in; + return desc; + } int day = (saveDate >> 24) & 0xFF; int month = (saveDate >> 16) & 0xFF; diff --git a/engines/fullpipe/detection.cpp b/engines/fullpipe/detection.cpp index 9dd1d6beb4..3144a0ebff 100644 --- a/engines/fullpipe/detection.cpp +++ b/engines/fullpipe/detection.cpp @@ -184,7 +184,9 @@ SaveStateList FullpipeMetaEngine::listSaves(const char *target) const { Common::ScopedPtr<Common::InSaveFile> in(saveFileMan->openForLoading(*file)); if (in) { Fullpipe::FullpipeSavegameHeader header; - Fullpipe::readSavegameHeader(in.get(), header); + if (!Fullpipe::readSavegameHeader(in.get(), header)) { + continue; + } SaveStateDescriptor desc; @@ -212,7 +214,9 @@ SaveStateDescriptor FullpipeMetaEngine::querySaveMetaInfos(const char *target, i if (f) { Fullpipe::FullpipeSavegameHeader header; - Fullpipe::readSavegameHeader(f.get(), header); + if (!Fullpipe::readSavegameHeader(f.get(), header, false)) { + return SaveStateDescriptor(); + } // Create the return descriptor SaveStateDescriptor desc; diff --git a/engines/fullpipe/gameloader.h b/engines/fullpipe/gameloader.h index 03c3093086..eb5957c78e 100644 --- a/engines/fullpipe/gameloader.h +++ b/engines/fullpipe/gameloader.h @@ -84,7 +84,7 @@ struct FullpipeSavegameHeader { uint32 date; uint16 time; uint32 playtime; - Common::SharedPtr<Graphics::Surface> thumbnail; + Graphics::Surface *thumbnail; }; struct SaveHeader { @@ -142,7 +142,7 @@ class GameLoader : public CObject { }; const char *getSavegameFile(int saveGameIdx); -bool readSavegameHeader(Common::InSaveFile *in, FullpipeSavegameHeader &header); +WARN_UNUSED_RESULT bool readSavegameHeader(Common::InSaveFile *in, FullpipeSavegameHeader &header, bool skipThumbnail = true); void parseSavegameHeader(Fullpipe::FullpipeSavegameHeader &header, SaveStateDescriptor &desc); Inventory2 *getGameLoaderInventory(); diff --git a/engines/fullpipe/interaction.h b/engines/fullpipe/interaction.h index 9ffcdccc91..7ea58e60b4 100644 --- a/engines/fullpipe/interaction.h +++ b/engines/fullpipe/interaction.h @@ -70,11 +70,10 @@ public: private: InteractionList _interactions; - int16 _field_20; static bool compareInteractions(const Interaction *i1, const Interaction *i2); public: - InteractionController() : _field_20(0), _flag24(true) {} + InteractionController() : _flag24(true) {} virtual ~InteractionController(); virtual bool load(MfcArchive &file); diff --git a/engines/fullpipe/modal.cpp b/engines/fullpipe/modal.cpp index be1e73ff70..3fdb6636ec 100644 --- a/engines/fullpipe/modal.cpp +++ b/engines/fullpipe/modal.cpp @@ -21,23 +21,21 @@ */ #include "fullpipe/fullpipe.h" -#include "fullpipe/messages.h" + #include "fullpipe/constants.h" +#include "fullpipe/gameloader.h" +#include "fullpipe/messages.h" +#include "fullpipe/modal.h" #include "fullpipe/motion.h" +#include "fullpipe/objectnames.h" #include "fullpipe/scenes.h" -#include "fullpipe/gameloader.h" #include "fullpipe/statics.h" -#include "fullpipe/modal.h" -#include "fullpipe/constants.h" -#include "fullpipe/objectnames.h" +#include "engines/savestate.h" #include "graphics/palette.h" #include "graphics/surface.h" -#include "engines/savestate.h" -#include "engines/advancedDetector.h" - namespace Fullpipe { ModalIntro::ModalIntro() { @@ -2142,7 +2140,8 @@ bool ModalSaveGame::getFileInfo(int slot, FileInfo *fileinfo) { return false; Fullpipe::FullpipeSavegameHeader header; - Fullpipe::readSavegameHeader(f.get(), header); + if (!Fullpipe::readSavegameHeader(f.get(), header)) + return false; // Create the return descriptor SaveStateDescriptor desc(slot, header.saveName); @@ -2396,7 +2395,7 @@ bool ModalDemo::init(int counterDiff) { if (_clickedQuit == -1) return true; - g_system->openUrl("http://www.amazon.de/EuroVideo-Bildprogramm-GmbH-Full-Pipe/dp/B003TO51YE/ref=sr_1_1?ie=UTF8&s=videogames&qid=1279207213&sr=8-1"); + g_system->openUrl("http://www.amazon.de/EuroVideo-Bildprogramm-GmbH-Full-Pipe/dp/B003TO51YE/ref=sr_1_1"); g_fp->_gameContinue = false; diff --git a/engines/fullpipe/scenes/scene22.cpp b/engines/fullpipe/scenes/scene22.cpp index 5b9b091f36..f71460576f 100644 --- a/engines/fullpipe/scenes/scene22.cpp +++ b/engines/fullpipe/scenes/scene22.cpp @@ -218,7 +218,7 @@ void sceneHandler22_stoolLogic(ExCommand *cmd) { ani = g_fp->_currentScene->getStaticANIObject1ById(ANI_TABURETTE, -1); if (ani && (ani->_flags & 4)) { int x = g_fp->_aniMan->_ox; - int y = g_fp->_aniMan->_ox; + int y = g_fp->_aniMan->_oy; if (sqrt((double)((841 - x) * (841 - x) + (449 - y) * (449 - y))) < sqrt((double)((1075 - x) * (1075 - x) + (449 - y) * (449 - y)))) { diff --git a/engines/fullpipe/stateloader.cpp b/engines/fullpipe/stateloader.cpp index 703190be0b..5b89b6b0e2 100644 --- a/engines/fullpipe/stateloader.cpp +++ b/engines/fullpipe/stateloader.cpp @@ -22,6 +22,13 @@ #include "fullpipe/fullpipe.h" +#include "fullpipe/constants.h" +#include "fullpipe/gameloader.h" +#include "fullpipe/interaction.h" +#include "fullpipe/objects.h" +#include "fullpipe/scene.h" +#include "fullpipe/statics.h" + #include "common/file.h" #include "common/array.h" #include "common/list.h" @@ -29,15 +36,6 @@ #include "graphics/thumbnail.h" -#include "fullpipe/objects.h" -#include "fullpipe/gameloader.h" -#include "fullpipe/scene.h" -#include "fullpipe/statics.h" -#include "fullpipe/interaction.h" -#include "fullpipe/gameloader.h" - -#include "fullpipe/constants.h" - namespace Fullpipe { bool GameLoader::readSavegame(const char *fname) { @@ -69,6 +67,11 @@ bool GameLoader::readSavegame(const char *fname) { Common::Array<byte> map(800); saveFile->read(map.data(), 800); + FullpipeSavegameHeader header2; + if (Fullpipe::readSavegameHeader(saveFile.get(), header2)) { + g_fp->setTotalPlayTime(header2.playtime * 1000); + } + { Common::MemoryReadStream tempStream(map.data(), 800, DisposeAfterUse::NO); MfcArchive temp(&tempStream); @@ -185,10 +188,10 @@ void fillDummyHeader(Fullpipe::FullpipeSavegameHeader &header) { // This is wrong header, perhaps it is original savegame. Thus fill out dummy values header.date = (20 << 24) | (9 << 16) | 2016; header.time = (9 << 8) | 56; - header.playtime = 1000; + header.playtime = 0; } -bool readSavegameHeader(Common::InSaveFile *in, FullpipeSavegameHeader &header) { +WARN_UNUSED_RESULT bool readSavegameHeader(Common::InSaveFile *in, FullpipeSavegameHeader &header, bool skipThumbnail) { uint oldPos = in->pos(); in->seek(-4, SEEK_END); @@ -232,13 +235,13 @@ bool readSavegameHeader(Common::InSaveFile *in, FullpipeSavegameHeader &header) header.description = header.saveName; // Get the thumbnail - header.thumbnail = Common::SharedPtr<Graphics::Surface>(Graphics::loadThumbnail(*in), Graphics::SurfaceDeleter()); + if (!Graphics::loadThumbnail(*in, header.thumbnail, skipThumbnail)) { + in->seek(oldPos, SEEK_SET); // Rewind the file + return false; + } in->seek(oldPos, SEEK_SET); // Rewind the file - if (!header.thumbnail) - return false; - return true; } diff --git a/engines/fullpipe/utils.cpp b/engines/fullpipe/utils.cpp index c1cf912de6..3b60e962c6 100644 --- a/engines/fullpipe/utils.cpp +++ b/engines/fullpipe/utils.cpp @@ -49,7 +49,7 @@ bool ObArray::load(MfcArchive &file) { debugC(5, kDebugLoading, "ObArray::load()"); int count = file.readCount(); - resize(count); + reserve(count); for (int i = 0; i < count; i++) { CObject *t = file.readClass<CObject>(); @@ -66,7 +66,7 @@ bool DWordArray::load(MfcArchive &file) { debugC(9, kDebugLoading, "DWordArray::count: %d", count); - resize(count); + reserve(count); for (int i = 0; i < count; i++) { int32 t = file.readSint32LE(); diff --git a/engines/game.cpp b/engines/game.cpp index 7ff51a99cc..ee14acf7c8 100644 --- a/engines/game.cpp +++ b/engines/game.cpp @@ -22,6 +22,7 @@ #include "engines/game.h" #include "common/gui_options.h" +#include "common/translation.h" const PlainGameDescriptor *findPlainGameDescriptor(const char *gameid, const PlainGameDescriptor *list) { @@ -34,93 +35,182 @@ const PlainGameDescriptor *findPlainGameDescriptor(const char *gameid, const Pla return 0; } -GameDescriptor::GameDescriptor() { - setVal("gameid", ""); - setVal("description", ""); +PlainGameDescriptor PlainGameDescriptor::empty() { + PlainGameDescriptor pgd; + pgd.gameId = nullptr; + pgd.description = nullptr; + return pgd; } -GameDescriptor::GameDescriptor(const PlainGameDescriptor &pgd, Common::String guioptions) { - setVal("gameid", pgd.gameId); - setVal("description", pgd.description); +PlainGameDescriptor PlainGameDescriptor::of(const char *gameId, const char *description) { + PlainGameDescriptor pgd; + pgd.gameId = gameId; + pgd.description = description; + return pgd; +} - if (!guioptions.empty()) - setVal("guioptions", Common::getGameGUIOptionsDescription(guioptions)); +DetectedGame::DetectedGame() : + engineName(nullptr), + hasUnknownFiles(false), + canBeAdded(true), + language(Common::UNK_LANG), + platform(Common::kPlatformUnknown), + gameSupportLevel(kStableGame) { } -GameDescriptor::GameDescriptor(const Common::String &g, const Common::String &d, Common::Language l, Common::Platform p, Common::String guioptions, GameSupportLevel gsl) { - setVal("gameid", g); - setVal("description", d); - if (l != Common::UNK_LANG) - setVal("language", Common::getLanguageCode(l)); - if (p != Common::kPlatformUnknown) - setVal("platform", Common::getPlatformCode(p)); - if (!guioptions.empty()) - setVal("guioptions", Common::getGameGUIOptionsDescription(guioptions)); - - setSupportLevel(gsl); +DetectedGame::DetectedGame(const PlainGameDescriptor &pgd) : + engineName(nullptr), + hasUnknownFiles(false), + canBeAdded(true), + language(Common::UNK_LANG), + platform(Common::kPlatformUnknown), + gameSupportLevel(kStableGame) { + + gameId = pgd.gameId; + preferredTarget = pgd.gameId; + description = pgd.description; } -void GameDescriptor::setGUIOptions(Common::String guioptions) { - if (!guioptions.empty()) - setVal("guioptions", Common::getGameGUIOptionsDescription(guioptions)); - else - erase("guioptions"); +DetectedGame::DetectedGame(const Common::String &id, const Common::String &d, Common::Language l, Common::Platform p, const Common::String &ex) : + engineName(nullptr), + hasUnknownFiles(false), + canBeAdded(true), + gameSupportLevel(kStableGame) { + + gameId = id; + preferredTarget = id; + description = d; + language = l; + platform = p; + extra = ex; + + // Append additional information, if set, to the description. + description += updateDesc(); } -void GameDescriptor::appendGUIOptions(const Common::String &str) { - setVal("guioptions", getVal("guioptions", "") + " " + str); +void DetectedGame::setGUIOptions(const Common::String &guioptions) { + _guiOptions = Common::getGameGUIOptionsDescription(guioptions); } -void GameDescriptor::updateDesc(const char *extra) { - const bool hasCustomLanguage = (language() != Common::UNK_LANG); - const bool hasCustomPlatform = (platform() != Common::kPlatformUnknown); - const bool hasExtraDesc = (extra && extra[0]); +void DetectedGame::appendGUIOptions(const Common::String &str) { + if (!_guiOptions.empty()) + _guiOptions += " "; + + _guiOptions += str; +} + +Common::String DetectedGame::updateDesc() const { + const bool hasCustomLanguage = (language != Common::UNK_LANG); + const bool hasCustomPlatform = (platform != Common::kPlatformUnknown); + const bool hasExtraDesc = !extra.empty(); // Adapt the description string if custom platform/language is set. - if (hasCustomLanguage || hasCustomPlatform || hasExtraDesc) { - Common::String descr = description(); + Common::String descr; + if (!hasCustomLanguage && !hasCustomPlatform && !hasExtraDesc) + return descr; - descr += " ("; + descr += " ("; + + if (hasExtraDesc) + descr += extra; + if (hasCustomPlatform) { if (hasExtraDesc) - descr += extra; - if (hasCustomPlatform) { - if (hasExtraDesc) - descr += "/"; - descr += Common::getPlatformDescription(platform()); - } - if (hasCustomLanguage) { - if (hasExtraDesc || hasCustomPlatform) - descr += "/"; - descr += Common::getLanguageDescription(language()); + descr += "/"; + descr += Common::getPlatformDescription(platform); + } + if (hasCustomLanguage) { + if (hasExtraDesc || hasCustomPlatform) + descr += "/"; + descr += Common::getLanguageDescription(language); + } + + descr += ")"; + + return descr; +} + +DetectionResults::DetectionResults(const DetectedGames &detectedGames) : + _detectedGames(detectedGames) { +} + +bool DetectionResults::foundUnknownGames() const { + for (uint i = 0; i < _detectedGames.size(); i++) { + if (_detectedGames[i].hasUnknownFiles) { + return true; } - descr += ")"; - setVal("description", descr); } + return false; } -GameSupportLevel GameDescriptor::getSupportLevel() { - GameSupportLevel gsl = kStableGame; - if (contains("gsl")) { - Common::String gslString = getVal("gsl"); - if (gslString.equals("unstable")) - gsl = kUnstableGame; - else if (gslString.equals("testing")) - gsl = kTestingGame; +DetectedGames DetectionResults::listRecognizedGames() { + DetectedGames candidates; + for (uint i = 0; i < _detectedGames.size(); i++) { + if (_detectedGames[i].canBeAdded) { + candidates.push_back(_detectedGames[i]); + } } - return gsl; + return candidates; } -void GameDescriptor::setSupportLevel(GameSupportLevel gsl) { - switch (gsl) { - case kUnstableGame: - setVal("gsl", "unstable"); - break; - case kTestingGame: - setVal("gsl", "testing"); - break; - case kStableGame: - // Fall Through intended - default: - erase("gsl"); +Common::String DetectionResults::generateUnknownGameReport(bool translate, uint32 wordwrapAt) const { + assert(!_detectedGames.empty()); + + const char *reportStart = _s("The game in '%s' seems to be an unknown game variant.\n\n" + "Please report the following data to the ScummVM team at %s " + "along with the name of the game you tried to add and " + "its version, language, etc.:"); + const char *reportEngineHeader = _s("Matched game IDs for the %s engine:"); + + Common::String report = Common::String::format( + translate ? _(reportStart) : reportStart, _detectedGames[0].path.c_str(), + "https://bugs.scummvm.org/" + ); + report += "\n"; + + FilePropertiesMap matchedFiles; + + const char *currentEngineName = nullptr; + for (uint i = 0; i < _detectedGames.size(); i++) { + const DetectedGame &game = _detectedGames[i]; + + if (!game.hasUnknownFiles) continue; + + if (!currentEngineName || strcmp(currentEngineName, game.engineName) != 0) { + currentEngineName = game.engineName; + + // If the engine is not the same as for the previous entry, print an engine line header + report += "\n"; + report += Common::String::format( + translate ? _(reportEngineHeader) : reportEngineHeader, + game.engineName + ); + report += " "; + + } else { + report += ", "; + } + + // Add the gameId to the list of matched games for the engine + // TODO: Use the gameId here instead of the preferred target. + // This is currently impossible due to the AD singleId feature losing the information. + report += game.preferredTarget; + + // Consolidate matched files across all engines and detection entries + for (FilePropertiesMap::const_iterator it = game.matchedFiles.begin(); it != game.matchedFiles.end(); it++) { + matchedFiles.setVal(it->_key, it->_value); + } + } + + if (wordwrapAt) { + report.wordWrap(wordwrapAt); } + + report += "\n\n"; + + for (FilePropertiesMap::const_iterator file = matchedFiles.begin(); file != matchedFiles.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"; + + return report; } diff --git a/engines/game.h b/engines/game.h index e01e5c6885..14f9962ce6 100644 --- a/engines/game.h +++ b/engines/game.h @@ -38,6 +38,9 @@ struct PlainGameDescriptor { const char *gameId; const char *description; + + static PlainGameDescriptor empty(); + static PlainGameDescriptor of(const char *gameId, const char *description); }; /** @@ -47,6 +50,18 @@ struct PlainGameDescriptor { */ const PlainGameDescriptor *findPlainGameDescriptor(const char *gameid, const PlainGameDescriptor *list); +class PlainGameList : public Common::Array<PlainGameDescriptor> { +public: + PlainGameList() {} + PlainGameList(const PlainGameList &list) : Common::Array<PlainGameDescriptor>(list) {} + PlainGameList(const PlainGameDescriptor *g) { + while (g->gameId) { + push_back(*g); + g++; + } + } +}; + /** * Ths is an enum to describe how done a game is. This also indicates what level of support is expected. */ @@ -56,63 +71,138 @@ enum GameSupportLevel { kUnstableGame // the game is not even ready for public testing yet }; + /** - * A hashmap describing details about a given game. In a sense this is a refined - * version of PlainGameDescriptor, as it also contains a gameid and a description string. - * But in addition, platform and language settings, as well as arbitrary other settings, - * can be contained in a GameDescriptor. - * This is an essential part of the glue between the game engines and the launcher code. + * A record describing the properties of a file. Used on the existing + * files while detecting a game. */ -class GameDescriptor : public Common::StringMap { -public: - GameDescriptor(); - GameDescriptor(const PlainGameDescriptor &pgd, Common::String guioptions = Common::String()); - GameDescriptor(const Common::String &gameid, - const Common::String &description, - Common::Language language = Common::UNK_LANG, - Common::Platform platform = Common::kPlatformUnknown, - Common::String guioptions = Common::String(), - GameSupportLevel gsl = kStableGame); +struct FileProperties { + int32 size; + Common::String md5; + + FileProperties() : size(-1) {} +}; + +/** + * A map of all relevant existing files while detecting. + */ +typedef Common::HashMap<Common::String, FileProperties, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> FilePropertiesMap; + +/** + * Details about a given game. + * + * While PlainGameDescriptor refers to a game supported by an engine, this refers to a game copy + * that has been detected by an engine's detector. + * It contains all the necessary data to add the game to the configuration manager and / or to launch it. + */ +struct DetectedGame { + DetectedGame(); + explicit DetectedGame(const PlainGameDescriptor &pgd); + DetectedGame(const Common::String &id, + const Common::String &description, + Common::Language language = Common::UNK_LANG, + Common::Platform platform = Common::kPlatformUnknown, + const Common::String &extra = Common::String()); + + void setGUIOptions(const Common::String &options); + void appendGUIOptions(const Common::String &str); + Common::String getGUIOptions() const { return _guiOptions; } /** - * Update the description string by appending (EXTRA/PLATFORM/LANG) to it. - * Values that are missing are omitted, so e.g. (EXTRA/LANG) would be - * added if no platform has been specified but a language and an extra string. + * The name of the engine supporting the detected game */ - void updateDesc(const char *extra = 0); + const char *engineName; - void setGUIOptions(Common::String options); - void appendGUIOptions(const Common::String &str); + /** + * A game was detected, but some files were not recognized + * + * This can happen when the md5 or size of the detected files did not match the engine's detection tables. + * When this is true, the list of matched files below contains detail about the unknown files. + * + * @see matchedFiles + */ + bool hasUnknownFiles; + + /** + * An optional list of the files that were used to match the game with the engine's detection tables + */ + FilePropertiesMap matchedFiles; + + /** + * This detection entry contains enough data to add the game to the configuration manager and launch it + * + * @see matchedGame + */ + bool canBeAdded; + + Common::String gameId; + Common::String preferredTarget; + Common::String description; + Common::Language language; + Common::Platform platform; + Common::String path; + Common::String extra; /** * What level of support is expected of this game */ - GameSupportLevel getSupportLevel(); - void setSupportLevel(GameSupportLevel gsl); - - Common::String &gameid() { return getVal("gameid"); } - Common::String &description() { return getVal("description"); } - const Common::String &gameid() const { return getVal("gameid"); } - const Common::String &description() const { return getVal("description"); } - Common::Language language() const { return contains("language") ? Common::parseLanguage(getVal("language")) : Common::UNK_LANG; } - Common::Platform platform() const { return contains("platform") ? Common::parsePlatform(getVal("platform")) : Common::kPlatformUnknown; } - - const Common::String &preferredtarget() const { - return contains("preferredtarget") ? getVal("preferredtarget") : getVal("gameid"); - } + GameSupportLevel gameSupportLevel; + +private: + /** + * Update the description string by appending (EXTRA/PLATFORM/LANG) to it. + * Values that are missing are omitted, so e.g. (EXTRA/LANG) would be + * added if no platform has been specified but a language and an extra string. + */ + Common::String updateDesc() const; + + Common::String _guiOptions; }; /** List of games. */ -class GameList : public Common::Array<GameDescriptor> { +typedef Common::Array<DetectedGame> DetectedGames; + +/** + * Contains a list of games found by the engines' detectors. + * + * Each detected game can either: + * - be fully recognized (e.g. an exact match was found in the detection tables of an engine) + * - be an unknown variant (e.g. a game using files with the same name was found in the detection tables) + * - be recognized with unknown files (e.g. the game was exactly not found in the detection tables, + * but the detector was able to gather enough data to allow launching the game) + * + * Practically, this means a detected game can be in both the recognized game list and in the unknown game + * report handled by this class. + */ +class DetectionResults { public: - GameList() {} - GameList(const GameList &list) : Common::Array<GameDescriptor>(list) {} - GameList(const PlainGameDescriptor *g) { - while (g->gameId) { - push_back(GameDescriptor(*g)); - g++; - } - } + explicit DetectionResults(const DetectedGames &detectedGames); + + /** + * List all the games that were recognized by the engines + * + * Recognized games can be added to the configuration manager and then launched. + */ + DetectedGames listRecognizedGames(); + + /** + * Were unknown game variants found by the engines? + * + * When unknown game variants are found, an unknown game report can be generated. + */ + bool foundUnknownGames() const; + + /** + * Generate a report that we found an unknown game variant, together with the file + * names, sizes and MD5 sums. + * + * @param translate translate the report to the currently active GUI language + * @param wordwrapAt word wrap the text part of the report after a number of characters + */ + Common::String generateUnknownGameReport(bool translate, uint32 wordwrapAt = 0) const; + +private: + DetectedGames _detectedGames; }; #endif diff --git a/engines/gnap/detection.cpp b/engines/gnap/detection.cpp index d19d420ff8..97c9128002 100644 --- a/engines/gnap/detection.cpp +++ b/engines/gnap/detection.cpp @@ -141,13 +141,8 @@ SaveStateList GnapMetaEngine::listSaves(const char *target) const { Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(*file); if (in) { - Gnap::GnapEngine::readSavegameHeader(in, header); - saveList.push_back(SaveStateDescriptor(slot, header._saveName)); - - if (header._thumbnail) { - header._thumbnail->free(); - delete header._thumbnail; - } + if (Gnap::GnapEngine::readSavegameHeader(in, header)) + saveList.push_back(SaveStateDescriptor(slot, header._saveName)); delete in; } } @@ -179,7 +174,11 @@ SaveStateDescriptor GnapMetaEngine::querySaveMetaInfos(const char *target, int s SaveStateDescriptor desc(slot, saveName); if (version != 1) { - Graphics::Surface *const thumbnail = Graphics::loadThumbnail(*file); + Graphics::Surface *thumbnail; + if (!Graphics::loadThumbnail(*file, thumbnail)) { + delete file; + return SaveStateDescriptor(); + } desc.setThumbnail(thumbnail); } diff --git a/engines/gnap/gnap.cpp b/engines/gnap/gnap.cpp index cf8710c64d..9b0015e646 100644 --- a/engines/gnap/gnap.cpp +++ b/engines/gnap/gnap.cpp @@ -979,7 +979,7 @@ int GnapEngine::playSoundC() { if (!_timers[_soundTimerIndexC]) { _timers[_soundTimerIndexC] = getRandom(50) + 150; - soundId = kSoundIdsC[getRandom(7)] ; + soundId = kSoundIdsC[getRandom(7)]; playSound(soundId | 0x10000, false); } return soundId; diff --git a/engines/gnap/gnap.h b/engines/gnap/gnap.h index dbefa31795..dd653304e7 100644 --- a/engines/gnap/gnap.h +++ b/engines/gnap/gnap.h @@ -319,7 +319,7 @@ public: Common::String generateSaveName(int slot); void synchronize(Common::Serializer &s); void writeSavegameHeader(Common::OutSaveFile *out, GnapSavegameHeader &header); - static bool readSavegameHeader(Common::InSaveFile *in, GnapSavegameHeader &header); + WARN_UNUSED_RESULT static bool readSavegameHeader(Common::InSaveFile *in, GnapSavegameHeader &header, bool skipThumbnail = true); void delayTicks(int val, int idx, bool updateCursor); void delayTicksA(int val, int idx); diff --git a/engines/gnap/menu.cpp b/engines/gnap/menu.cpp index 9606273b4c..34b5f18473 100644 --- a/engines/gnap/menu.cpp +++ b/engines/gnap/menu.cpp @@ -589,9 +589,8 @@ void GnapEngine::writeSavegameHeader(Common::OutSaveFile *out, GnapSavegameHeade out->writeSint16LE(td.tm_min); } -bool GnapEngine::readSavegameHeader(Common::InSaveFile *in, GnapSavegameHeader &header) { +WARN_UNUSED_RESULT bool GnapEngine::readSavegameHeader(Common::InSaveFile *in, GnapSavegameHeader &header, bool skipThumbnail) { char saveIdentBuffer[SAVEGAME_STR_SIZE + 1]; - header._thumbnail = nullptr; // Validate the header Id in->read(saveIdentBuffer, SAVEGAME_STR_SIZE + 1); @@ -612,9 +611,9 @@ bool GnapEngine::readSavegameHeader(Common::InSaveFile *in, GnapSavegameHeader & if (header._version == 1) header._thumbnail = nullptr; else { - header._thumbnail = Graphics::loadThumbnail(*in); - if (!header._thumbnail) + if (!Graphics::loadThumbnail(*in, header._thumbnail, skipThumbnail)) { return false; + } } // Read in save date/time diff --git a/engines/gob/detection/detection.cpp b/engines/gob/detection/detection.cpp index e204ced1e8..864a701aa6 100644 --- a/engines/gob/detection/detection.cpp +++ b/engines/gob/detection/detection.cpp @@ -33,9 +33,9 @@ class GobMetaEngine : public AdvancedMetaEngine { public: GobMetaEngine(); - virtual GameDescriptor findGame(const char *gameId) const; + PlainGameDescriptor findGame(const char *gameId) const override; - virtual const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const; + ADDetectedGame fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const override; virtual const char *getName() const; virtual const char *getOriginalCopyright() const; @@ -59,29 +59,26 @@ GobMetaEngine::GobMetaEngine() : _guiOptions = GUIO1(GUIO_NOLAUNCHLOAD); } -GameDescriptor GobMetaEngine::findGame(const char *gameId) const { +PlainGameDescriptor 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; +ADDetectedGame GobMetaEngine::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const { + ADDetectedGame detectedGame = detectGameFilebased(allFiles, fslist, Gob::fileBased); + if (!detectedGame.desc) { + return ADDetectedGame(); + } - const Gob::GOBGameDescription *game; - game = (const Gob::GOBGameDescription *)detectGameFilebased(allFiles, fslist, Gob::fileBased, &filesProps); - if (!game) - return 0; + const Gob::GOBGameDescription *game = (const Gob::GOBGameDescription *)detectedGame.desc; if (game->gameType == Gob::kGameTypeOnceUponATime) { game = detectOnceUponATime(fslist); - if (!game) - return 0; + if (game) { + detectedGame.desc = &game->desc; + } } - ADGameIdList gameIds; - gameIds.push_back(game->desc.gameId); - - reportUnknown(fslist.begin()->getParent(), filesProps, gameIds); - return (const ADGameDescription *)game; + return detectedGame; } const Gob::GOBGameDescription *GobMetaEngine::detectOnceUponATime(const Common::FSList &fslist) { diff --git a/engines/hopkins/computer.cpp b/engines/hopkins/computer.cpp index 16977e0cb8..aff3c914f6 100644 --- a/engines/hopkins/computer.cpp +++ b/engines/hopkins/computer.cpp @@ -918,9 +918,9 @@ void ComputerManager::getScoreName() { char score[16]; sprintf(score, "%d", _breakoutScore); int scoreLen = 0; - do + do { ++scoreLen; - while (score[scoreLen]); + } while (score[scoreLen]); for (int i = scoreLen - 1, scorePos = 8; i >= 0; i--) { _score[scoreLine]._score.setChar(score[i], scorePos--); diff --git a/engines/hopkins/detection.cpp b/engines/hopkins/detection.cpp index 041afecaa8..ebefee6278 100644 --- a/engines/hopkins/detection.cpp +++ b/engines/hopkins/detection.cpp @@ -168,9 +168,6 @@ SaveStateList HopkinsMetaEngine::listSaves(const char *target) const { if (in) { if (Hopkins::SaveLoadManager::readSavegameHeader(in, header)) { saveList.push_back(SaveStateDescriptor(slot, header._saveName)); - - header._thumbnail->free(); - delete header._thumbnail; } delete in; @@ -198,7 +195,11 @@ SaveStateDescriptor HopkinsMetaEngine::querySaveMetaInfos(const char *target, in if (f) { Hopkins::hopkinsSavegameHeader header; - Hopkins::SaveLoadManager::readSavegameHeader(f, header); + if (!Hopkins::SaveLoadManager::readSavegameHeader(f, header, false)) { + delete f; + return SaveStateDescriptor(); + } + delete f; // Create the return descriptor diff --git a/engines/hopkins/dialogs.cpp b/engines/hopkins/dialogs.cpp index fc613f892d..cabdc9e9ea 100644 --- a/engines/hopkins/dialogs.cpp +++ b/engines/hopkins/dialogs.cpp @@ -448,9 +448,9 @@ void DialogsManager::showInventory() { _vm->_script->_tempObjectFl = false; if (_vm->_soundMan->_voiceOffFl) { - do + do { _vm->_events->refreshScreenAndEvents(); - while (!_vm->_globals->_exitId && _vm->_events->getMouseButton() != 1); + } while (!_vm->_globals->_exitId && _vm->_events->getMouseButton() != 1); _vm->_fontMan->hideText(9); } if (_vm->_globals->_exitId) { @@ -692,7 +692,7 @@ void DialogsManager::showSaveLoad(SaveLoadMode mode) { for (int slotNumber = 1; slotNumber <= 6; ++slotNumber) { hopkinsSavegameHeader header; - if (_vm->_saveLoad->readSavegameHeader(slotNumber, header)) { + if (_vm->_saveLoad->readSavegameHeader(slotNumber, header, false)) { Graphics::Surface thumb8; _vm->_saveLoad->convertThumb16To8(header._thumbnail, &thumb8); diff --git a/engines/hopkins/font.cpp b/engines/hopkins/font.cpp index 7d57f564a9..9d11c5a940 100644 --- a/engines/hopkins/font.cpp +++ b/engines/hopkins/font.cpp @@ -281,9 +281,9 @@ void FontManager::box(int idx, int messageId, const Common::String &filename, in int ptrb = _boxWidth - 4; for (;;) { lineSize = curLineSize; - do + do { curChar = _tempText[tempTextIdx + curLineSize++]; - while (curChar != ' ' && curChar != '%'); + } while (curChar != ' ' && curChar != '%'); if (curLineSize >= ptrb / _fontFixedWidth) { if (curChar == '%') curChar = ' '; diff --git a/engines/hopkins/hopkins.cpp b/engines/hopkins/hopkins.cpp index f28de39827..dd64c0cc71 100644 --- a/engines/hopkins/hopkins.cpp +++ b/engines/hopkins/hopkins.cpp @@ -314,9 +314,9 @@ bool HopkinsEngine::runWin95Demo() { _graphicsMan->loadImage("ENDUK"); _graphicsMan->fadeInLong(); _events->mouseOn(); - do + do { _events->refreshScreenAndEvents(); - while (_events->getMouseButton() != 1); + } while (_events->getMouseButton() != 1); _graphicsMan->fadeOutLong(); restoreSystem(); } else @@ -2047,9 +2047,9 @@ void HopkinsEngine::playUnderwaterBaseCutscene() { _graphicsMan->fadeInLong(); _objectsMan->enableHidingBehavior(); - do + do { _events->refreshScreenAndEvents(); - while (!shouldQuit() && _objectsMan->getBobAnimDataIdx(8) != 22); + } while (!shouldQuit() && _objectsMan->getBobAnimDataIdx(8) != 22); if (!shouldQuit()) { _graphicsMan->fadeOutLong(); @@ -2092,9 +2092,9 @@ void HopkinsEngine::playEnding() { _graphicsMan->fadeInLong(); _globals->_eventMode = EVENTMODE_IGNORE; - do + do { _events->refreshScreenAndEvents(); - while (_objectsMan->getBobAnimDataIdx(6) != 54); + } while (_objectsMan->getBobAnimDataIdx(6) != 54); _globals->_introSpeechOffFl = true; _talkMan->startAnimatedCharacterDialogue("GM4.PE2"); @@ -2104,38 +2104,38 @@ void HopkinsEngine::playEnding() { _objectsMan->setBobAnimation(9); _objectsMan->setBobAnimation(7); - do + do { _events->refreshScreenAndEvents(); - while (_objectsMan->getBobAnimDataIdx(7) != 54); + } while (_objectsMan->getBobAnimDataIdx(7) != 54); _soundMan->playSample(1); - do + do { _events->refreshScreenAndEvents(); - while (_objectsMan->getBobAnimDataIdx(7) != 65); + } while (_objectsMan->getBobAnimDataIdx(7) != 65); _globals->_introSpeechOffFl = true; _talkMan->startAnimatedCharacterDialogue("DUELB4.PE2"); _events->mouseOff(); _globals->_disableInventFl = true; - do + do { _events->refreshScreenAndEvents(); - while (_objectsMan->getBobAnimDataIdx(7) != 72); + } while (_objectsMan->getBobAnimDataIdx(7) != 72); _globals->_introSpeechOffFl = true; _talkMan->startAnimatedCharacterDialogue("DUELH1.PE2"); - do + do { _events->refreshScreenAndEvents(); - while (_objectsMan->getBobAnimDataIdx(7) != 81); + } while (_objectsMan->getBobAnimDataIdx(7) != 81); _globals->_introSpeechOffFl = true; _talkMan->startAnimatedCharacterDialogue("DUELB5.PE2"); - do + do { _events->refreshScreenAndEvents(); - while (_objectsMan->getBobAnimDataIdx(7) != 120); + } while (_objectsMan->getBobAnimDataIdx(7) != 120); _objectsMan->stopBobAnimation(7); if (_globals->_saveData->_data[svGameWonFl] == 1) { @@ -2150,9 +2150,9 @@ void HopkinsEngine::playEnding() { _events->_rateCounter = 0; if (!_events->_escKeyFl) { - do + do { _events->refreshEvents(); - while (_events->_rateCounter < 2000 / _globals->_speed && !_events->_escKeyFl); + } while (_events->_rateCounter < 2000 / _globals->_speed && !_events->_escKeyFl); } _events->_escKeyFl = false; _graphicsMan->fadeOutLong(); @@ -2184,15 +2184,15 @@ void HopkinsEngine::playEnding() { _talkMan->startAnimatedCharacterDialogue("GM5.PE2"); _globals->_disableInventFl = true; - do + do { _events->refreshScreenAndEvents(); - while (_objectsMan->getBobAnimDataIdx(8) != 5); + } while (_objectsMan->getBobAnimDataIdx(8) != 5); _soundMan->directPlayWav("SOUND41.WAV"); - do + do { _events->refreshScreenAndEvents(); - while (_objectsMan->getBobAnimDataIdx(8) != 21); + } while (_objectsMan->getBobAnimDataIdx(8) != 21); _graphicsMan->fadeOutLong(); _graphicsMan->endDisplayBob(); diff --git a/engines/hopkins/objects.cpp b/engines/hopkins/objects.cpp index b54b21bbc9..d67cd7b60c 100644 --- a/engines/hopkins/objects.cpp +++ b/engines/hopkins/objects.cpp @@ -2761,9 +2761,9 @@ void ObjectsManager::handleSpecialGames() { break; _vm->_globals->_disableInventFl = true; - do + do { _vm->_events->refreshScreenAndEvents(); - while (getBobAnimDataIdx(8) != 3); + } while (getBobAnimDataIdx(8) != 3); _vm->_globals->_introSpeechOffFl = true; _vm->_talkMan->startAnimatedCharacterDialogue("GM3.PE2"); stopBobAnimation(8); diff --git a/engines/hopkins/saveload.cpp b/engines/hopkins/saveload.cpp index 05c7fb8119..35a80458ff 100644 --- a/engines/hopkins/saveload.cpp +++ b/engines/hopkins/saveload.cpp @@ -77,9 +77,8 @@ void SaveLoadManager::load(const Common::String &file, byte *buf) { delete savefile; } -bool SaveLoadManager::readSavegameHeader(Common::InSaveFile *in, hopkinsSavegameHeader &header) { +WARN_UNUSED_RESULT bool SaveLoadManager::readSavegameHeader(Common::InSaveFile *in, hopkinsSavegameHeader &header, bool skipThumbnail) { char saveIdentBuffer[SAVEGAME_STR_SIZE + 1]; - header._thumbnail = NULL; // Validate the header Id in->read(saveIdentBuffer, SAVEGAME_STR_SIZE + 1); @@ -96,9 +95,9 @@ bool SaveLoadManager::readSavegameHeader(Common::InSaveFile *in, hopkinsSavegame while ((ch = (char)in->readByte()) != '\0') header._saveName += ch; // Get the thumbnail - header._thumbnail = Graphics::loadThumbnail(*in); - if (!header._thumbnail) + if (!Graphics::loadThumbnail(*in, header._thumbnail, skipThumbnail)) { return false; + } // Read in save date/time header._year = in->readSint16LE(); @@ -186,10 +185,10 @@ Common::Error SaveLoadManager::loadGame(int slot) { // Read in the savegame header hopkinsSavegameHeader header; - readSavegameHeader(savefile, header); - if (header._thumbnail) - header._thumbnail->free(); - delete header._thumbnail; + if (!readSavegameHeader(savefile, header)) { + delete savefile; + return Common::kReadingFailed; + } // Read in the savegame data syncSavegameData(serializer, header._version); @@ -212,14 +211,14 @@ Common::Error SaveLoadManager::loadGame(int slot) { return Common::kNoError; } -bool SaveLoadManager::readSavegameHeader(int slot, hopkinsSavegameHeader &header) { +WARN_UNUSED_RESULT bool SaveLoadManager::readSavegameHeader(int slot, hopkinsSavegameHeader &header, bool skipThumbnail) { // Try and open the save file for reading Common::InSaveFile *savefile = g_system->getSavefileManager()->openForLoading( _vm->generateSaveName(slot)); if (!savefile) return false; - bool result = readSavegameHeader(savefile, header); + bool result = readSavegameHeader(savefile, header, skipThumbnail); delete savefile; return result; } diff --git a/engines/hopkins/saveload.h b/engines/hopkins/saveload.h index 7b4ec307f5..33234f49fa 100644 --- a/engines/hopkins/saveload.h +++ b/engines/hopkins/saveload.h @@ -61,9 +61,9 @@ public: bool saveFile(const Common::String &file, const void *buf, size_t n); void load(const Common::String &file, byte *buf); - static bool readSavegameHeader(Common::InSaveFile *in, hopkinsSavegameHeader &header); + WARN_UNUSED_RESULT static bool readSavegameHeader(Common::InSaveFile *in, hopkinsSavegameHeader &header, bool skipThumbnail = true); void writeSavegameHeader(Common::OutSaveFile *out, hopkinsSavegameHeader &header); - bool readSavegameHeader(int slot, hopkinsSavegameHeader &header); + WARN_UNUSED_RESULT bool readSavegameHeader(int slot, hopkinsSavegameHeader &header, bool skipThumbnail = true); Common::Error saveGame(int slot, const Common::String &saveName); Common::Error loadGame(int slot); diff --git a/engines/hopkins/talk.cpp b/engines/hopkins/talk.cpp index 00c4ab0332..f3cd6bd026 100644 --- a/engines/hopkins/talk.cpp +++ b/engines/hopkins/talk.cpp @@ -120,9 +120,9 @@ void TalkManager::startAnimatedCharacterDialogue(const Common::String &filename) if (_vm->_globals->_introSpeechOffFl) { int idx = 1; int answer; - do + do { answer = dialogAnswer(idx++, false); - while (answer != -1); + } while (answer != -1); } clearCharacterAnim(); _vm->_globals->_introSpeechOffFl = false; @@ -208,9 +208,9 @@ void TalkManager::startStaticCharacterDialogue(const Common::String &filename) { if (_vm->_globals->_introSpeechOffFl) { int idx = 1; int answer; - do + do { answer = dialogAnswer(idx++, true); - while (answer != -1); + } while (answer != -1); } _characterBuffer = _vm->_globals->freeMemory(_characterBuffer); @@ -879,9 +879,9 @@ void TalkManager::handleForestAnswser(int zone, int verb) { _vm->_objectsMan->setBobAnimation(6); _vm->_soundMan->playSample(1); _vm->_objectsMan->showSpecialActionAnimation(_vm->_objectsMan->_forestSprite, "13,14,15,14,13,12,13,14,15,16,-1,", 4); - do + do { _vm->_events->refreshScreenAndEvents(); - while (_vm->_objectsMan->getBobAnimDataIdx(6) < 12); + } while (_vm->_objectsMan->getBobAnimDataIdx(6) < 12); _vm->_objectsMan->stopBobAnimation(6); _vm->_objectsMan->setBobAnimation(8); @@ -927,9 +927,9 @@ void TalkManager::handleForestAnswser(int zone, int verb) { _vm->_objectsMan->setBobAnimation(5); _vm->_soundMan->playSample(1); _vm->_objectsMan->showSpecialActionAnimation(_vm->_objectsMan->_forestSprite, "13,14,15,14,13,12,13,14,15,16,-1,", 4); - do + do { _vm->_events->refreshScreenAndEvents(); - while (_vm->_objectsMan->getBobAnimDataIdx(5) < 12); + } while (_vm->_objectsMan->getBobAnimDataIdx(5) < 12); _vm->_objectsMan->stopBobAnimation(5); _vm->_objectsMan->setBobAnimation(7); switch (_vm->_globals->_screenId) { diff --git a/engines/hugo/detection.cpp b/engines/hugo/detection.cpp index 4e4746c002..6d2fec5421 100644 --- a/engines/hugo/detection.cpp +++ b/engines/hugo/detection.cpp @@ -241,7 +241,12 @@ SaveStateDescriptor HugoMetaEngine::querySaveMetaInfos(const char *target, int s SaveStateDescriptor desc(slot, saveName); - Graphics::Surface *const thumbnail = Graphics::loadThumbnail(*file); + Graphics::Surface *thumbnail; + if (!Graphics::loadThumbnail(*file, thumbnail)) { + warning("Missing or broken savegame thumbnail"); + delete file; + return SaveStateDescriptor(); + } desc.setThumbnail(thumbnail); uint32 saveDate = file->readUint32BE(); diff --git a/engines/hugo/hugo.cpp b/engines/hugo/hugo.cpp index 5f6892bd20..ed82c04d7d 100644 --- a/engines/hugo/hugo.cpp +++ b/engines/hugo/hugo.cpp @@ -638,7 +638,6 @@ void HugoEngine::initialize() { calcMaxScore(); // Initialize maxscore _rnd = new Common::RandomSource("hugo"); - _rnd->setSeed(42); // Kick random number generator switch (_gameVariant) { case kGameVariantH1Dos: diff --git a/engines/kyra/detection.cpp b/engines/kyra/detection.cpp index 70c7e7c93c..79e1d9f494 100644 --- a/engines/kyra/detection.cpp +++ b/engines/kyra/detection.cpp @@ -256,7 +256,7 @@ SaveStateList KyraMetaEngine::listSaves(const char *target) const { if (slotNum >= 0 && slotNum <= 999) { Common::InSaveFile *in = saveFileMan->openForLoading(*file); if (in) { - if (Kyra::KyraEngine_v1::readSaveHeader(in, false, header) == Kyra::KyraEngine_v1::kRSHENoError) { + if (Kyra::KyraEngine_v1::readSaveHeader(in, header) == Kyra::KyraEngine_v1::kRSHENoError) { // WORKAROUND: Old savegames are using 'German' as description for kyra3 restart game save (slot 0), // since that looks odd we replace it by "New Game". if (slotNum == 0 && header.gameID == Kyra::GI_KYRA3) @@ -298,7 +298,7 @@ SaveStateDescriptor KyraMetaEngine::querySaveMetaInfos(const char *target, int s Kyra::KyraEngine_v1::SaveHeader header; Kyra::KyraEngine_v1::ReadSaveHeaderError error; - error = Kyra::KyraEngine_v1::readSaveHeader(in, true, header); + error = Kyra::KyraEngine_v1::readSaveHeader(in, header, false); delete in; if (error == Kyra::KyraEngine_v1::kRSHENoError) { diff --git a/engines/kyra/eobcommon.h b/engines/kyra/eobcommon.h index 1401d59dae..d214da0944 100644 --- a/engines/kyra/eobcommon.h +++ b/engines/kyra/eobcommon.h @@ -885,7 +885,7 @@ protected: void inflictMonsterDamage(EoBMonsterInPlay *m, int damage, bool giveExperience); void calcAndInflictMonsterDamage(EoBMonsterInPlay *m, int times, int pips, int offs, int flags, int savingThrowType, int savingThrowEffect); void calcAndInflictCharacterDamage(int charIndex, int times, int itemOrPips, int useStrModifierOrBase, int flags, int savingThrowType, int savingThrowEffect); - int calcCharacterDamage(int charIndex, int times, int itemOrPips, int useStrModifierOrBase, int flags, int savingThrowType, int damageType) ; + int calcCharacterDamage(int charIndex, int times, int itemOrPips, int useStrModifierOrBase, int flags, int savingThrowType, int damageType); void inflictCharacterDamage(int charIndex, int damage); bool characterAttackHitTest(int charIndex, int monsterIndex, int item, int attackType); diff --git a/engines/kyra/kyra_v1.h b/engines/kyra/kyra_v1.h index 4de7494510..bbbd59a4b8 100644 --- a/engines/kyra/kyra_v1.h +++ b/engines/kyra/kyra_v1.h @@ -416,7 +416,7 @@ protected: kRSHEIoError = 3 }; - static ReadSaveHeaderError readSaveHeader(Common::SeekableReadStream *file, bool loadThumbnail, SaveHeader &header); + WARN_UNUSED_RESULT static ReadSaveHeaderError readSaveHeader(Common::SeekableReadStream *file, SaveHeader &header, bool skipThumbnail = true); void loadGameStateCheck(int slot); virtual Common::Error loadGameState(int slot) = 0; diff --git a/engines/kyra/saveload.cpp b/engines/kyra/saveload.cpp index b44850f5c9..c306d6cb5d 100644 --- a/engines/kyra/saveload.cpp +++ b/engines/kyra/saveload.cpp @@ -37,12 +37,11 @@ namespace Kyra { -KyraEngine_v1::ReadSaveHeaderError KyraEngine_v1::readSaveHeader(Common::SeekableReadStream *in, bool loadThumbnail, SaveHeader &header) { +WARN_UNUSED_RESULT KyraEngine_v1::ReadSaveHeaderError KyraEngine_v1::readSaveHeader(Common::SeekableReadStream *in, SaveHeader &header, bool skipThumbnail) { uint32 type = in->readUint32BE(); header.originalSave = false; header.oldHeader = false; header.flags = 0; - header.thumbnail = 0; if (type == MKTAG('K', 'Y', 'R', 'A') || type == MKTAG('A', 'R', 'Y', 'K')) { // old Kyra1 header ID header.gameID = GI_KYRA1; @@ -125,10 +124,8 @@ KyraEngine_v1::ReadSaveHeaderError KyraEngine_v1::readSaveHeader(Common::Seekabl header.flags = in->readUint32BE(); if (header.version >= 14) { - if (loadThumbnail) { - header.thumbnail = Graphics::loadThumbnail(*in); - } else { - Graphics::skipThumbnail(*in); + if (!Graphics::loadThumbnail(*in, header.thumbnail, skipThumbnail)) { + return kRSHEIoError; } } @@ -140,7 +137,7 @@ Common::SeekableReadStream *KyraEngine_v1::openSaveForReading(const char *filena if (!(in = _saveFileMan->openForLoading(filename))) return 0; - ReadSaveHeaderError errorCode = KyraEngine_v1::readSaveHeader(in, false, header); + ReadSaveHeaderError errorCode = KyraEngine_v1::readSaveHeader(in, header); if (errorCode != kRSHENoError) { if (errorCode == kRSHEInvalidType) warning("No ScummVM Kyra engine savefile header"); diff --git a/engines/kyra/screen_eob.cpp b/engines/kyra/screen_eob.cpp index 6c1bd572e1..3a9a647fa5 100644 --- a/engines/kyra/screen_eob.cpp +++ b/engines/kyra/screen_eob.cpp @@ -499,7 +499,7 @@ void Screen_EoB::drawShape(uint8 pageNum, const uint8 *shapeData, int x, int y, int16 dX = x - (_dsX1 << 3); int16 dY = y; int16 dW = _dsX2 - _dsX1; - uint8 pixelsPerByte = *src++ ; + uint8 pixelsPerByte = *src++; uint16 dH = *src++; uint16 width = (*src++) << 3; @@ -1211,7 +1211,7 @@ void Screen_EoB::createFadeTable(uint8 *palData, uint8 *dst, uint8 rootColor, ui if (t <= v && (ii == rootColor || ii != i)) { v = t; - col = ii ; + col = ii; } } *dst++ = col; diff --git a/engines/kyra/sequences_darkmoon.cpp b/engines/kyra/sequences_darkmoon.cpp index 53a30d0079..68d6f752e0 100644 --- a/engines/kyra/sequences_darkmoon.cpp +++ b/engines/kyra/sequences_darkmoon.cpp @@ -876,7 +876,7 @@ void DarkMoonEngine::seq_playCredits(DarkmoonSequenceHelper *sq, const uint8 *da const uint8 *shp = sq->_shapes[(*++posOld) - 1]; items[i + 1].data = shp; items[i + 1].size = shp[1]; - items[i + 1].x = (dm->w - shp[2]) << 2 ; + items[i + 1].x = (dm->w - shp[2]) << 2; items[i + 1].dataType = 1; delete[] items[i + 1].str; items[i + 1].str = 0; diff --git a/engines/kyra/sequences_hof.cpp b/engines/kyra/sequences_hof.cpp index 942cf16502..7ee1ad7e4b 100644 --- a/engines/kyra/sequences_hof.cpp +++ b/engines/kyra/sequences_hof.cpp @@ -3378,7 +3378,7 @@ void KyraEngine_HoF::seq_showStarcraftLogo() { int KyraEngine_HoF::seq_playIntro() { bool startupSaveLoadable = saveFileLoadable(0); - return SeqPlayer_HOF(this, _screen, _system, startupSaveLoadable).play(kSequenceVirgin, startupSaveLoadable? kSequenceTitle : kSequenceNoLooping); + return SeqPlayer_HOF(this, _screen, _system, startupSaveLoadable).play(kSequenceVirgin, startupSaveLoadable ? kSequenceTitle : kSequenceNoLooping); } int KyraEngine_HoF::seq_playOutro() { diff --git a/engines/kyra/sequences_lok.cpp b/engines/kyra/sequences_lok.cpp index 58484c2fd9..a8091c9d41 100644 --- a/engines/kyra/sequences_lok.cpp +++ b/engines/kyra/sequences_lok.cpp @@ -785,6 +785,8 @@ void KyraEngine_LoK::seq_dispelMagicAnimation() { return; } _screen->hideMouse(); + // TODO + // This condition is always false. Is this a typo or a bug in the original? if (_currentCharacter->sceneId == 210 && _currentCharacter->sceneId < 160) _currentCharacter->facing = 3; if (_malcolmFlag == 7 && _beadStateVar == 3) { diff --git a/engines/kyra/sound_digital.cpp b/engines/kyra/sound_digital.cpp index a1600f6464..551d79cc55 100644 --- a/engines/kyra/sound_digital.cpp +++ b/engines/kyra/sound_digital.cpp @@ -30,6 +30,8 @@ #include "audio/decoders/vorbis.h" #include "audio/decoders/flac.h" +#include "common/util.h" + namespace Kyra { class KyraAudioStream : public Audio::SeekableAudioStream { @@ -203,11 +205,7 @@ int AUDStream::readBuffer(int16 *buffer, const int numSamples) { } inline int16 clip8BitSample(int16 sample) { - if (sample > 255) - return 255; - if (sample < 0) - return 0; - return sample; + return CLIP<int16>(sample, 0, 255); } int AUDStream::readChunk(int16 *buffer, const int maxSamples) { diff --git a/engines/kyra/sound_intern.h b/engines/kyra/sound_intern.h index 4b77bf1351..3559133cb1 100644 --- a/engines/kyra/sound_intern.h +++ b/engines/kyra/sound_intern.h @@ -169,24 +169,24 @@ public: SoundPC98(KyraEngine_v1 *vm, Audio::Mixer *mixer); virtual ~SoundPC98(); - virtual kType getMusicType() const { return kPC98; } + virtual kType getMusicType() const override { return kPC98; } - virtual bool init(); + virtual bool init() override; - virtual void initAudioResourceInfo(int set, void *info); - virtual void selectAudioResourceSet(int set); - virtual bool hasSoundFile(uint file) const; - virtual void loadSoundFile(uint file); - virtual void loadSoundFile(Common::String file); + virtual void initAudioResourceInfo(int set, void *info) override; + virtual void selectAudioResourceSet(int set) override; + virtual bool hasSoundFile(uint file) const override; + virtual void loadSoundFile(uint file) override; + virtual void loadSoundFile(Common::String file) override; - virtual void playTrack(uint8 track); - virtual void haltTrack(); - virtual void beginFadeOut(); + virtual void playTrack(uint8 track) override; + virtual void haltTrack() override; + virtual void beginFadeOut() override; virtual int32 voicePlay(const char *file, Audio::SoundHandle *handle, uint8 volume, uint8 priority, bool isSfx) override { return -1; } - virtual void playSoundEffect(uint8 track, uint8 volume = 0xFF); + virtual void playSoundEffect(uint8 track, uint8 volume = 0xFF) override; - virtual void updateVolumeSettings(); + virtual void updateVolumeSettings() override; private: int _lastTrack; @@ -204,25 +204,25 @@ public: SoundTownsPC98_v2(KyraEngine_v1 *vm, Audio::Mixer *mixer); virtual ~SoundTownsPC98_v2(); - virtual kType getMusicType() const { return _vm->gameFlags().platform == Common::kPlatformFMTowns ? kTowns : kPC98; } + virtual kType getMusicType() const override { return _vm->gameFlags().platform == Common::kPlatformFMTowns ? kTowns : kPC98; } - virtual bool init(); - virtual void process(); + virtual bool init() override; + virtual void process() override; - virtual void initAudioResourceInfo(int set, void *info); - virtual void selectAudioResourceSet(int set); - virtual bool hasSoundFile(uint file) const; - virtual void loadSoundFile(uint file) {} - virtual void loadSoundFile(Common::String file); + virtual void initAudioResourceInfo(int set, void *info) override; + virtual void selectAudioResourceSet(int set) override; + virtual bool hasSoundFile(uint file) const override; + virtual void loadSoundFile(uint file) override {} + virtual void loadSoundFile(Common::String file) override; - virtual void playTrack(uint8 track); - virtual void haltTrack(); - virtual void beginFadeOut(); + virtual void playTrack(uint8 track) override; + virtual void haltTrack() override; + virtual void beginFadeOut() override; virtual int32 voicePlay(const char *file, Audio::SoundHandle *handle, uint8 volume = 255, uint8 priority = 255, bool isSfx = true) override; - virtual void playSoundEffect(uint8 track, uint8 volume = 0xFF); + virtual void playSoundEffect(uint8 track, uint8 volume = 0xFF) override; - virtual void updateVolumeSettings(); + virtual void updateVolumeSettings() override; private: Audio::AudioStream *_currentSFX; @@ -320,22 +320,22 @@ public: SoundAmiga(KyraEngine_v1 *vm, Audio::Mixer *mixer); virtual ~SoundAmiga(); - virtual kType getMusicType() const { return kAmiga; } //FIXME + virtual kType getMusicType() const override { return kAmiga; } //FIXME - virtual bool init(); + virtual bool init() override; - virtual void initAudioResourceInfo(int set, void *info); - virtual void selectAudioResourceSet(int set); - virtual bool hasSoundFile(uint file) const; - virtual void loadSoundFile(uint file); - virtual void loadSoundFile(Common::String) {} + virtual void initAudioResourceInfo(int set, void *info) override; + virtual void selectAudioResourceSet(int set) override; + virtual bool hasSoundFile(uint file) const override; + virtual void loadSoundFile(uint file) override; + virtual void loadSoundFile(Common::String) override {} - virtual void playTrack(uint8 track); - virtual void haltTrack(); - virtual void beginFadeOut(); + virtual void playTrack(uint8 track) override; + virtual void haltTrack() override; + virtual void beginFadeOut() override; virtual int32 voicePlay(const char *file, Audio::SoundHandle *handle, uint8 volume, uint8 priority, bool isSfx) override { return -1; } - virtual void playSoundEffect(uint8 track, uint8 volume = 0xFF); + virtual void playSoundEffect(uint8 track, uint8 volume = 0xFF) override; protected: Audio::MaxTrax *_driver; diff --git a/engines/lab/lab.h b/engines/lab/lab.h index 2a1e527098..aedf0181ec 100644 --- a/engines/lab/lab.h +++ b/engines/lab/lab.h @@ -502,7 +502,7 @@ private: void handleTrialWarning(); }; -bool readSaveGameHeader(Common::InSaveFile *in, SaveGameHeader &header); +WARN_UNUSED_RESULT bool readSaveGameHeader(Common::InSaveFile *in, SaveGameHeader &header, bool skipThumbnail = true); } // End of namespace Lab diff --git a/engines/lab/map.cpp b/engines/lab/map.cpp index 057cac3589..a856b76885 100644 --- a/engines/lab/map.cpp +++ b/engines/lab/map.cpp @@ -367,13 +367,11 @@ void LabEngine::drawMap(uint16 curRoom, uint16 curMsg, uint16 floorNum, bool fad _imgHugeMaze->drawImage(_utils->mapScaleX(524), _utils->mapScaleY(97)); } else if (floorNum == kFloorSurMaze) { Common::Rect textRect = Common::Rect(_utils->mapScaleX(360), 0, _utils->mapScaleX(660), _utils->mapScaleY(450)); - const char *textPtr = _resource->getStaticText(kTextSurmazeMessage).c_str(); - _graphics->flowText(_msgFont, 0, 7, 0, true, true, true, true, textRect, textPtr); + _graphics->flowText(_msgFont, 0, 7, 0, true, true, true, true, textRect, _resource->getStaticText(kTextSurmazeMessage).c_str()); } if ((floorNum >= kFloorLower) && (floorNum <= kFloorCarnival)) { - const char *textPrt = _resource->getStaticText(floorNum - 1).c_str(); - _graphics->flowText(_msgFont, 0, 5, 3, true, true, true, true, _utils->vgaRectScale(14, 75, 134, 97), textPrt); + _graphics->flowText(_msgFont, 0, 5, 3, true, true, true, true, _utils->vgaRectScale(14, 75, 134, 97), _resource->getStaticText(floorNum - 1).c_str()); } if (!_rooms[curMsg]._roomMsg.empty()) diff --git a/engines/lab/savegame.cpp b/engines/lab/savegame.cpp index 656595e3e5..46ef1486f0 100644 --- a/engines/lab/savegame.cpp +++ b/engines/lab/savegame.cpp @@ -76,7 +76,7 @@ void LabEngine::writeSaveGameHeader(Common::OutSaveFile *out, const Common::Stri out->writeUint32BE(playTime); } -bool readSaveGameHeader(Common::InSaveFile *in, SaveGameHeader &header) { +WARN_UNUSED_RESULT bool readSaveGameHeader(Common::InSaveFile *in, SaveGameHeader &header, bool skipThumbnail) { uint32 id = in->readUint32BE(); // Check if it's a valid ScummVM savegame @@ -98,7 +98,11 @@ bool readSaveGameHeader(Common::InSaveFile *in, SaveGameHeader &header) { header._descr.setDescription(saveName); // Get the thumbnail - header._descr.setThumbnail(Graphics::loadThumbnail(*in)); + Graphics::Surface *thumbnail; + if (!Graphics::loadThumbnail(*in, thumbnail, skipThumbnail)) { + return false; + } + header._descr.setThumbnail(thumbnail); uint32 saveDate = in->readUint32BE(); uint16 saveTime = in->readUint16BE(); @@ -174,7 +178,11 @@ bool LabEngine::loadGame(int slot) { return false; SaveGameHeader header; - readSaveGameHeader(file, header); + if (!readSaveGameHeader(file, header)) { + delete file; + return false; + } + _roomNum = file->readUint16LE(); _music->checkRoomMusic(1, _roomNum); _direction = file->readUint16LE(); diff --git a/engines/lastexpress/menu/menu.cpp b/engines/lastexpress/menu/menu.cpp index cbd2a4a819..3a17cec6df 100644 --- a/engines/lastexpress/menu/menu.cpp +++ b/engines/lastexpress/menu/menu.cpp @@ -434,7 +434,7 @@ bool Menu::handleEvent(StartMenuAction action, Common::EventType type) { case kMenuCase4: if (clicked) _index = 0; - // fall down to kMenuContinue + // fall through ////////////////////////////////////////////////////////////////////////// case kMenuContinue: { diff --git a/engines/lilliput/configure.engine b/engines/lilliput/configure.engine new file mode 100644 index 0000000000..0b912f7dc8 --- /dev/null +++ b/engines/lilliput/configure.engine @@ -0,0 +1,3 @@ +# This file is included from the main "configure" script +# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] +add_engine lilliput "Lilliput" no diff --git a/engines/sludge/transition.h b/engines/lilliput/console.cpp index 5ce556870d..e7746a01bb 100644 --- a/engines/sludge/transition.h +++ b/engines/lilliput/console.cpp @@ -8,25 +8,27 @@ * 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 SLUDGE_TRANSITION_H -#define SLUDGE_TRANSITION_H -namespace Sludge { +#include "lilliput/console.h" +#include "lilliput/lilliput.h" + +namespace Lilliput { -void fixBrightness(); -void resetRandW(); +LilliputConsole::LilliputConsole(LilliputEngine *vm) : GUI::Debugger(), _vm(vm) { +} -} // End of namespace Sludge +LilliputConsole::~LilliputConsole() { +} -#endif +} // End of namespace Lilliput diff --git a/engines/lilliput/console.h b/engines/lilliput/console.h new file mode 100644 index 0000000000..cd7edb3f42 --- /dev/null +++ b/engines/lilliput/console.h @@ -0,0 +1,49 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef LILLIPUT_CONSOLE_H +#define LILLIPUT_CONSOLE_H + +#include "gui/debugger.h" + +namespace Lilliput { + +class LilliputEngine; + +class LilliputConsole : public GUI::Debugger { +public: + LilliputConsole(LilliputEngine *vm); + virtual ~LilliputConsole(void); + +private: + LilliputEngine *_vm; + bool Cmd_listScreens(int argc, const char **argv); + bool Cmd_listObjects(int argc, const char **argv); + bool Cmd_getObject(int argc, const char **argv); + bool Cmd_getAllObjects(int argc, const char **argv); + bool Cmd_gotoScreen(int argc, const char **argv); + bool Cmd_boundaries(int argc, const char **argv); +}; + +} // End of namespace Lilliput + +#endif diff --git a/engines/lilliput/detection.cpp b/engines/lilliput/detection.cpp new file mode 100644 index 0000000000..eb65ad2d64 --- /dev/null +++ b/engines/lilliput/detection.cpp @@ -0,0 +1,292 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can 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 "engines/advancedDetector.h" +#include "common/system.h" +#include "common/savefile.h" +#include "common/textconsole.h" +#include "graphics/thumbnail.h" +#include "graphics/surface.h" + +#include "lilliput/lilliput.h" + +namespace Lilliput { + +struct LilliputGameDescription { + ADGameDescription desc; + GameType gameType; +}; + +uint32 LilliputEngine::getFeatures() const { + return _gameDescription->desc.flags; +} + +const char *LilliputEngine::getGameId() const { + return _gameDescription->desc.gameId; +} + + +static const PlainGameDescriptor lilliputGames[] = { + // Games + {"robin", "Adventures of Robin Hood"}, + {"rome", "Rome: Pathway to Power"}, + {0, 0} +}; + +static const LilliputGameDescription gameDescriptions[] = { + + // Robin Hood English + { + { + "robin", 0, + { + {"erules.prg", 0, "92aaf84693a8948497ad57864fa31c2a", 71010}, + {"isomap.dta", 0, "bad97eae03a4db3e99565e39b0b3c06a", 16384}, + AD_LISTEND + }, + Common::EN_ANY, + Common::kPlatformDOS, + ADGF_UNSTABLE, + GUIO0() + }, + kGameTypeRobin + }, + // Robin Hood French + { + { + "robin", 0, + { + {"frules.prg", 0, "cf076c5ebfe8b3571e74a6a46d79426f", 76660}, + {"isomap.dta", 0, "bad97eae03a4db3e99565e39b0b3c06a", 16384}, + AD_LISTEND + }, + Common::FR_FRA, + Common::kPlatformDOS, + ADGF_UNSTABLE, + GUIO0() + }, + kGameTypeRobin + }, + // Robin Hood German + { + { + "robin", 0, + { + {"grules.prg", 0, "b53b7353dc1e841b206a64851e7bc58c", 78050}, + {"isomap.dta", 0, "bad97eae03a4db3e99565e39b0b3c06a", 16384}, + AD_LISTEND + }, + Common::DE_DEU, + Common::kPlatformDOS, + ADGF_UNSTABLE, + GUIO0() + }, + kGameTypeRobin + }, + // Robin Hood Italian + { + { + "robin", 0, + { + {"irules.prg", 0, "4d69ed3cda1e1d73585905517ea705d1", 75654}, + {"isomap.dta", 0, "bad97eae03a4db3e99565e39b0b3c06a", 16384}, + AD_LISTEND + }, + Common::IT_ITA, + Common::kPlatformDOS, + ADGF_UNSTABLE, + GUIO0() + }, + kGameTypeRobin + }, + {AD_TABLE_END_MARKER, kGameTypeNone} +}; + +class LilliputMetaEngine : public AdvancedMetaEngine { +public: + LilliputMetaEngine() : AdvancedMetaEngine(gameDescriptions, sizeof(LilliputGameDescription), lilliputGames) { + } + + const char *getName() const { + return "Lilliput"; + } + + const char *getOriginalCopyright() const { + return "Lilliput Engine copyright S.L.Grand, Brainware, 1991-1992"; + } + + bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const; + bool hasFeature(MetaEngineFeature f) const; + + int getMaximumSaveSlot() const; + SaveStateList listSaves(const char *target) const; + SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const; + void removeSaveState(const char *target, int slot) const; +}; + +bool LilliputMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const { + if (gd) { + *engine = new LilliputEngine(syst, (const LilliputGameDescription *)gd); + ((LilliputEngine *)*engine)->initGame((const LilliputGameDescription *)gd); + } + return gd != 0; +} + +bool LilliputMetaEngine::hasFeature(MetaEngineFeature f) const { + return + (f == kSupportsListSaves) || + (f == kSupportsLoadingDuringStartup) || + (f == kSupportsDeleteSave) || + (f == kSavesSupportMetaInfo) || + (f == kSavesSupportThumbnail) || + (f == kSavesSupportCreationDate); +} + +int LilliputMetaEngine::getMaximumSaveSlot() const { + return 99; +} + +SaveStateList LilliputMetaEngine::listSaves(const char *target) const { + Common::SaveFileManager *saveFileMan = g_system->getSavefileManager(); + Common::StringArray filenames; + Common::String pattern = target; + pattern += "-##.SAV"; + + filenames = saveFileMan->listSavefiles(pattern); + + SaveStateList saveList; + char slot[3]; + int slotNum = 0; + for (Common::StringArray::const_iterator filename = filenames.begin(); filename != filenames.end(); ++filename) { + slot[0] = filename->c_str()[filename->size() - 6]; + slot[1] = filename->c_str()[filename->size() - 5]; + slot[2] = '\0'; + // Obtain the last 2 digits of the filename (without extension), since they correspond to the save slot + slotNum = atoi(slot); + if (slotNum >= 0 && slotNum <= getMaximumSaveSlot()) { + Common::InSaveFile *file = saveFileMan->openForLoading(*filename); + if (file) { + int saveVersion = file->readByte(); + + if (saveVersion != kSavegameVersion) { + warning("Savegame of incompatible version"); + delete file; + continue; + } + + // read name + uint16 nameSize = file->readUint16BE(); + if (nameSize >= 255) { + delete file; + continue; + } + char name[256]; + file->read(name, nameSize); + name[nameSize] = 0; + + saveList.push_back(SaveStateDescriptor(slotNum, name)); + delete file; + } + } + } + + Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator()); + return saveList; +} + +SaveStateDescriptor LilliputMetaEngine::querySaveMetaInfos(const char *target, int slot) const { + Common::String fileName = Common::String::format("%s-%02d.SAV", target, slot); + Common::InSaveFile *file = g_system->getSavefileManager()->openForLoading(fileName); + + if (file) { + int saveVersion = file->readByte(); + + if (saveVersion != kSavegameVersion) { + warning("Savegame of incompatible version"); + delete file; + return SaveStateDescriptor(); + } + + uint32 saveNameLength = file->readUint16BE(); + Common::String saveName; + for (uint32 i = 0; i < saveNameLength; ++i) { + char curChr = file->readByte(); + saveName += curChr; + } + + SaveStateDescriptor desc(slot, saveName); + + Graphics::Surface *thumbnail; + if (!Graphics::loadThumbnail(*file, thumbnail)) { + delete file; + return SaveStateDescriptor(); + } + desc.setThumbnail(thumbnail); + + desc.setDeletableFlag(true); + desc.setWriteProtectedFlag(false); + + uint32 saveDate = file->readUint32BE(); + uint16 saveTime = file->readUint16BE(); + + int day = (saveDate >> 24) & 0xFF; + int month = (saveDate >> 16) & 0xFF; + int year = saveDate & 0xFFFF; + + desc.setSaveDate(year, month, day); + + int hour = (saveTime >> 8) & 0xFF; + int minutes = saveTime & 0xFF; + + desc.setSaveTime(hour, minutes); + + // Slot 0 is used for the 'restart game' save in all Robin games, thus + // we prevent it from being deleted. + desc.setDeletableFlag(slot != 0); + desc.setWriteProtectedFlag(slot == 0); + + delete file; + return desc; + } + return SaveStateDescriptor(); +} + +void LilliputMetaEngine::removeSaveState(const char *target, int slot) const { + Common::String fileName = Common::String::format("%s-%02d.SAV", target, slot); + g_system->getSavefileManager()->removeSavefile(fileName); +} +} // End of namespace Lilliput + +#if PLUGIN_ENABLED_DYNAMIC(LILLIPUT) +REGISTER_PLUGIN_DYNAMIC(LILLIPUT, PLUGIN_TYPE_ENGINE, Lilliput::LilliputMetaEngine); +#else +REGISTER_PLUGIN_STATIC(LILLIPUT, PLUGIN_TYPE_ENGINE, Lilliput::LilliputMetaEngine); +#endif + +namespace Lilliput { + +void LilliputEngine::initGame(const LilliputGameDescription *gd) { + _gameType = gd->gameType; + _platform = gd->desc.platform; +} + +} // End of namespace Lilliput diff --git a/engines/lilliput/lilliput.cpp b/engines/lilliput/lilliput.cpp new file mode 100644 index 0000000000..e41ec36067 --- /dev/null +++ b/engines/lilliput/lilliput.cpp @@ -0,0 +1,2856 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can 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/system.h" +#include "common/random.h" +#include "common/error.h" +#include "common/debug-channels.h" +#include "common/config-manager.h" +#include "common/textconsole.h" +#include "common/memstream.h" +#include "common/events.h" +#include "engines/util.h" +#include "graphics/cursorman.h" + +#include "lilliput/lilliput.h" +#include "engines/util.h" +#include "lilliput/script.h" +#include "lilliput/sound.h" + +namespace Lilliput { + +LilliputEngine *LilliputEngine::s_Engine = 0; + +static const byte _basisPalette[768] = { + 0, 0, 0, 0, 0, 42, 0, 42, 0, 0, 42, 42, + 42, 0, 0, 42, 0, 42, 42, 21, 0, 42, 42, 42, + 21, 21, 21, 21, 21, 63, 21, 63, 21, 21, 63, 63, + 63, 21, 21, 63, 21, 63, 63, 63, 21, 63, 63, 63, + 63, 63, 63, 59, 59, 59, 54, 54, 54, 50, 50, 50, + 46, 46, 46, 42, 42, 42, 38, 38, 38, 33, 33, 33, + 29, 29, 29, 25, 25, 25, 21, 21, 21, 17, 17, 17, + 13, 13, 13, 8, 8, 8, 4, 4, 4, 0, 0, 0, + 63, 54, 54, 63, 46, 46, 63, 39, 39, 63, 31, 31, + 63, 23, 23, 63, 16, 16, 63, 8, 8, 63, 0, 0, + 57, 0, 0, 51, 0, 0, 45, 0, 0, 39, 0, 0, + 33, 0, 0, 28, 0, 0, 22, 0, 0, 16, 0, 0, + 63, 58, 54, 63, 54, 46, 63, 50, 39, 63, 46, 31, + 63, 42, 23, 63, 38, 16, 63, 34, 8, 63, 30, 0, + 57, 27, 0, 51, 24, 0, 45, 21, 0, 39, 19, 0, + 33, 16, 0, 28, 14, 0, 22, 11, 0, 16, 8, 0, + 63, 63, 54, 63, 63, 46, 63, 63, 39, 63, 63, 31, + 63, 62, 23, 63, 61, 16, 63, 61, 8, 63, 61, 0, + 57, 54, 0, 51, 49, 0, 45, 43, 0, 39, 39, 0, + 33, 33, 0, 28, 27, 0, 22, 21, 0, 16, 16, 0, + 62, 63, 54, 59, 61, 47, 56, 59, 42, 53, 58, 36, + 50, 56, 32, 47, 54, 26, 44, 52, 22, 41, 50, 17, + 36, 46, 14, 32, 42, 11, 28, 37, 8, 24, 33, 6, + 20, 29, 4, 16, 25, 2, 13, 20, 1, 10, 16, 0, + 54, 63, 54, 48, 61, 48, 43, 59, 43, 38, 58, 38, + 33, 56, 33, 29, 54, 29, 25, 52, 24, 21, 50, 20, + 16, 46, 16, 14, 42, 13, 10, 37, 9, 8, 33, 7, + 6, 29, 4, 4, 25, 2, 2, 20, 1, 1, 16, 0, + 59, 63, 63, 53, 63, 63, 47, 62, 63, 41, 61, 62, + 35, 60, 62, 30, 59, 62, 24, 57, 62, 18, 55, 62, + 20, 52, 56, 15, 47, 50, 11, 42, 45, 8, 37, 39, + 5, 32, 33, 3, 27, 27, 1, 22, 22, 0, 16, 16, + 54, 59, 63, 46, 56, 63, 39, 53, 63, 31, 50, 63, + 23, 47, 63, 16, 44, 63, 8, 42, 63, 0, 39, 63, + 0, 35, 57, 0, 31, 51, 0, 27, 45, 0, 23, 39, + 0, 19, 33, 0, 16, 28, 0, 12, 22, 0, 9, 16, + 54, 54, 63, 46, 47, 63, 39, 39, 63, 31, 32, 63, + 23, 24, 63, 16, 16, 63, 8, 9, 63, 0, 1, 63, + 0, 1, 57, 0, 1, 51, 0, 0, 45, 0, 0, 39, + 0, 0, 33, 0, 0, 28, 0, 0, 22, 0, 0, 16, + 54, 63, 54, 47, 63, 46, 39, 63, 39, 32, 63, 31, + 24, 63, 23, 16, 63, 16, 8, 63, 8, 0, 63, 0, + 0, 56, 0, 0, 49, 0, 0, 43, 0, 0, 36, 0, + 0, 30, 0, 0, 23, 0, 0, 16, 0, 0, 10, 0, + 63, 54, 63, 63, 46, 63, 63, 39, 63, 63, 31, 63, + 63, 23, 63, 63, 16, 63, 63, 8, 63, 63, 0, 63, + 56, 0, 57, 50, 0, 51, 45, 0, 45, 39, 0, 39, + 33, 0, 33, 27, 0, 28, 22, 0, 22, 16, 0, 16, + 63, 58, 55, 63, 56, 52, 63, 54, 49, 63, 53, 47, + 63, 51, 44, 63, 49, 41, 63, 47, 39, 63, 46, 36, + 63, 44, 32, 63, 41, 28, 63, 39, 24, 60, 37, 23, + 58, 35, 22, 55, 34, 21, 52, 32, 20, 50, 31, 19, + 47, 30, 18, 45, 28, 17, 42, 26, 16, 40, 25, 15, + 39, 24, 14, 36, 23, 13, 34, 22, 12, 32, 20, 11, + 29, 19, 10, 27, 18, 9, 23, 16, 8, 21, 15, 7, + 18, 14, 6, 16, 12, 6, 14, 11, 5, 10, 8, 3, + 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 +}; + + +LilliputEngine::LilliputEngine(OSystem *syst, const LilliputGameDescription *gd) : Engine(syst), _gameDescription(gd) { + _system = syst; + DebugMan.addDebugChannel(kDebugEngine, "Engine", "Engine debug level"); + DebugMan.addDebugChannel(kDebugScript, "Script", "Script debug level"); + DebugMan.addDebugChannel(kDebugSound, "Sound", "Sound debug level"); + DebugMan.addDebugChannel(kDebugEngineTBC, "EngineTBC", "Engine debug level"); + DebugMan.addDebugChannel(kDebugScriptTBC, "ScriptTBC", "Script debug level"); + + _console = new LilliputConsole(this); + _rnd = 0; + _mousePos = Common::Point(0, 0); + _oldMousePos = Common::Point(0, 0); + _mouseDisplayPos = Common::Point(0, 0); + _mouseButton = 0; + _mouseClicked = false; + _savedMousePosDivided = Common::Point(-1, -1); + _mousePreviousEventType = Common::EVENT_INVALID; + _skipDisplayFlag1 = 1; + _skipDisplayFlag2 = 0; + _displayMap = false; + _debugFlag = 0; + _debugFlag2 = 0; + + _scriptHandler = new LilliputScript(this); + _soundHandler = new LilliputSound(this); + + _handleOpcodeReturnCode = 0; + _delayedReactivationAction = false; + _selectedCharacterId = -1; + _numCharactersToDisplay = 0; + _nextDisplayCharacterPos = Common::Point(0, 0); + _animationTick = 0; + _byte12A05 = 10; // Used to trigger sound and animations in int8, 1 time out of 10 + _refreshScreenFlag = false; + _byte16552 = 0; + _lastInterfaceHotspotIndex = -1; + _lastInterfaceHotspotButton = 0; + _lastAnimationTick = 0; + + _currentScriptCharacter = 0; + _currentScriptCharacterPos = Common::Point(0, 0); + _host = 0; + _nextCharacterIndex = 0; + _waitingSignal = -1; + _waitingSignalCharacterId = -1; + _newModesEvaluatedNumber = 0; + _savedSurfaceUnderMousePos = Common::Point(0, 0); + _displayGreenHand = false; + _isCursorGreenHand = false; + _displayStringIndex = 0; + _signalTimer = 0; + _numCharacters = 0; + + _saveFlag = true; + _actionType = kActionNone; + + _doorEntranceMask[0] = _doorExitMask[3] = 1; + _doorEntranceMask[1] = _doorExitMask[2] = 2; + _doorEntranceMask[2] = _doorExitMask[1] = 4; + _doorEntranceMask[3] = _doorExitMask[0] = 8; + + for (int i = 0; i < 3; i++) + _codeEntered[i] = 0; + + for (int i = 0; i < 4; i++) + _homeInDirLikelyhood[i] = 0; + + for (int i = 0; i < 40; i++) { + _characterTargetPos[i] = Common::Point(0, 0); + _charactersToDisplay[i] = 0; + _characterRelativePos[i] = Common::Point(-1, -1); + _characterDisplay[i] = Common::Point(0, 0); + _characterMagicPuffFrame[i] = -1; + _characterSubTargetPos[i] = Common::Point(-1, -1); + _specialCubes[i] = 0; + + _characterSignals[i] = -1; + _characterPos[i] = Common::Point(-1, -1); + _characterPosAltitude[i] = 0; + _characterFrameArray[i] = 0; + _characterCarried[i] = -1; + _characterBehindDist[i] = 4; + _characterAboveDist[i] = 0; + _spriteSizeArray[i] = 20; + _characterDirectionArray[i] = 0; + _characterMobility[i] = 0; + _characterTypes[i] = 0; + _characterBehaviour[i] = 0; + _characterHomePos[i] = Common::Point(0, 0); + _signalArr[i] = -1; + } + + for (int i = 0; i < 30; i++) + _signalArray[i] = -1; + + for (int i = 0; i < 256; i++) + _savedSurfaceUnderMouse[i] = 0; + + for (int i = 0; i < 160; i++) + _displayStringBuf[i] = 0; + + for (int i = 0; i < 1400 + 3120; i++) { + _characterVariables[i] = 0; + } + + _currentCharacterAttributes = NULL; + _bufferIdeogram = NULL; + _bufferMen = NULL; + _bufferMen2 = NULL; + _bufferIsoChars = NULL; + _bufferIsoMap = NULL; + _bufferCubegfx = NULL; + + _sequencesArr = nullptr; + _packedStringIndex = nullptr; + _packedStringNumb = 0; + _packedStrings = nullptr; + _initScript = nullptr; + _initScriptSize = 0; + _menuScript = nullptr; + _menuScriptSize = 0; + _arrayGameScriptIndex = nullptr; + _gameScriptIndexSize = 0; + _arrayGameScripts = nullptr; + _listNumb = 0; + _listIndex = nullptr; + _listArr = nullptr; + _rectNumb = 0; + for (int i = 0; i < 40; ++i) + _enclosureRect[i] = Common::Rect(0, 0, 0, 0); + + _interfaceHotspotNumb = 0; + for (int i = 0; i < 20; ++i) + _keyboardMapping[i] = Common::KEYCODE_DOLLAR; + + _mainSurface = nullptr; + _smallAnimsFrameIndex = 0; + _keyDelay = 0; + _int8Timer = 0; + _keyboard_nextIndex = 0; + _keyboard_oldIndex = 0; + _normalCursor = nullptr; + _greenCursor = nullptr; + _word10800_ERULES = 0; + _currentDisplayCharacter = 0; + + _shouldQuit = false; + _eventMan = nullptr; + _lastTime = 0; + _gameType = kGameTypeNone; + _platform = Common::kPlatformUnknown; +} + +LilliputEngine::~LilliputEngine() { + DebugMan.clearAllDebugChannels(); + delete _console; + delete _soundHandler; + delete _scriptHandler; + delete _rnd; +} + +GUI::Debugger *LilliputEngine::getDebugger() { + return _console; +} + +void LilliputEngine::update() { + // update every 20 ms. + int currentTime = _system->getMillis(); + if (currentTime - _lastTime > 20) { + _lastTime += ((currentTime - _lastTime) / 20) * 20; + newInt8(); + pollEvent(); + if (_displayGreenHand == true && _isCursorGreenHand == false) { + _isCursorGreenHand = true; + CursorMan.pushCursor(_greenCursor, 16, 16, 0, 0, 0); + } else if (_displayGreenHand == false && _isCursorGreenHand == true) { + _isCursorGreenHand = false; + CursorMan.popCursor(); + } + + _system->copyRectToScreen((byte *)_mainSurface->getPixels(), 320, 0, 0, 320, 200); + _system->updateScreen(); + } +} + +void LilliputEngine::newInt8() { + _soundHandler->refresh(); + + if (_byte12A05 != 0) + --_byte12A05; + else { + _byte12A05 = 10; + if (_int8Timer != 0) + --_int8Timer; + + _animationTick ^= 1; + if (!_refreshScreenFlag) + displayRefreshScreen(); + } +} + +bool LilliputEngine::hasFeature(EngineFeature f) const { + return (f == kSupportsRTL) || (f == kSupportsLoadingDuringRuntime) || (f == kSupportsSavingDuringRuntime); +} + +const char *LilliputEngine::getCopyrightString() const { + return "copyright S.L.Grand, Brainware, 1991 - 1992"; +} + +GameType LilliputEngine::getGameType() const { + return _gameType; +} + +Common::Platform LilliputEngine::getPlatform() const { + return _platform; +} + +void LilliputEngine::displayCharacter(int index, Common::Point pos, int flags) { + debugC(2, kDebugEngine, "displayCharacter(%d, %d - %d, %d)", index, pos.x, pos.y, flags); + + byte *buf = _savedSurfaceGameArea1 + (pos.y * 256) + pos.x; + + byte *src = _bufferMen; + if (index < 0) { + src = _bufferIdeogram; + index = -index; + } else if (index >= 0xF0) { + src = _bufferMen2; + index -= 0xF0; + } + + src += (index * 256); + + if ((flags & 2) == 0) { + for (int y = 0; y < 16; y++) { + for (int x = 0; x < 16; x++) { + if (src[x] != 0) + buf[x] = src[x]; + } + src += 16; + buf += 256; + } + } else { + // Sprite mirror + for (int y = 0; y < 16; y++) { + for (int x = 0; x < 16; x++) { + // May need a hack of 1 pixel + if (src[15 - x] != 0) + buf[x] = src[15 - x]; + } + src += 16; + buf += 256; + } + } +} + +void LilliputEngine::display16x16IndexedBuf(byte *buf, int index, Common::Point pos, bool transparent, bool updateScreen) { + debugC(2, kDebugEngine, "display16x16IndexedBuf(buf, %d, %d - %d)", index, pos.x, pos.y); + + int index1 = index * 16 * 16; + byte *newBuf = &buf[index1]; + + int vgaIndex = pos.x + (pos.y * 320); + + for (int i = 0; i < 16; i++) { + // clip on y + if (pos.y + i < 200) { + for (int j = 0; j < 16; j++) { + // clip on x + if ((newBuf[j] != 0 || !transparent) && (pos.x + j < 320)) + ((byte *)_mainSurface->getPixels())[vgaIndex + j] = newBuf[j]; + } + } + vgaIndex += 320; + newBuf += 16; + } + + if (updateScreen) { + _system->copyRectToScreen((byte *)_mainSurface->getPixels(), 320, 0, 0, 320, 200); + _system->updateScreen(); + } +} + +void LilliputEngine::display16x16Buf(byte *buf, Common::Point pos, bool transparent, bool updateScreen) { + debugC(2, kDebugEngine, "display16x16Buf(buf, %d, %d)", pos.x, pos.y); + + display16x16IndexedBuf(buf, 0, pos, transparent, updateScreen); +} + +void LilliputEngine::fill16x16Rect(byte col, Common::Point pos) { + debugC(2, kDebugEngineTBC, "fill16x16Rect(%d, %d - %d)", col, pos.x, pos.y); + + int index = pos.x + (pos.y * 320); + for (int i = 0; i < 16; i++) { + for (int j = 0; j < 16; j++) { + ((byte *)_mainSurface->getPixels())[index + j] = col; + } + index += 320; + } +} + +void LilliputEngine::saveSurfaceGameArea() { + debugC(2, kDebugEngine, "saveSurfaceGameArea()"); + + int index = (16 * 320) + 64; // 5184 + for (int i = 0; i < 176; i++) { + for (int j = 0; j < 256; j++) + _savedSurfaceGameArea3[(i * 256) + j] = ((byte *)_mainSurface->getPixels())[index + j]; + index += 320; + } +} + +void LilliputEngine::saveSurfaceSpeech() { + debugC(2, kDebugEngine, "saveSurfaceSpeech()"); + + int index = 66; + for (int i = 0; i < 16; i++) { + for (int j = 0; j < 252; j++) + _savedSurfaceSpeech[(i * 252) + j] = ((byte *)_mainSurface->getPixels())[index + j]; + index += 320; + } +} + +void LilliputEngine::restoreSurfaceSpeech() { + debugC(2, kDebugEngine, "restoreSurfaceSpeech()"); + + int index = 66; + for (int i = 0; i < 16; i++) { + for (int j = 0; j < 252; j++) + ((byte *)_mainSurface->getPixels())[index + j] = _savedSurfaceSpeech[(i * 252) + j]; + index += 320; + } +} + + +void LilliputEngine::displayInterfaceHotspots() { + debugC(2, kDebugEngine, "displayInterfaceHotspots()"); + + if (_displayMap) + return; + + for (int index = 0; index < _interfaceHotspotNumb; index++) { + int tmpVal = _scriptHandler->_interfaceHotspotStatus[index] * 20; + display16x16IndexedBuf(_bufferIdeogram, tmpVal + index, _interfaceHotspots[index]); + } +} + +void LilliputEngine::displayLandscape() { + debugC(2, kDebugEngine, "displayLandscape()"); + + memcpy(_savedSurfaceGameArea2, _savedSurfaceGameArea3, 176 * 256); // 45056 + + int index = (_scriptHandler->_viewportPos.y * 64 + _scriptHandler->_viewportPos.x) * 4; + + for (int posY = 0; posY < 8; posY++) { + for (int posX = 0; posX < 8 ; posX++) { + assert (index < 16384); + displayIsometricBlock(_savedSurfaceGameArea2, _bufferIsoMap[index], posX, posY, 0); + index += 4; + } + index += 224; + } +} + +// Display dialog bubble +void LilliputEngine::displaySpeechBubble() { + debugC(2, kDebugEngine, "displaySpeechBubble()"); + static const byte _array15976[16] = {244, 248, 250, 250, 252, 252, 252, 252, 252, 252, 252, 252, 250, 250, 248, 244}; + + int index = 192; + + for (int i = 0; i < 16; i++) { + int var3 = _array15976[i]; + int tmpIndex = index - (var3 / 2); + var3 &= 0xFE; + for (int j = 0; j < var3; j++) { + ((byte *)_mainSurface->getPixels())[tmpIndex + j] = 17; + } + index += 320; + } +} + +void LilliputEngine::displaySpeechLine(int vgaIndex, byte *srcBuf, int &bufIndex) { + debugC(2, kDebugEngine, "displaySpeechLine()"); + + int var3 = 0; + int var1; + int bckIndex = bufIndex; + + for (;;) { + var1 = srcBuf[bufIndex]; + if ((var1 == 0) || (var1 == '|')) + break; + + ++bufIndex; + ++var3; + } + + var1 = (0x3D - var3) * 2; + vgaIndex += var1; + + bufIndex = bckIndex; + for (;;) { + var1 = srcBuf[bufIndex]; + ++bufIndex; + if ((var1 == 0) || (var1 == '|')) + break; + + displayChar(vgaIndex, var1); + vgaIndex += 4; + } +} + +void LilliputEngine::displaySpeech(byte *buf) { + debugC(2, kDebugEngine, "displaySpeech(%s)", buf); + + int vgaIndex = 70; + int bufIndex = 0; + + bool multiLineFlag = false; + byte var1; + + for (;;) { + var1 = buf[bufIndex]; + ++bufIndex; + if (var1 == 0) { + vgaIndex += (4 * 320); + break; + } else if (var1 == '|') { + multiLineFlag = true; + break; + } + } + + bufIndex = 0; + displaySpeechLine(vgaIndex, buf, bufIndex); + if (multiLineFlag) { + vgaIndex += (8 * 320); + displaySpeechLine(vgaIndex, buf, bufIndex); + } +} + +void LilliputEngine::initGameAreaDisplay() { + debugC(1, kDebugEngine, "initGameAreaDisplay()"); + + // display background + byte *tmpBuf = loadVGA("SCREEN.GFX", 320 * 200, true); + memcpy(_mainSurface->getPixels(), tmpBuf, 320 * 200); + _system->copyRectToScreen((byte *)_mainSurface->getPixels(), 320, 0, 0, 320, 200); + _system->updateScreen(); + + // display game area on top of background + saveSurfaceGameArea(); + saveSurfaceSpeech(); + displayInterfaceHotspots(); + displayLandscape(); + prepareGameArea(); + displayGameArea(); + + free(tmpBuf); +} + +void LilliputEngine::displayIsometricBlock(byte *buf, int var1, int posX, int posY, int var3) { + debugC(1, kDebugEngine, "displayIsometricBlock(buf, %d, %d - %d, %d)", var1, posX, posY, var3); + + byte tmpByte1 = ((7 + posX - posY) << 4) & 0xFF; + byte tmpByte2 = ((4 + posX + posY - (var3 >> 7)) << 3) & 0xFF; + + int index = (tmpByte2 << 8) + tmpByte1; + int index2 = var1 << 10; + + for (int i = 0; i < 32; i++) { + for (int j = 0; j < 32; j++) { + if (_bufferCubegfx[index2 + j] != 0) + buf[index + j] = _bufferCubegfx[index2 + j]; + } + index2 += 32; + index += 256; + } +} + +void LilliputEngine::displayGameArea() { + debugC(2, kDebugEngine, "displayGameArea()"); + + if (_displayMap) + return; + + int index = (16 * 320) + 64; // 5184 + for (int i = 0; i < 176; i++) { + for (int j = 0; j < 256; j++) + ((byte *)_mainSurface->getPixels())[index + j] = _savedSurfaceGameArea1[(i * 256) + j]; + index += 320; + } + + _system->copyRectToScreen((byte *)_mainSurface->getPixels(), 320, 0, 0, 320, 200); + _system->updateScreen(); +} + +void LilliputEngine::restoreMapPoints() { + debugC(2, kDebugEngine, "restoreMapPoints()"); + + byte *buf = (byte *)_mainSurface->getPixels(); + for (byte index = 0; index < _numCharacters; index++) { + buf[_mapSavedPixelIndex[index]] = _mapSavedPixel[index]; + } +} + +void LilliputEngine::displayCharactersOnMap() { + debugC(2, kDebugEngineTBC, "displayCharactersOnMap()"); + + moveCharacters(); + + byte *buf = (byte *)_mainSurface->getPixels(); + for (int index = _numCharacters - 1; index >= 0; index--) { + if (((_characterTypes[index] & 2) == 0) && (_scriptHandler->_characterTilePos[index].y != -1)) { + // FIXME: This is still wrong, but less. The values in both arrays should be verified now! + int pixIndex = 320 + ((15 * _scriptHandler->_characterTilePos[index].y) / 4) + (_scriptHandler->_characterTilePos[index].x * 4) + 1; + + _mapSavedPixelIndex[index] = pixIndex; + _mapSavedPixel[index] = buf[pixIndex]; + buf[pixIndex] = _scriptHandler->_characterMapPixelColor[index]; + } + } +} + +void LilliputEngine::moveCharacters() { + debugC(2, kDebugEngine, "moveCharacters()"); + + _numCharactersToDisplay = 0; + byte index = _numCharacters - 1; + Common::Point pos16213 = Common::Point(_scriptHandler->_viewportPos.x << 3, _scriptHandler->_viewportPos.y << 3); + + for (int i = index; i >= 0; i--) { + if (_characterCarried[i] != -1) { + int index2 = _characterCarried[i]; + _characterPosAltitude[i] = _characterPosAltitude[index2] + _characterAboveDist[i]; + int8 behindDist = _characterBehindDist[i]; + _characterDirectionArray[i] = _characterDirectionArray[index2]; + int nextPosX = _characterPos[index2].x; + int nextPosY = _characterPos[index2].y; + + switch (_characterDirectionArray[i]) { + case 0: + nextPosX -= behindDist; + break; + case 1: + nextPosY += behindDist; + break; + case 2: + nextPosY -= behindDist; + break; + default: + nextPosX += behindDist; + break; + } + + _characterPos[i] = Common::Point(nextPosX, nextPosY); + } + + _scriptHandler->_characterTilePos[i] = Common::Point(_characterPos[i].x >> 3, _characterPos[i].y >> 3); + _characterRelativePos[i] = Common::Point(-1, -1); + _characterDisplay[i] = Common::Point(-1, -1); + + int tileX = (_characterPos[i].x >> 3) - _scriptHandler->_viewportPos.x; + int tileY = (_characterPos[i].y >> 3) - _scriptHandler->_viewportPos.y; + if ((tileX >= 0) && (tileX <= 7) && (tileY >= 0) && (tileY <= 7)) { + _characterRelativePos[i] = Common::Point(tileX, tileY); + int tempX = _characterPos[i].x - pos16213.x; + int tempY = _characterPos[i].y - pos16213.y; + _characterDisplay[i].x = ((60 + tempX - tempY) * 2) & 0xFF; + _characterDisplay[i].y = (20 + tempX + tempY - _characterPosAltitude[i]) & 0xFF; + _charactersToDisplay[_numCharactersToDisplay] = i; + ++_numCharactersToDisplay; + } + } + + sortCharacters(); +} + +void LilliputEngine::setNextDisplayCharacter(int var1) { + debugC(2, kDebugEngine, "setNextDisplayCharacter(%d)", var1); + + byte charNum = var1 & 0xFF; + if (charNum < _numCharactersToDisplay) { + int index = _charactersToDisplay[charNum]; + _nextDisplayCharacterPos = _characterRelativePos[index]; + } else + _nextDisplayCharacterPos = Common::Point(-1, -1); +} + +void LilliputEngine::prepareGameArea() { + debugC(2, kDebugEngine, "prepareGameArea()"); + + moveCharacters(); + _currentDisplayCharacter = 0; + setNextDisplayCharacter(0); + + memcpy(_savedSurfaceGameArea1, _savedSurfaceGameArea2, 176 * 256); // 45056; + + int index1 = (_scriptHandler->_viewportPos.y * 64 + _scriptHandler->_viewportPos.x) * 4; + assert(index1 < 16384); + byte *map = &_bufferIsoMap[index1]; + + for (int posY = 0; posY < 8; posY++) { + for (int posX = 0; posX < 8; posX++) { + if (map[1] != 0xFF) { + int var1 = map[1]; + if ((_cubeFlags[var1] & 128) != 0) + var1 += _animationTick; + displayIsometricBlock(_savedSurfaceGameArea1, var1, posX, posY, 1 << 8); + } + renderCharacters(map, Common::Point(posX, posY)); + + if (map[2] != 0xFF) { + int var1 = map[2]; + if ((_cubeFlags[var1] & 128) != 0) + var1 += _animationTick; + displayIsometricBlock(_savedSurfaceGameArea1, var1, posX, posY, 2 << 8); + } + map += 4; + } + map += 224; + } +} + +void LilliputEngine::displayRefreshScreen() { + debugC(2, kDebugEngine, "displayRefreshScreen()"); + + if (_displayMap) { + bool forceReturnFl = false; + checkMapClosing(forceReturnFl); + if (forceReturnFl) + return; + + restoreMapPoints(); + updateCharPosSequence(); + handleCharacterTimers(); + checkInteractions(); + checkSpecialCubes(); + handleSignals(); + displayCharactersOnMap(); + } else { + scrollToViewportCharacterTarget(); + checkSpeechClosing(); + prepareGameArea(); + displayGameArea(); + updateCharPosSequence(); + handleCharacterTimers(); + checkInteractions(); + checkSpecialCubes(); + handleSignals(); + handleGameMouseClick(); + checkInterfaceActivationDelay(); + displayHeroismIndicator(); + } +} + +void LilliputEngine::resetSmallAnims() { + debugC(2, kDebugEngine, "resetSmallAnims()"); + + _smallAnims[0]._active = false; + _smallAnims[1]._active = false; + _smallAnims[2]._active = false; + _smallAnims[3]._active = false; + _smallAnimsFrameIndex = 0; +} + +void LilliputEngine::displaySmallIndexedAnim(byte index, byte subIndex) { + debugC(2, kDebugEngine, "displaySmallIndexedAnim(%d, %d)", index, subIndex); + + if (!_smallAnims[index]._active) + return; + + display16x16IndexedBuf(_bufferIdeogram, _smallAnims[index]._frameIndex[subIndex], _smallAnims[index]._pos, false); +} + +void LilliputEngine::displaySmallAnims() { + debugC(2, kDebugEngine, "displaySmallAnims()"); + + if (_animationTick == _lastAnimationTick) + return; + + _lastAnimationTick = _animationTick; + + assert(_smallAnimsFrameIndex < 8); + int subIndex = _smallAnimsFrameIndex; + displaySmallIndexedAnim(0, subIndex); + displaySmallIndexedAnim(1, subIndex); + displaySmallIndexedAnim(2, subIndex); + displaySmallIndexedAnim(3, subIndex); + + ++subIndex; + if (subIndex == 8) + subIndex = 0; + + _smallAnimsFrameIndex = subIndex; +} + +void LilliputEngine::paletteFadeOut() { + debugC(2, kDebugEngine, "paletteFadeOut()"); + + resetSmallAnims(); + byte palette[768]; + for (int fade = 256; fade >= 0; fade -= 8) { + for (int i = 0; i < 768; i++) { + palette[i] = (_curPalette[i] * fade) >> 8; + } + _system->getPaletteManager()->setPalette(palette, 0, 256); + _system->updateScreen(); + _system->delayMillis(20); + pollEvent(); + } +} + +void LilliputEngine::paletteFadeIn() { + debugC(2, kDebugEngine, "paletteFadeIn()"); + + byte palette[768]; + for (int fade = 8; fade <= 256; fade += 8) { + for (int i = 0; i < 768; i++) { + palette[i] = (_curPalette[i] * fade) >> 8; + } + _system->getPaletteManager()->setPalette(palette, 0, 256); + _system->updateScreen(); + _system->delayMillis(20); + pollEvent(); + } +} + +int16 LilliputEngine::checkObstacle(int x1, int y1, int x2, int y2) { + debugC(2, kDebugEngine, "checkObstacle(%d, %d, %d, %d)", x1, y1, x2, y2); + + int index = ((y1 * 64) + x1) * 4; + assert((index > 0) && (index <= 16380)); + byte *isoMap = &_bufferIsoMap[index + 1]; + + int16 dx = x2 - x1; + int16 dy = y2 - y1; + + int16 tmpMapMoveX = 0; + int16 tmpMapMoveY = 0; + int16 mapMoveY = 0; + int16 mapMoveX = 0; + + int16 nonDiagdelta = 0; + int16 diagDelta = 0; + + if (dx < 0) { + dx = -dx; + tmpMapMoveX = -4; + } else { + tmpMapMoveX = 4; + } + + if (dy < 0) { + dy = -dy; + tmpMapMoveY = -256; + } else { + tmpMapMoveY = 256; + } + + if (dx >= dy) { + mapMoveY = 0; + mapMoveX = tmpMapMoveX; + } else { + int16 tmp = dy; + dy = dx; + dx = tmp; + mapMoveX = 0; + mapMoveY = tmpMapMoveY; + } + + nonDiagdelta = dy * 2; + int16 var1 = nonDiagdelta - dx; + diagDelta = nonDiagdelta - (dx * 2); + + mapMoveX += mapMoveY; + tmpMapMoveX += tmpMapMoveY; + + int count = 0; + + while (*isoMap == 0xFF) { + if (var1 >= 0) { + isoMap += tmpMapMoveX; + var1 += diagDelta; + } else { + isoMap += mapMoveX; + var1 += nonDiagdelta; + } + + count++; + if (count > dx) { + return 0; + } + } + return tmpMapMoveY; +} + +void LilliputEngine::startNavigateFromMap() { + debugC(2, kDebugEngine, "startNavigateFromMap()"); + + _selectedCharacterId = -1; + _savedMousePosDivided = Common::Point(-1, -1); + byte newX = _mousePos.x / 4; + byte newY = _mousePos.y / 3; + + if ((newX >= 64) || (newY >= 64)) + return; + + _savedMousePosDivided = Common::Point(newX, newY); + _actionType = kCubeSelected; +} + +void LilliputEngine::unselectInterfaceHotspots() { + debugC(2, kDebugEngine, "unselectInterfaceHotspots()"); + + for (int index = 0; index < _interfaceHotspotNumb; index++) { + if (_scriptHandler->_interfaceHotspotStatus[index] == kHotspotSelected) + _scriptHandler->_interfaceHotspotStatus[index] = kHotspotEnabled; + } +} + +void LilliputEngine::checkMapClosing(bool &forceReturnFl) { + debugC(2, kDebugEngineTBC, "checkMapClosing()"); + + forceReturnFl = false; + if (!_displayMap) + return; + + pollEvent(); + if (!_keyboard_checkKeyboard()) { + _keyboard_getch(); + } else { + if (_mouseButton != 1) + return; + + _mouseButton = 0; + startNavigateFromMap(); + } + + _displayMap = false; + paletteFadeOut(); + _displayGreenHand = false; + unselectInterfaceHotspots(); + initGameAreaDisplay(); + _scriptHandler->_heroismLevel = 0; + moveCharacters(); + paletteFadeIn(); + forceReturnFl = true; +} + +void LilliputEngine::checkInteractions() { + debugC(2, kDebugEngine, "checkInteractions()"); + + for (int index = _numCharacters - 1; index >= 0; index--) { + if (_characterTypes[index] & 1) + continue; + + int c1 = _scriptHandler->_characterTilePos[index].x; + int c2 = _scriptHandler->_characterTilePos[index].y; + + // Hack: Skip if disabled (c2 negative) + if (c2 == -1) + continue; + + for (int index2 = _numCharacters - 1; index2 >= 0; index2--) { + byte _newStatus = 0; + if ((index != index2) && + (_characterCarried[index] != index2) && + (_characterCarried[index2] != index) && + (_characterTypes[index2] & 2) == 0) { + int d1 = _scriptHandler->_characterTilePos[index2].x; + int d2 = _scriptHandler->_characterTilePos[index2].y; + + if (d1 != -1) { + int x = c1 - d1; + if ((x > -6) && (x < 6)) { + int y = c2 - d2; + if ((y > -6) && (y < 6)) { + _newStatus = 1; + + if ((c1 == d1) && (c2 == d2)) { + _newStatus = 4; + } else if ((_characterTypes[index] & 4) != 0) { + _newStatus = 0; + } else { + switch (_characterDirectionArray[index]) { + case 0: + if (d1 > c1) { + _newStatus = 2; + + if (d2 == c2) + _newStatus = 3; + + if (checkObstacle(c1, c2, d1, d2) != 0) + _newStatus = 1; + } + break; + case 1: + if (d2 < c2) { + _newStatus = 2; + + if (d1 == c1) + _newStatus = 3; + + if (checkObstacle(c1, c2, d1, d2) != 0) + _newStatus = 1; + } + break; + case 2: + if (d2 > c2) { + _newStatus = 2; + + if (d1 == c1) + _newStatus = 3; + + if (checkObstacle(c1, c2, d1, d2) != 0) + _newStatus = 1; + } + break; + default: + if (d1 < c1) { + _newStatus = 2; + + if (d2 == c2) + _newStatus = 3; + + if (checkObstacle(c1, c2, d1, d2) != 0) + _newStatus = 1; + } + break; + } + } + } + } + } + } + + int8 v2 = _scriptHandler->_interactions[index2 + (index * 40)] & 0xFF; + int8 v1 = v2; + + if (v2 != _newStatus) { + _scriptHandler->_characterScriptEnabled[index] = 1; + v2 = _newStatus; + } + _scriptHandler->_interactions[index2 + (index * 40)] = (v1 << 8) + v2; + } + } +} + +void LilliputEngine::displayCharacterStatBar(int8 type, int16 averagePosX, int8 score, int16 posY) { + debugC(2, kDebugEngine, "displayCharacterStatBar(%d, %d, %d, %d)", type, averagePosX, score, posY); + + int16 posX = averagePosX; + + // If var equals 45 ('-'), score bar from -x to +x. If not (usually 43 '+'), score bar from 0 to x. + if (type == 45) { + posX += 35; + score -= 35; + + if (score < 0) { + posX += score; + score = -score; + } + } + + byte *vgaBuf = (byte *)_mainSurface->getPixels(); + int vgaIndex = posX + (320 * posY); + + if (score == 0) + ++score; + + // Draw bar, color green, high = 4, width = score + for (int i = 0; i < 4; i++) { + for (int j = 0; j < score; j++) { + vgaBuf[vgaIndex + j] = 2; + } + vgaIndex += 320; + } +} + +void LilliputEngine::displayString(byte *buf, Common::Point pos) { + debugC(2, kDebugEngine, "displayString(%s, %d - %d)", buf, pos.x, pos.y); + + int index = (pos.y * 320) + pos.x; + + int i = 0; + while (buf[i] != 0) { + displayChar(index, buf[i]); + ++i; + index += 4; + } +} + +void LilliputEngine::displayChar(int index, int var1) { + debugC(2, kDebugEngine, "displayChar(%d, %d)", index, var1); + + int indexVga = index; + int indexChar = var1 << 5; + + for (int i = 0; i < 8; i++) { + for (int j = 0; j < 4; j++) + ((byte *)_mainSurface->getPixels())[indexVga + j] = _bufferIsoChars[indexChar + j]; + indexVga += 320; + indexChar += 4; + } + +} + +void LilliputEngine::sortCharacters() { + debugC(2, kDebugEngine, "sortCharacters()"); + + if (_numCharactersToDisplay <= 1) + return; + + for (int var4 = _numCharactersToDisplay - 1; var4 > 0; var4--) { + bool found = false; + + for (int var2 = 0; var2 < var4; var2++) { + int index1 = _charactersToDisplay[var2]; + int index2 = _charactersToDisplay[var2 + 1]; + + if (_characterRelativePos[index1].y < _characterRelativePos[index2].y) + continue; + + if (_characterRelativePos[index1].y == _characterRelativePos[index2].y) { + if (_characterRelativePos[index1].x < _characterRelativePos[index2].x) + continue; + + if (_characterRelativePos[index1].x == _characterRelativePos[index2].x) { + if (_characterPosAltitude[index1] < _characterPosAltitude[index2]) + continue; + + if (_characterPosAltitude[index1] == _characterPosAltitude[index2]) { + if (_characterDisplay[index1].y < _characterDisplay[index2].y) + continue; + } + } + } + + byte tmpVal = _charactersToDisplay[var2]; + _charactersToDisplay[var2] = _charactersToDisplay[var2 + 1]; + _charactersToDisplay[var2 + 1] = tmpVal; + found = true; + } + + if (!found) + return; + } +} + +void LilliputEngine::scrollToViewportCharacterTarget() { + debugC(2, kDebugEngine, "scrollToViewportCharacterTarget()"); + + if (_scriptHandler->_viewportCharacterTarget == -1) + return; + + int tileX = (_characterPos[_scriptHandler->_viewportCharacterTarget].x >> 3) - _scriptHandler->_viewportPos.x; + int tileY = (_characterPos[_scriptHandler->_viewportCharacterTarget].y >> 3) - _scriptHandler->_viewportPos.y; + Common::Point newPos = _scriptHandler->_viewportPos; + + if (tileX >= 1) { + if (tileX > 6){ + newPos.x += 4; + if (newPos.x > 56) + newPos.x = 56; + } + } else { + newPos.x -= 4; + if (newPos.x < 0) + newPos.x = 0; + } + + if ((tileY < 1) && (newPos.y < 4)) + newPos.y = 0; + else { + if (tileY < 1) + newPos.y -= 4; + + if (tileY > 6) { + newPos.y += 4; + if (newPos.y >= 56) + newPos.y = 56; + } + } + viewportScrollTo(newPos); +} + +void LilliputEngine::viewportScrollTo(Common::Point goalPos) { + debugC(2, kDebugEngine, "viewportScrollTo(%d, %d)", goalPos.x, goalPos.y); + + if (goalPos == _scriptHandler->_viewportPos) + return; + + int16 dx = 0; + if (goalPos.x != _scriptHandler->_viewportPos.x) { + if (goalPos.x < _scriptHandler->_viewportPos.x) + --dx; + else + ++dx; + } + + int16 dy = 0; + if (goalPos.y != _scriptHandler->_viewportPos.y) { + if (goalPos.y < _scriptHandler->_viewportPos.y) + --dy; + else + ++dy; + } + + do { + _scriptHandler->_viewportPos.x += dx; + _scriptHandler->_viewportPos.y += dy; + + displayLandscape(); + prepareGameArea(); + displayGameArea(); + + if (goalPos.x == _scriptHandler->_viewportPos.x) + dx = 0; + + if (goalPos.y == _scriptHandler->_viewportPos.y) + dy = 0; + } while ((dx != 0) || (dy != 0)); + + _soundHandler->update(); +} + +void LilliputEngine::renderCharacters(byte *buf, Common::Point pos) { + debugC(2, kDebugEngine, "renderCharacters(buf, %d - %d)", pos.x, pos.y); + + if (_nextDisplayCharacterPos != pos) + return; + + _byte16552 = 0; + + if (buf[1] != 0xFF) { + int tmpIndex = buf[1]; + if ((_cubeFlags[tmpIndex] & 16) == 0) + ++_byte16552; + } + + int index = _charactersToDisplay[_currentDisplayCharacter]; + Common::Point characterPos = _characterDisplay[index]; + + if (index == _scriptHandler->_talkingCharacter) + displaySpeechBubbleTail(characterPos); + + if (_byte16552 != 1) { + byte flag = _characterDirectionArray[index]; + int16 frame = _characterFrameArray[index]; + + if (frame != -1) { + frame += _scriptHandler->_characterPose[index]; + if ((flag & 1) == 1) + frame += _spriteSizeArray[index]; + + if (_characterMagicPuffFrame[index] != -1) { + frame = _characterMagicPuffFrame[index] + 82; + --_characterMagicPuffFrame[index]; + frame = -frame; + } + + displayCharacter(frame, characterPos, flag); + } + } + + ++_currentDisplayCharacter; + setNextDisplayCharacter(_currentDisplayCharacter); + + renderCharacters(buf, pos); +} + +void LilliputEngine::displaySpeechBubbleTail(Common::Point displayPos) { + debugC(2, kDebugEngine, "displaySpeechBubbleTail(%d, %d)", displayPos.x, displayPos.y); + + int orgX = displayPos.x + 8; + int orgY = displayPos.y; + int var2 = 0; + + int x = orgX; + int y = orgY; + do { + displaySpeechBubbleTailLine(Common::Point(x, y), var2); + --x; + y /= 2; + } while (y != 0); + + x = orgX + 1; + y = orgY / 2; + + while (y != 0) { + displaySpeechBubbleTailLine(Common::Point(x, y), var2); + ++x; + y /= 2; + } +} + +void LilliputEngine::displaySpeechBubbleTailLine(Common::Point pos, int var2) { + debugC(2, kDebugEngine, "displaySpeechBubbleTailLine(%d - %d, %d)", pos.x, pos.y, var2); + + int index = pos.x + (var2 * 256); + for (int i = 1 + pos.y - var2; i > 0; i--) { + _savedSurfaceGameArea1[index] = 17; + index += 256; + } +} + +void LilliputEngine::checkSpeechClosing() { + debugC(2, kDebugEngine, "checkSpeechClosing()"); + + if (_scriptHandler->_speechTimer != 0) { + --_scriptHandler->_speechTimer; + if (_scriptHandler->_speechTimer == 0) { + restoreSurfaceSpeech(); + _scriptHandler->_talkingCharacter = -1; + } + } +} + +byte LilliputEngine::getDirection(Common::Point param1, Common::Point param2) { + debugC(2, kDebugEngine, "getDirection(%d - %d, %d - %d)", param1.x, param1.y, param2.x, param2.y); + + static const byte _directionsArray[8] = {0, 2, 0, 1, 3, 2, 3, 1}; + + Common::Point var1 = param2; + Common::Point var2 = param1; + + int8 var1h = var1.x - var2.x; + int8 var1l = var1.y - var2.y; + int8 var2l = 0; + + if (var1h < 0) { + var2l |= 4; + var1h = -var1h; + } + + if (var1l < 0) { + var2l |= 2; + var1l = -var1l; + } + + if (var1h < var1l) + var2l |= 1; + + return _directionsArray[var2l]; +} + +byte LilliputEngine::sequenceCharacterHomeIn(int index, Common::Point param1) { + debugC(2, kDebugEngine, "sequenceCharacterHomeIn(%d, %d - %d)", index, param1.x, param1.y); + + Common::Point target = _characterSubTargetPos[index]; + + if (target.x != -1) { + if (target != _scriptHandler->_characterTilePos[index]) { + homeInChooseDirection(index); + _scriptHandler->_characterNextSequence[index] -= (param1.x & 0x0F); + return kSeqNoInc | kSeqRepeat; + } + + if (target == _characterTargetPos[index]) + return kSeqRepeat; + } + + homeInPathFinding(index); + + Common::Point pos1 = _scriptHandler->_characterTilePos[index]; + Common::Point pos2 = _characterSubTargetPos[index]; + + _characterDirectionArray[index] = getDirection(pos1, pos2); + + homeInChooseDirection(index); + _scriptHandler->_characterNextSequence[index] -= (param1.x & 0x0F); + return kSeqNoInc | kSeqRepeat; +} + +void LilliputEngine::homeInPathFinding(int index) { + debugC(2, kDebugEngine, "homeInPathFinding(%d)", index); + + int16 enclosureSrc = checkEnclosure(_scriptHandler->_characterTilePos[index]); + int16 enclosureDst = checkEnclosure(_characterTargetPos[index]); + + if (enclosureSrc == enclosureDst) { + _characterSubTargetPos[index] = _characterTargetPos[index]; + return; + } + + if (enclosureSrc == -1) { + int tmpVal = checkOuterEnclosure(_characterTargetPos[index]); + if (tmpVal == -1) + warning("homeInPathFinding: Unexpected negative index"); + else + _characterSubTargetPos[index] = _portalPos[tmpVal]; + return; + } + + if ((enclosureDst != -1) && + (_characterTargetPos[index].x >= _enclosureRect[enclosureSrc].left) && + (_characterTargetPos[index].x <= _enclosureRect[enclosureSrc].right) && + (_characterTargetPos[index].y >= _enclosureRect[enclosureSrc].top) && + (_characterTargetPos[index].y <= _enclosureRect[enclosureSrc].bottom)) { + _characterSubTargetPos[index] = _portalPos[enclosureDst]; + return; + } + + _characterSubTargetPos[index] = _portalPos[enclosureSrc]; + + if (_enclosureRect[enclosureSrc].left != _enclosureRect[enclosureSrc].right) { + if (_portalPos[enclosureSrc].x == _enclosureRect[enclosureSrc].left) { + _characterSubTargetPos[index] = Common::Point(_portalPos[enclosureSrc].x - 1, _portalPos[enclosureSrc].y); + return; + } + + if (_portalPos[enclosureSrc].x == _enclosureRect[enclosureSrc].right) { + _characterSubTargetPos[index] = Common::Point(_portalPos[enclosureSrc].x + 1, _portalPos[enclosureSrc].y); + return; + } + + if (_enclosureRect[enclosureSrc].bottom != _enclosureRect[enclosureSrc].top) { + if (_portalPos[enclosureSrc].y == _enclosureRect[enclosureSrc].top) + _characterSubTargetPos[index] = Common::Point(_portalPos[enclosureSrc].x, _portalPos[enclosureSrc].y - 1); + else // CHECKME: Should be a check on y == bottom + _characterSubTargetPos[index] = Common::Point(_portalPos[enclosureSrc].x, _portalPos[enclosureSrc].y + 1); + + return; + } + } + + int mapIndex = (_portalPos[enclosureSrc].y * 64 + _portalPos[enclosureSrc].x) * 4; + assert(mapIndex < 16384); + + int tmpVal = _bufferIsoMap[mapIndex + 3]; + if ((tmpVal & 8) != 0) + _characterSubTargetPos[index] = Common::Point(_portalPos[enclosureSrc].x + 1, _portalPos[enclosureSrc].y); + else if ((tmpVal & 4) != 0) + _characterSubTargetPos[index] = Common::Point(_portalPos[enclosureSrc].x, _portalPos[enclosureSrc].y - 1); + else if ((tmpVal & 2) != 0) + _characterSubTargetPos[index] = Common::Point(_portalPos[enclosureSrc].x, _portalPos[enclosureSrc].y + 1); + else + _characterSubTargetPos[index] = Common::Point(_portalPos[enclosureSrc].x - 1, _portalPos[enclosureSrc].y); + + return; +} + +void LilliputEngine::homeInChooseDirection(int index) { + debugC(2, kDebugEngine, "homeInChooseDirection(%d)", index); + + static const int16 mapArrayMove[4] = {4, -256, 256, -4}; + + _curCharacterTilePos = _scriptHandler->_characterTilePos[index]; + + evaluateDirections(index); + int direction = (_characterDirectionArray[index] ^ 3); + + _homeInDirLikelyhood[direction] -= 8; + byte closeWallFl = 0; + + int mapIndex = ((_curCharacterTilePos.y * 64) + _curCharacterTilePos.x) * 4; + int retVal = 0; + for (int i = 3; i >= 0; i--) { + int mapIndexDiff = mapArrayMove[i]; + assert(mapIndex + mapIndexDiff + 3 < 16384); + if (((_bufferIsoMap[mapIndex + mapIndexDiff + 3] & _doorEntranceMask[i]) != 0) && ((_bufferIsoMap[mapIndex + 3] & _doorExitMask[i]) != 0)) { + if ((_bufferIsoMap[mapIndex + mapIndexDiff + 3] & 0x80) != 0 && (homeInAvoidDeadEnds(i, index) != 0)) { + _homeInDirLikelyhood[i] -= 20; + } + + int tmpVal = ((_characterMobility[index] & 7) ^ 7); + retVal = _cubeFlags[_bufferIsoMap[mapIndex + mapIndexDiff]]; + tmpVal &= retVal; + if (tmpVal == 0) + continue; + } + _homeInDirLikelyhood[i] = -98; + ++closeWallFl; + } + + if (closeWallFl != 0) + _homeInDirLikelyhood[_characterDirectionArray[index]] += 3; + + int tmpVal = -99; + for (int i = 3; i >= 0; i--) { + if (tmpVal < _homeInDirLikelyhood[i]) { + retVal = i; + tmpVal = _homeInDirLikelyhood[i]; + } + } + + _characterDirectionArray[index] = retVal; +} + +byte LilliputEngine::homeInAvoidDeadEnds(int indexb, int indexs) { + debugC(2, kDebugEngine, "homeInAvoidDeadEnds(%d, %d)", indexb, indexs); + + static const int8 constDirX[4] = {1, 0, 0, -1}; + static const int8 constDirY[4] = {0, -1, 1, 0}; + + Common::Point tmpPos = Common::Point(_curCharacterTilePos.x + constDirX[indexb], _curCharacterTilePos.y + constDirY[indexb]); + + int16 idx = checkEnclosure(tmpPos); + if (idx == -1) + return 1; + + if ((tmpPos.x >= _enclosureRect[idx].left) && (tmpPos.x <= _enclosureRect[idx].right) && (tmpPos.y >= _enclosureRect[idx].top) && (tmpPos.y <= _enclosureRect[idx].bottom)) + return 0; + + if ((tmpPos.x >= _enclosureRect[idx].left) && (tmpPos.x <= _enclosureRect[idx].right) && (tmpPos.y >= _enclosureRect[idx].top) && (tmpPos.y <= _enclosureRect[idx].bottom)) + return 0; + + return 1; +} + +int16 LilliputEngine::checkEnclosure(Common::Point pos) { + debugC(2, kDebugEngine, "checkEnclosure(%d, %d)", pos.x, pos.y); + + for (int i = 0; i < _rectNumb; ++i) { + if ((pos.x >= _enclosureRect[i].left) && (pos.x <= _enclosureRect[i].right) && (pos.y >= _enclosureRect[i].top) && (pos.y <= _enclosureRect[i].bottom)) + return i; + } + return -1; +} + +int16 LilliputEngine::checkOuterEnclosure(Common::Point pos) { + debugC(2, kDebugEngine, "checkOuterEnclosure(%d, %d)", pos.x, pos.y); + + for (int i = _rectNumb - 1; i >= 0 ; --i) { + if ((pos.x >= _enclosureRect[i].left) && (pos.x <= _enclosureRect[i].right) && (pos.y >= _enclosureRect[i].top) && (pos.y <= _enclosureRect[i].bottom)) + return i; + } + return -1; +} + +void LilliputEngine::evaluateDirections(int index) { + debugC(2, kDebugEngine, "evaluateDirections(%d)", index); + + static const int8 arrayMoveX[4] = {1, 0, 0, -1}; + static const int8 arrayMoveY[4] = {0, -1, 1, 0}; + + int16 arrayDistance[4]; + + for (int i = 3; i >= 0; i--) { + int16 var1h = _curCharacterTilePos.x + arrayMoveX[i] - _characterSubTargetPos[index].x; + int16 var1l = _curCharacterTilePos.y + arrayMoveY[i] - _characterSubTargetPos[index].y; + arrayDistance[i] = (var1l * var1l) + (var1h * var1h); + } + + for (int i = 0; i < 4; i++) + _homeInDirLikelyhood[i] = 0; + + int8 tmpIndex = 0; + for (int i = 3; i > 0; i--) { + int16 smallestDistance = 0x7FFF; + for (int j = 0; j < 4; j++) { + if (smallestDistance > arrayDistance[j]) { + smallestDistance = arrayDistance[j]; + tmpIndex = j; + } + } + arrayDistance[tmpIndex] = 0x7FFF; + _homeInDirLikelyhood[tmpIndex] = i; + } +} + +void LilliputEngine::addCharToBuf(byte character) { + debugC(2, kDebugEngine, "addCharToBuf(%c)", character); + + _displayStringBuf[_displayStringIndex] = character; + if (_displayStringIndex < 158) + ++_displayStringIndex; +} + +void LilliputEngine::numberToString(int param1) { + debugC(2, kDebugEngine, "numberToString(%d)", param1); + + static const int exp10[6] = {10000, 1000, 100, 10, 1}; + + int var1 = param1; + bool hideZeros = true; + for (int i = 0; i < 5; i++) { + int count = 0; + while (var1 >= 0) { + ++count; + var1 -= exp10[i]; + } + var1 += exp10[i]; + --count; + + byte tmpVal = count + 0x30; + + if (i == 4) + addCharToBuf(tmpVal); + else if ((count != 0) || (!hideZeros)) { + hideZeros = false; + addCharToBuf(tmpVal); + } + } +} + +void LilliputEngine::updateCharPosSequence() { + debugC(2, kDebugEngine, "updateCharPosSequence()"); + + int index = _numCharacters - 1; + byte result; + while (index >= 0) { + result = kSeqRepeat; + while (result & kSeqRepeat) { + if (_scriptHandler->_characterNextSequence[index] == 16) + break; + + uint16 index2 = _scriptHandler->_characterNextSequence[index] + (index * 16); + Common::Point var1 = _scriptHandler->_sequenceArr[index2]; + + // /8, then /2 as the function array is a word array + int16 posSeqType = var1.x / 16; + + switch (posSeqType) { + case 0: // Move + // x stands for moveType, y for poseType + result = sequenceMoveCharacter(index, var1.x, var1.y); + break; + case 1: // Face direction + // x stands for the next direction, y for the poseType + result = sequenceSetCharacterDirection(index, var1.x, var1.y); + break; + case 10: // Seek move target + result = sequenceSeekMovingCharacter(index, var1); + break; + case 11: // Sound + result = sequenceSound(index, var1); + break; + case 12: // Home in target + result = sequenceCharacterHomeIn(index, var1); + break; + case 13: // Character mobility + result = sequenceSetMobility(index, var1); + break; + case 14: // Repeat sequence + result = sequenceRepeat(index, var1, index2); + break; + case 15: // End + result = sequenceEnd(index); + break; + default: + result = kSeqNone; + break; + } + + if ((result & kSeqNoInc) == 0) { + ++_scriptHandler->_characterNextSequence[index]; + if (_scriptHandler->_characterNextSequence[index] == 16) + _scriptHandler->_characterScriptEnabled[index] = 1; + } + } + --index; + } +} + +byte LilliputEngine::sequenceEnd(int index) { + debugC(2, kDebugEngine, "sequenceEnd(%d)", index); + + _scriptHandler->_characterNextSequence[index] = 16; + _scriptHandler->_characterScriptEnabled[index] = 1; + + return kSeqNoInc; +} + +byte LilliputEngine::sequenceRepeat(int index, Common::Point var1, int tmpVal) { + debugC(2, kDebugEngine, "sequenceRepeat(%d, %d - %d, %d)", index, var1.x, var1.y, tmpVal); + + byte counter = var1.y; + if (counter != 0) { + if ((counter & 0xF0) == 0) + counter |= (counter << 4); + + counter -= 16; + _scriptHandler->_sequenceArr[tmpVal] = Common::Point(var1.x, counter); + + if ((counter & 0xF0) == 0) + return kSeqRepeat; + } + + _scriptHandler->_characterNextSequence[index] -= (var1.x & 0x0F); + return kSeqNoInc | kSeqRepeat; +} + +byte LilliputEngine::sequenceSetCharacterDirection(int index, int direction, int poseType) { + debugC(2, kDebugEngine, "sequenceSetCharacterDirection(%d, %d - %d)", index, direction, poseType); + + char newDir = direction & 3; + _characterDirectionArray[index] = newDir; + setCharacterPose(index, poseType); + + return kSeqNone; +} + +byte LilliputEngine::sequenceSetMobility(int index, Common::Point var1) { + debugC(2, kDebugEngine, "sequenceSetMobility(%d, %d - %d)", index, var1.x, var1.y); + + _characterMobility[index] = var1.y; + return kSeqRepeat; +} + +byte LilliputEngine::sequenceSound(int index, Common::Point var1) { + debugC(2, kDebugEngine, "sequenceSound(%d, %d - %d)", index, var1.x, var1.y); + + int param4x = ((index | 0xFF00) >> 8); + _soundHandler->play(var1.y, _scriptHandler->_viewportPos, + _scriptHandler->_characterTilePos[index], Common::Point(param4x, 0)); + return kSeqRepeat; +} + +byte LilliputEngine::sequenceSeekMovingCharacter(int index, Common::Point var1) { + debugC(2, kDebugEngine, "sequenceSeekMovingCharacter(%d, %d - %d)", index, var1.x, var1.y); + + int charIndex = _scriptHandler->_characterSeek[index]; + Common::Point charPos = _scriptHandler->_characterTilePos[charIndex]; + + if ((_characterSubTargetPos[index].x != -1) && (_characterSubTargetPos[index] == _characterTargetPos[index])) + _characterSubTargetPos[index] = charPos; + + _characterTargetPos[index] = charPos; + + return sequenceCharacterHomeIn(index, var1); +} + +void LilliputEngine::checkSpecialCubes() { + debugC(2, kDebugEngine, "checkSpecialCubes()"); + + for (int index1 = _numCharacters - 1; index1 >= 0; index1--) { + // Hack: The original doesn't check if it's disabled, which looks wrong + if ((_scriptHandler->_characterTilePos[index1].x == -1) || (_scriptHandler->_characterTilePos[index1].y == -1)) + continue; + // + + int mapIndex = 3 + (_scriptHandler->_characterTilePos[index1].y * 64 + _scriptHandler->_characterTilePos[index1].x) * 4; + assert((mapIndex >= 0) && (mapIndex < 16384)); + byte var1 = _bufferIsoMap[mapIndex] & 0x40; + + if (var1 == _specialCubes[index1]) + continue; + + _specialCubes[index1] = var1; + if (var1 != 0) + _scriptHandler->_characterScriptEnabled[index1] = 1; + } +} + +void LilliputEngine::handleCharacterTimers() { + debugC(2, kDebugEngine, "handleCharacterTimers()"); + + int index1 = _animationTick + 2; + + for (byte i = 0; i < _numCharacters; i++) { + byte *varPtr = getCharacterAttributesPtr(index1); + if (varPtr[0] != 0) { + if (varPtr[0] == 1) { + varPtr[0] = 0; + } else { + --varPtr[0]; + if (varPtr[0] == 1) + _scriptHandler->_characterScriptEnabled[i] = 1; + } + } + + index1 += 32; + } +} + +void LilliputEngine::keyboard_handleInterfaceShortcuts(bool &forceReturnFl) { + debugC(2, kDebugEngine, "keyboard_handleInterfaceShortcuts()"); + + forceReturnFl = false; + + if (!_keyboard_checkKeyboard()) + return; + + Common::Event event = _keyboard_getch(); + + int8 index = -1; + for (int8 i = 0; i < _interfaceHotspotNumb; i++) { + if (event.kbd.keycode == _keyboardMapping[i]) { + index = i; + break; + } + } + + if (index != -1) { + byte button = 1; + if (event.type == Common::EVENT_KEYUP) + button = 2; + handleInterfaceHotspot(index, button); + forceReturnFl = true; + } +} + +void LilliputEngine::checkNumericCode() { + debugC(2, kDebugEngine, "checkNumericCode()"); + + static bool altKeyFl = false; + static int16 keyCount = 0; + + if (_keyboard_oldIndex == _keyboard_nextIndex) + return; + + Common::Event oldEvent = _keyboard_buffer[_keyboard_oldIndex]; + if ((oldEvent.kbd.keycode == Common::KEYCODE_LALT) || (oldEvent.kbd.keycode == Common::KEYCODE_RALT)) { + if (oldEvent.type == Common::EVENT_KEYDOWN) { + altKeyFl = true; + keyCount = 0; + return; + } else if (oldEvent.type == Common::EVENT_KEYUP) { + altKeyFl = false; + if (keyCount == 3) + _actionType = kCodeEntered; + return; + } + } + + if (keyCount >= 3) + return; + + if ((altKeyFl) && (oldEvent.type == Common::EVENT_KEYDOWN)) { + switch (oldEvent.kbd.keycode) { + case Common::KEYCODE_KP0: + case Common::KEYCODE_KP1: + case Common::KEYCODE_KP2: + case Common::KEYCODE_KP3: + case Common::KEYCODE_KP4: + case Common::KEYCODE_KP5: + case Common::KEYCODE_KP6: + case Common::KEYCODE_KP7: + case Common::KEYCODE_KP8: + case Common::KEYCODE_KP9: + case Common::KEYCODE_0: + case Common::KEYCODE_1: + case Common::KEYCODE_2: + case Common::KEYCODE_3: + case Common::KEYCODE_4: + case Common::KEYCODE_5: + case Common::KEYCODE_6: + case Common::KEYCODE_7: + case Common::KEYCODE_8: + case Common::KEYCODE_9: + _codeEntered[keyCount] = oldEvent.kbd.keycode - Common::KEYCODE_0; + ++keyCount; + break; + default: + break; + } + } +} + +void LilliputEngine::handleGameMouseClick() { + debugC(2, kDebugEngine, "handleGameMouseClick()"); + + checkNumericCode(); + + bool forceReturnFl = false; + keyboard_handleInterfaceShortcuts(forceReturnFl); + if (forceReturnFl) + return; + + if (_mouseButton == 0) { + if (!_mouseClicked) + return; + _mouseClicked = false; + _mouseButton = 2; + } + + int button = _mouseButton; + _mouseButton = 0; + + if (button == 2) { + if (_lastInterfaceHotspotIndex != -1) + handleInterfaceHotspot(_lastInterfaceHotspotIndex, button); + return; + } + + forceReturnFl = false; + checkInterfaceHotspots(forceReturnFl); + if (forceReturnFl) + return; + + Common::Point pos = Common::Point(_mousePos.x - 64, _mousePos.y - 16); + + if ((pos.x < 0) || (pos.x > 255) || (pos.y < 0) || (pos.y > 176)) + return; + + forceReturnFl = false; + checkClickOnCharacter(pos, forceReturnFl); + if (forceReturnFl) + return; + + checkClickOnGameArea(pos); +} + +void LilliputEngine::checkClickOnGameArea(Common::Point pos) { + debugC(2, kDebugEngine, "checkClickOnGameArea(%d, %d)", pos.x, pos.y); + + int x = pos.x - 8; + int y = pos.y - 4; + + x = (x / 16) - 7; + y = (y / 8) - 4; + + int arrowY = (y - x) >> 1; + int arrowX = y - arrowY; + + if ((arrowX >= 0) && (arrowY >= 0) && (arrowX < 8) && (arrowY < 8)) { + arrowX += _scriptHandler->_viewportPos.x; + arrowY += _scriptHandler->_viewportPos.y; + _savedMousePosDivided = Common::Point(arrowX, arrowY); + _actionType = kCubeSelected; + } +} + +void LilliputEngine::checkClickOnCharacter(Common::Point pos, bool &forceReturnFl) { + debugC(2, kDebugEngine, "checkClickOnCharacter(%d, %d)", pos.x, pos.y); + + forceReturnFl = false; + + for (int8 i = 0; i < _numCharacters; i++) { + // check if position is over a character + if ((pos.x >= _characterDisplay[i].x) && (pos.x <= _characterDisplay[i].x + 17) && (pos.y >= _characterDisplay[i].y) && (pos.y <= _characterDisplay[i].y + 17) && (i != _host)) { + _selectedCharacterId = i; + _actionType = kActionGoto; + if (_delayedReactivationAction) + _actionType = kActionTalk; + + forceReturnFl = true; + return; + } + } +} + +void LilliputEngine::checkInterfaceHotspots(bool &forceReturnFl) { + debugC(2, kDebugEngine, "checkInterfaceHotspots()"); + + forceReturnFl = false; + for (int index = _interfaceHotspotNumb - 1; index >= 0; index--) { + if (isMouseOverHotspot(_mousePos, _interfaceHotspots[index])) { + handleInterfaceHotspot(index, 1); + forceReturnFl = true; + return; + } + } +} + +bool LilliputEngine::isMouseOverHotspot(Common::Point mousePos, Common::Point hotspotPos) { + debugC(2, kDebugEngine, "isMouseOverHotspot(%d - %d, %d - %d)", mousePos.x, mousePos.y, hotspotPos.x, hotspotPos.y); + + if ((mousePos.x < hotspotPos.x) || (mousePos.y < hotspotPos.y) || (mousePos.x > hotspotPos.x + 16) || (mousePos.y > hotspotPos.y + 16)) + return false; + + return true; +} + +void LilliputEngine::handleInterfaceHotspot(byte index, byte button) { + debugC(2, kDebugEngine, "handleInterfaceHotspot(%d, %d)", index, button); + + if (_scriptHandler->_interfaceHotspotStatus[index] < kHotspotEnabled) + return; + + _lastInterfaceHotspotIndex = index; + _lastInterfaceHotspotButton = button; + + if (button == 2) { + if (!_delayedReactivationAction) { + _scriptHandler->_interfaceHotspotStatus[index] = kHotspotEnabled; + _actionType = kButtonReleased; + displayInterfaceHotspots(); + } + return; + } + + if (_delayedReactivationAction) { + unselectInterfaceButton(); + return; + } + + unselectInterfaceHotspots(); + _scriptHandler->_interfaceHotspotStatus[index] = kHotspotSelected; + if (_interfaceTwoStepAction[index] == 1) { + _delayedReactivationAction = true; + _displayGreenHand = true; + } else { + _actionType = kButtonPressed; + } + + displayInterfaceHotspots(); +} + +void LilliputEngine::setCharacterPose(int charIdx, int poseIdx) { + debugC(2, kDebugEngine, "setCharacterPose(%d, %d)", charIdx, poseIdx); + + // CHECKME: Add an assert on poseIdx to check if it's between 0 and 31? + int index = (charIdx * 32) + poseIdx; + _scriptHandler->_characterPose[charIdx] = _poseArray[index]; +} + +byte LilliputEngine::sequenceMoveCharacter(int idx, int moveType, int poseType) { + debugC(2, kDebugEngine, "sequenceMoveCharacter(%d, %d - %d)", idx, moveType, poseType); + + setCharacterPose(idx, poseType); + + int index = idx; + switch (moveType) { + case 0: + // No movement + break; + case 1: + moveCharacterSpeed2(index); + break; + case 2: + moveCharacterSpeed4(index); + break; + case 3: + moveCharacterBack2(index); + break; + case 4: + turnCharacter1(index); + break; + case 5: + turnCharacter2(index); + break; + case 6: + moveCharacterUp1(index); + break; + case 7: + moveCharacterUp2(index); + break; + case 8: + moveCharacterDown1(index); + break; + case 9: + moveCharacterDown2(index); + break; + case 10: + moveCharacterSpeed3(index); + break; + default: + // CHECKME: It's so bad it could be an error() + warning("sequenceMoveCharacter - Unexpected value %d", moveType); + } + + return kSeqNone; +} + +void LilliputEngine::turnCharacter1(int index) { + debugC(2, kDebugEngine, "turnCharacter1(%d)", index); + + static const byte nextDirection[4] = {1, 3, 0, 2}; + _characterDirectionArray[index] = nextDirection[_characterDirectionArray[index]]; +} + +void LilliputEngine::turnCharacter2(int index) { + debugC(2, kDebugEngine, "turnCharacter2(%d)", index); + + static const byte nextDirection[4] = {2, 0, 3, 1}; + _characterDirectionArray[index] = nextDirection[_characterDirectionArray[index]]; +} + +void LilliputEngine::moveCharacterUp1(int index) { + debugC(2, kDebugEngine, "moveCharacterUp1(%d)", index); + + _characterPosAltitude[index] += 1; +} + +void LilliputEngine::moveCharacterUp2(int index) { + debugC(2, kDebugEngine, "moveCharacterUp2(%d)", index); + + _characterPosAltitude[index] += 2; +} + +void LilliputEngine::moveCharacterDown1(int index) { + debugC(2, kDebugEngine, "moveCharacterDown1(%d)", index); + + _characterPosAltitude[index] -= 1; +} + +void LilliputEngine::moveCharacterDown2(int index) { + debugC(2, kDebugEngine, "moveCharacterDown2(%d)", index); + + _characterPosAltitude[index] -= 2; +} + +void LilliputEngine::moveCharacterSpeed2(int index) { + debugC(2, kDebugEngine, "moveCharacterSpeed2(%d)", index); + + moveCharacterForward(index, 2); +} + +void LilliputEngine::moveCharacterSpeed4(int index) { + debugC(2, kDebugEngine, "moveCharacterSpeed4(%d)", index); + + moveCharacterForward(index, 4); +} + +void LilliputEngine::moveCharacterBack2(int index) { + debugC(2, kDebugEngine, "moveCharacterBack2(%d)", index); + + moveCharacterForward(index, -2); +} + +void LilliputEngine::moveCharacterSpeed3(int index) { + debugC(2, kDebugEngine, "moveCharacterSpeed3(%d)", index); + + moveCharacterForward(index, 3); +} + +void LilliputEngine::moveCharacterForward(int index, int16 speed) { + debugC(2, kDebugEngine, "moveCharacterForward(%d, %d)", index, speed); + + int16 newX = _characterPos[index].x; + int16 newY = _characterPos[index].y; + switch (_characterDirectionArray[index]) { + case 0: + newX += speed; + break; + case 1: + newY -= speed; + break; + case 2: + newY += speed; + break; + default: + newX -= speed; + break; + } + checkCollision(index, Common::Point(newX, newY), _characterDirectionArray[index]); +} + +void LilliputEngine::checkCollision(int index, Common::Point pos, int direction) { + debugC(2, kDebugEngine, "checkCollision(%d, %d - %d, %d)", index, pos.x, pos.y, direction); + + int16 diffX = pos.x >> 3; + if (((diffX & 0xFF) == _scriptHandler->_characterTilePos[index].x) && ((pos.y >> 3) == _scriptHandler->_characterTilePos[index].y)) { + _characterPos[index] = pos; + return; + } + + if ((pos.x < 0) || (pos.x >= 512) || (pos.y < 0) || (pos.y >= 512)) + return; + + int mapIndex = (_scriptHandler->_characterTilePos[index].y * 64 + _scriptHandler->_characterTilePos[index].x) * 4; + assert(mapIndex < 16384); + + if ((_bufferIsoMap[mapIndex + 3] & _doorExitMask[direction]) == 0) + return; + + mapIndex = ((pos.y & 0xFFF8) << 3) + diffX; + mapIndex <<= 2; + + if ((_bufferIsoMap[mapIndex + 3] & _doorEntranceMask[direction]) == 0) + return; + + byte var1 = _characterMobility[index]; + var1 &= 7; + var1 ^= 7; + + if ((var1 & _cubeFlags[_bufferIsoMap[mapIndex]]) != 0) + return; + + _characterPos[index] = pos; +} + +void LilliputEngine::signalDispatcher(byte type, byte index, int var4) { + debugC(2, kDebugEngine, "signalDispatcher(%d, %d, %d)", type, index, var4); + + if (type == 0) { // Message sent to one target character + sendMessageToCharacter(index, var4); + return; + } + + if (type == 3) { // Broadcast - Sent to all characters + for (int i = _numCharacters - 1; i >= 0; i--) + sendMessageToCharacter(i, var4); + return; + } + + int index2 = var4 & 0xFF; + for (byte i = 0; i < _numCharacters; i++) { + if ((_scriptHandler->_interactions[index2] & 0xFF) >= type) + sendMessageToCharacter(i, var4); + index2 += 40; + } +} + +void LilliputEngine::sendMessageToCharacter(byte index, int var4) { + debugC(2, kDebugEngine, "sendMessageToCharacter(%d, %d)", index, var4); + + if (_characterSignals[index] != -1) { + _signalArr[index] = var4; + } else { + _scriptHandler->_characterScriptEnabled[index] = 1; + _characterSignals[index] = var4; + } +} + +void LilliputEngine::handleSignals() { + debugC(2, kDebugEngine, "handleSignals()"); + + for (byte i = 0; i < _numCharacters; i++) { + if (_signalArr[i] != -1) { + _characterSignals[i] = _signalArr[i]; + _signalArr[i] = -1; + _scriptHandler->_characterScriptEnabled[i] = 1; + } + } + + ++_signalTimer; + + for (int i = 0; i < 10; i++) { + if ((_signalArray[(3 * i) + 1] != -1) && (_signalArray[3 * i] == _signalTimer)) { + int16 var1 = _signalArray[(3 * i) + 1]; + int var4 = _signalArray[(3 * i) + 2]; + _signalArray[(3 * i) + 1] = -1; + + byte type = var1 >> 8; + byte index = var1 & 0xFF; + + signalDispatcher(type, index, var4); + } + } +} + +void LilliputEngine::checkInterfaceActivationDelay() { + debugC(2, kDebugEngine, "checkInterfaceActivationDelay()"); + + if (_animationTick != 1) + return; + + bool needRedraw = false; + for (int i = 0; i < _interfaceHotspotNumb; i++) { + if (_scriptHandler->_interfaceButtonActivationDelay[i] != 0) { + --_scriptHandler->_interfaceButtonActivationDelay[i]; + if (_scriptHandler->_interfaceButtonActivationDelay[i] == 0) { + _scriptHandler->_interfaceHotspotStatus[i] = kHotspotEnabled; + needRedraw = true; + } + } + } + + if (needRedraw) + displayInterfaceHotspots(); +} + +void LilliputEngine::displayHeroismIndicator() { + debugC(2, kDebugEngine, "displayHeroismIndicator()"); + + if (_scriptHandler->_barAttrPtr == NULL) + return; + + int var1 = (_scriptHandler->_barAttrPtr[0] * 25) >> 8; + + if (var1 == _scriptHandler->_heroismLevel) + return; + + int var2 = 1; + if (var1 > _scriptHandler->_heroismLevel) + var1 = 150; + else { + var2 = -1; + var1 = 40; + } + + _scriptHandler->_heroismLevel += var2; + + int index = _scriptHandler->_heroismBarX + (_scriptHandler->_heroismBarBottomY * 320); + + var2 = _scriptHandler->_heroismLevel & 0xFF; + if (var2 != 0) { + for (int i = 0; i < (var2 << 2); i++) { + ((byte *)_mainSurface->getPixels())[index] = var1; + ((byte *)_mainSurface->getPixels())[index + 1] = var1; + ((byte *)_mainSurface->getPixels())[index + 2] = var1; + index -= 320; + } + } + + if (25 - _scriptHandler->_heroismLevel != 0) { + var2 = (25 - _scriptHandler->_heroismLevel) << 2; + for (int i = 0; i < var2; i++) { + ((byte *)_mainSurface->getPixels())[index] = 23; + ((byte *)_mainSurface->getPixels())[index + 1] = 23; + ((byte *)_mainSurface->getPixels())[index + 2] = 23; + index -= 320; + } + } +} + +void LilliputEngine::pollEvent() { + debugC(2, kDebugEngine, "pollEvent()"); + + Common::Event event; + while (_system->getEventManager()->pollEvent(event)) { + switch (event.type) { + case Common::EVENT_MOUSEMOVE: + case Common::EVENT_LBUTTONDOWN: + case Common::EVENT_LBUTTONUP: { + Common::Point newMousePos = Common::Point(CLIP<int>(event.mouse.x, 0, 304), CLIP<int>(event.mouse.y, 0, 184)); + + if (_mousePreviousEventType != event.type) { + _mousePreviousEventType = event.type; + if (_mouseButton != 1) { + _mouseButton = 2; + if (event.type != Common::EVENT_MOUSEMOVE) { + _mouseButton = 1; + _mousePos = Common::Point(newMousePos.x + 5, newMousePos.y + 1); + } + } else { + _mouseClicked = true; + } + } + + if (newMousePos != _oldMousePos) { + _oldMousePos = newMousePos; + _mouseDisplayPos = newMousePos; + } + _lastEventType = event.type; + } + break; + case Common::EVENT_QUIT: + _shouldQuit = true; + break; + case Common::EVENT_KEYUP: + case Common::EVENT_KEYDOWN: { + if ((event.type == _lastKeyPressed.type) && (event.kbd == _lastKeyPressed.kbd)) + break; + + _lastKeyPressed = event; + int nextIndex = (_keyboard_nextIndex + 1) % 8; + if (_keyboard_oldIndex != nextIndex) { + _keyboard_buffer[_keyboard_nextIndex] = event; + _keyboard_nextIndex = nextIndex; + } + + _lastEventType = event.type; + } + break; + default: + break; + } + } +} + +byte *LilliputEngine::loadVGA(Common::String filename, int expectedSize, bool loadPal) { + debugC(1, kDebugEngine, "loadVGA(%s, %d, %d)", filename.c_str(), expectedSize, (loadPal) ? 1 : 0); + + Common::File f; + + if (!f.open(filename)) + error("Missing game file %s", filename.c_str()); + + int remainingSize = f.size(); + if (loadPal) { + for (int i = 0; i < 768; ++i) + _curPalette[i] = f.readByte(); + remainingSize -= 768; + + fixPaletteEntries(_curPalette, 256); + } + + uint8 curByte; + byte *decodeBuffer = (byte *)malloc(expectedSize); + int size = 0; + + for (; (remainingSize > 0) && (size < expectedSize);) { + curByte = f.readByte(); + --remainingSize; + + if (curByte == 0xFF) + break; + + if (curByte & 0x80) { + // Compressed + int compSize = (curByte & 0x7F); + curByte = f.readByte(); + --remainingSize; + + for (int i = 0; i < compSize; ++i) { + decodeBuffer[size] = curByte; + ++size; + if (size == expectedSize) + break; + } + } else { + // Not compressed + int rawSize = (curByte & 0xFF); + for (int i = 0; i < rawSize; ++i) { + decodeBuffer[size] = f.readByte(); + --remainingSize; + ++size; + if (size == expectedSize) + break; + } + } + } + f.close(); + + for (int i = size; i < expectedSize; i++) + decodeBuffer[i] = 0; + + return decodeBuffer; +} + +byte *LilliputEngine::loadRaw(Common::String filename, int filesize) { + debugC(1, kDebugEngine, "loadRaw(%s)", filename.c_str()); + + Common::File f; + + if (!f.open(filename)) + error("Missing game file %s", filename.c_str()); + + byte *res = (byte *)malloc(sizeof(byte) * filesize); + for (int i = 0; i < filesize; ++i) + res[i] = f.readByte(); + + f.close(); + return res; +} + +void LilliputEngine::loadRules() { + debugC(1, kDebugEngine, "loadRules()"); + + static const Common::KeyCode keybMappingArray[26] = { + Common::KEYCODE_a, Common::KEYCODE_b, Common::KEYCODE_c, Common::KEYCODE_d, Common::KEYCODE_e, + Common::KEYCODE_f, Common::KEYCODE_g, Common::KEYCODE_h, Common::KEYCODE_i, Common::KEYCODE_j, + Common::KEYCODE_k, Common::KEYCODE_l, Common::KEYCODE_m, Common::KEYCODE_n, Common::KEYCODE_o, + Common::KEYCODE_p, Common::KEYCODE_q, Common::KEYCODE_r, Common::KEYCODE_s, Common::KEYCODE_t, + Common::KEYCODE_u, Common::KEYCODE_v, Common::KEYCODE_w, Common::KEYCODE_x, Common::KEYCODE_y, + Common::KEYCODE_z}; + Common::File f; + uint16 curWord; + + Common::String filename = "ERULES.PRG"; + Common::Language lang = Common::parseLanguage(ConfMan.get("language")); + + switch (lang) { + case Common::EN_ANY: + break; + case Common::FR_FRA: + filename = "FRULES.PRG"; + break; + case Common::IT_ITA: + filename = "IRULES.PRG"; + break; + case Common::DE_DEU: + filename = "GRULES.PRG"; + break; + default: + warning("unsupported language, switching back to English"); + } + + if (!f.open(filename)) + error("Missing game file %s", filename.c_str()); + + _word10800_ERULES = f.readUint16LE(); + + // Chunk 1 : Sequences + int size = f.readUint16LE(); + _sequencesArr = (byte *)malloc(sizeof(byte) * size); + for (int i = 0; i < size; ++i) + _sequencesArr[i] = f.readByte(); + + // Chunk 2 : Characters + _numCharacters = (f.readUint16LE() & 0xFF); + assert(_numCharacters <= 40); + + for (int i = _numCharacters, j = 0; i != 0; i--, j++) { + curWord = f.readUint16LE(); + if (curWord != 0xFFFF) + curWord = (curWord << 3) + 4; + _characterPos[j].x = curWord; + + curWord = f.readUint16LE(); + if (curWord != 0xFFFF) + curWord = (curWord << 3) + 4; + _characterPos[j].y = curWord; + + _characterPosAltitude[j] = (f.readUint16LE() & 0xFF); + _characterFrameArray[j] = f.readUint16LE(); + _characterCarried[j] = (int8)f.readByte(); + _characterBehindDist[j] = (int8)f.readByte(); + _characterAboveDist[j] = f.readByte(); + _spriteSizeArray[j] = f.readByte(); + _characterDirectionArray[j] = f.readByte(); + _characterMobility[j] = f.readByte(); + _characterTypes[j] = f.readByte(); + _characterBehaviour[j] = f.readByte(); + _characterHomePos[j].x = f.readByte(); + _characterHomePos[j].y = f.readByte(); + + for (int k = 0; k < 32; k++) + _characterVariables[(j * 32) + k] = f.readByte(); + + for (int k = 0; k < 32; k++) + _poseArray[(j * 32) + k] = f.readByte(); + } + + // Chunk 3 & 4 : Packed strings & associated indexes + _packedStringNumb = f.readSint16LE(); + curWord = f.readSint16LE(); + + _packedStringIndex = (int *)malloc(sizeof(int) * _packedStringNumb); + for (int i = 0; i < _packedStringNumb; ++i) + _packedStringIndex[i] = f.readUint16LE(); + + _packedStrings = (char *)malloc(curWord); + for (int i = 0; i < curWord; ++i) + _packedStrings[i] = f.readByte(); + + // Chunk 5: Scripts + // Use byte instead of int, therefore multiply by two the size. + // This is for converting it into a memory read stream + _initScriptSize = f.readUint16LE() * 2; + _initScript = (byte *)malloc(_initScriptSize); + for (int i = 0; i < _initScriptSize; ++i) + _initScript[i] = f.readByte(); + + // Chunk 6: Menu Script + _menuScriptSize = f.readUint16LE() * 2; + _menuScript = (byte *)malloc(sizeof(byte) * _menuScriptSize); + for (int i = 0; i < _menuScriptSize; ++i) + _menuScript[i] = f.readByte(); + + // Chunk 7 & 8: Game scripts and indexes + _gameScriptIndexSize = f.readUint16LE(); + // Added one position to keep the total size too, as it's useful later + _arrayGameScriptIndex = (int *)malloc(sizeof(int) * (_gameScriptIndexSize + 1)); + for (int i = 0; i < _gameScriptIndexSize; ++i) + _arrayGameScriptIndex[i] = f.readUint16LE(); + + curWord = f.readUint16LE(); + _arrayGameScriptIndex[_gameScriptIndexSize] = curWord; + + _arrayGameScripts = (byte *)malloc(sizeof(byte) * curWord); + for (int i = 0; i < curWord; ++i) + _arrayGameScripts[i] = f.readByte(); + + // Chunk 9 : Cube flags + for (int i = 0; i < 60; i++) + _cubeFlags[i] = f.readByte(); + + // Chunk 10 & 11 : Lists + _listNumb = f.readByte(); + assert(_listNumb <= 20); + + if (_listNumb != 0) { + _listIndex = (int16 *)malloc(sizeof(int16) * _listNumb); + int totalSize = 0; + for (int i = 0; i < _listNumb; ++i) { + _listIndex[i] = totalSize; + totalSize += f.readByte(); + } + if (totalSize != 0) { + _listArr = (byte *)malloc(sizeof(byte) * totalSize); + for (int i = 0; i < totalSize; i++) + _listArr[i] = f.readByte(); + } + } + + // Chunk 12 + _rectNumb = f.readUint16LE(); + assert((_rectNumb >= 0) && (_rectNumb <= 40)); + + for (int i = 0; i < _rectNumb; i++) { + _enclosureRect[i].right = (int16)f.readByte(); + _enclosureRect[i].left = (int16)f.readByte(); + _enclosureRect[i].bottom = (int16)f.readByte(); + _enclosureRect[i].top = (int16)f.readByte(); + + int16 tmpValY = (int16)f.readByte(); + int16 tmpValX = (int16)f.readByte(); + _keyPos[i] = Common::Point(tmpValX, tmpValY); + + tmpValY = (int16)f.readByte(); + tmpValX = (int16)f.readByte(); + _portalPos[i] = Common::Point(tmpValX, tmpValY); + } + + // Chunk 13 + _interfaceHotspotNumb = f.readUint16LE(); + for (int i = 0 ; i < 20; i++) + _interfaceTwoStepAction[i] = f.readByte(); + + for (int i = 0 ; i < 20; i++) + _interfaceHotspots[i].x = f.readSint16LE(); + + for (int i = 0 ; i < 20; i++) + _interfaceHotspots[i].y = f.readSint16LE(); + + for (int i = 0; i < 20; i++) { + byte curByte = f.readByte(); + + if (curByte == 0x20) + _keyboardMapping[i] = Common::KEYCODE_SPACE; + else if (curByte == 0xD) + _keyboardMapping[i] = Common::KEYCODE_RETURN; + // Hack to avoid xlat out of bounds + else if (curByte == 0xFF) + _keyboardMapping[i] = Common::KEYCODE_INVALID; // 0x21; ? + // Hack to avoid xlat out of bounds + else if (curByte == 0x00) + _keyboardMapping[i] = Common::KEYCODE_INVALID; // 0xB4; ? + else { + assert((curByte > 0x40) && (curByte <= 0x41 + 26)); + _keyboardMapping[i] = keybMappingArray[curByte - 0x41]; + } + } + f.close(); +} + +void LilliputEngine::displayVGAFile(Common::String fileName) { + debugC(1, kDebugEngine, "displayVGAFile(%s)", fileName.c_str()); + + byte *buffer = loadVGA(fileName, 64000, true); + memcpy(_mainSurface->getPixels(), buffer, 320*200); + _system->copyRectToScreen((byte *)_mainSurface->getPixels(), 320, 0, 0, 320, 200); + _system->updateScreen(); +} + +void LilliputEngine::fixPaletteEntries(uint8 *palette, int num) { + debugC(1, kDebugEngine, "fixPaletteEntries(palette, %d)", num); + // Color values are coded on 6bits (for old 6bits DAC) + for (int32 i = 0; i < num * 3; i++) { + int32 col = palette[i]; + assert(col < 64); + + col = (col << 2) | (col >> 4); + if (col > 255) + col = 255; + palette[i] = col; + } +} + +void LilliputEngine::initPalette() { + debugC(1, kDebugEngine, "initPalette()"); + + for (int i = 0; i < 768; i++) + _curPalette[i] = _basisPalette[i]; + + fixPaletteEntries(_curPalette, 256); + _system->getPaletteManager()->setPalette(_curPalette, 0, 256); +} + +void LilliputEngine::setCurrentCharacter(int index) { + debugC(1, kDebugEngine, "setCurrentCharacter(%d)", index); + + assert(index < 40); + _currentScriptCharacter = index; + _currentScriptCharacterPos = Common::Point(_characterPos[index].x >> 3, _characterPos[index].y >> 3); + _currentCharacterAttributes = getCharacterAttributesPtr(_currentScriptCharacter * 32); +} + +void LilliputEngine::unselectInterfaceButton() { + debugC(1, kDebugEngine, "unselectInterfaceButton()"); + + _delayedReactivationAction = false; + _displayGreenHand = false; + _lastInterfaceHotspotButton = 0; + unselectInterfaceHotspots(); + displayInterfaceHotspots(); +} + +void LilliputEngine::handleMenu() { + debugC(1, kDebugEngine, "handleMenu()"); + + if (_actionType == kActionNone) + return; + + if (_delayedReactivationAction && (_actionType != kActionTalk)) + return; + + setCurrentCharacter(_host); + debugC(1, kDebugScriptTBC, "========================== Menu Script =============================="); + _scriptHandler->runMenuScript(ScriptStream(_menuScript, _menuScriptSize)); + debugC(1, kDebugScriptTBC, "========================== End of Menu Script=============================="); + _savedMousePosDivided = Common::Point(-1, -1); + _selectedCharacterId = -1; + + if (_actionType == kActionTalk) + unselectInterfaceButton(); + + _actionType = kActionNone; +} + +void LilliputEngine::handleGameScripts() { + debugC(1, kDebugEngine, "handleGameScripts()"); + + int index = _nextCharacterIndex; + int i; + for (i = 0; (_scriptHandler->_characterScriptEnabled[index] == 0) && (i < _numCharacters); i++) { + ++index; + if (index >= _numCharacters) + index = 0; + } + + if (i > _numCharacters) + return; + + _nextCharacterIndex = (index + 1) % _numCharacters; + + _scriptHandler->_characterScriptEnabled[index] = 0; + setCurrentCharacter(index); + + _waitingSignal = _characterSignals[index] >> 8; + _waitingSignalCharacterId = _characterSignals[index] & 0xFF; + _characterSignals[index] = -1; + _newModesEvaluatedNumber = 0; + + int tmpVal = _characterBehaviour[index]; + if (tmpVal == 0xFF) + return; + + /* Decompiler follows + + //_scriptHandler->listAllTexts(); + + debugC(1, kDebugEngineTBC, "================= Menu Script =================="); + ScriptStream script = ScriptStream(_menuScript, _menuScriptSize); + _scriptHandler->disasmScript(script); + debugC(1, kDebugEngineTBC, "============= End Menu Script =================="); + + + for (int i = 0; i < _gameScriptIndexSize; i++) { + assert(tmpVal < _gameScriptIndexSize); + debugC(1, kDebugEngineTBC, "================= Game Script %d ==================", i); + ScriptStream script = ScriptStream(&_arrayGameScripts[_arrayGameScriptIndex[i]], _arrayGameScriptIndex[i + 1] - _arrayGameScriptIndex[i]); + _scriptHandler->disasmScript(script); + debugC(1, kDebugEngineTBC, "============= End Game Script %d ==================", i); + } + + while (1); + */ + + //i = index; + //debugC(1, kDebugEngineTBC, "before char %d, pos %d %d, var0 %d, var1 %d, var2 %d var16 %d, script enabled %d", i, _characterPositionX[i], _characterPositionY[i], *getCharacterVariablesPtr(i * 32 + 0), *getCharacterVariablesPtr(i * 32 + 1), *getCharacterVariablesPtr(i * 32 + 2), *getCharacterVariablesPtr(i * 32 + 22), _scriptHandler->_characterScriptEnabled[i]); + + assert(tmpVal < _gameScriptIndexSize); + debugC(1, kDebugEngine, "================= Game Script %d for character %d ==================", tmpVal, index); + _scriptHandler->runScript(ScriptStream(&_arrayGameScripts[_arrayGameScriptIndex[tmpVal]], _arrayGameScriptIndex[tmpVal + 1] - _arrayGameScriptIndex[tmpVal])); + debugC(1, kDebugEngine, "============= End Game Script %d for character %d ==================", tmpVal, index); + + //warning("dump char stat"); + //debugC(1, kDebugEngineTBC, "after char %d, pos %d %d, var0 %d, var1 %d, var2 %d var16 %d, script enabled %d", i, _characterPositionX[i], _characterPositionY[i], *getCharacterVariablesPtr(i * 32 + 0), *getCharacterVariablesPtr(i * 32 + 1), *getCharacterVariablesPtr(i * 32 + 2), *getCharacterVariablesPtr(i * 32 + 22), _scriptHandler->_characterScriptEnabled[i]); +} + +Common::Error LilliputEngine::run() { + debugC(1, kDebugEngine, "run()"); + + s_Engine = this; + initialize(); + initGraphics(320, 200); + _mainSurface = new Graphics::Surface(); + _mainSurface->create(320, 200, Graphics::PixelFormat::createFormatCLUT8()); + + // Setup mixer + syncSoundSettings(); + _soundHandler->init(); + + // Init palette + initPalette(); + + // Load files. In the original, the size was hardcoded + _bufferIdeogram = loadVGA("IDEOGRAM.VGA", 25600, false); + _bufferMen = loadVGA("MEN.VGA", 61440, false); + _bufferMen2 = loadVGA("MEN2.VGA", 61440, false); + _bufferIsoChars = loadVGA("ISOCHARS.VGA", 4096, false); + _bufferIsoMap = loadRaw("ISOMAP.DTA", 16384); + _normalCursor = &_bufferIdeogram[80 * 16 * 16]; + _greenCursor = &_bufferIdeogram[81 * 16 * 16]; + + CursorMan.replaceCursor(_normalCursor, 16, 16, 0, 0, 0); + CursorMan.showMouse(true); + + loadRules(); + + _lastTime = _system->getMillis(); + _scriptHandler->runScript(ScriptStream(_initScript, _initScriptSize)); + + while (!_shouldQuit) { + handleMenu(); + handleGameScripts(); + // To be removed when handled in the previous fonctions + update(); + } + + return Common::kNoError; +} + +void LilliputEngine::initialize() { + debugC(1, kDebugEngine, "initialize"); + + _rnd = new Common::RandomSource("robin"); + _rnd->setSeed(42); // Kick random number generator + _shouldQuit = false; + + for (int i = 0; i < 4; i++) { + _smallAnims[i]._active = false; + _smallAnims[i]._pos = Common::Point(0, 0); + for (int j = 0; j < 8; j ++) + _smallAnims[i]._frameIndex[j] = 0; + } +} + +byte *LilliputEngine::getCharacterAttributesPtr(int16 index) { + debugC(1, kDebugEngine, "getCharacterVariablesPtr(%d)", index); + + assert((index > -3120) && (index < 1400)); + if (index >= 0) + return &_characterVariables[index]; + else + return &_characterVariables[1400 - index]; +} + +void LilliputEngine::syncSoundSettings() { + Engine::syncSoundSettings(); + +// _sound->syncVolume(); +} + +Common::String LilliputEngine::getSavegameFilename(int slot) { + return _targetName + Common::String::format("-%02d.SAV", slot); +} + +Common::Event LilliputEngine::_keyboard_getch() { + warning("getch()"); + while(_keyboard_nextIndex == _keyboard_oldIndex) + pollEvent(); + + Common::Event tmpEvent = _keyboard_buffer[_keyboard_oldIndex]; + _keyboard_oldIndex = (_keyboard_oldIndex + 1) % 8; + + return tmpEvent; +} + +bool LilliputEngine::_keyboard_checkKeyboard() { + return (_keyboard_nextIndex != _keyboard_oldIndex); +} + +void LilliputEngine::_keyboard_resetKeyboardBuffer() { + _keyboard_nextIndex = _keyboard_oldIndex = 0; +} + +} // End of namespace Lilliput diff --git a/engines/lilliput/lilliput.h b/engines/lilliput/lilliput.h new file mode 100644 index 0000000000..cb4e43c3da --- /dev/null +++ b/engines/lilliput/lilliput.h @@ -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. + * + */ + +#ifndef LILLIPUT_LILLIPUT_H +#define LILLIPUT_LILLIPUT_H + +#include "lilliput/console.h" +#include "lilliput/script.h" +#include "lilliput/sound.h" +#include "lilliput/stream.h" + +#include "common/file.h" +#include "common/rect.h" +#include "common/events.h" + +#include "engines/engine.h" +#include "graphics/palette.h" +#include "graphics/surface.h" + +namespace Common { +class RandomSource; +} + +/** + * This is the namespace of the Lilliput engine. + * + * Status of this engine: + * - Adventures of Robin Hood is mostly working without sound + * + * Games using this engine: + * - Adventures of Robin Hood + * - Rome: Pathway to Rome + */ +namespace Lilliput { + +static const int kSavegameVersion = 1; + +enum GameType { + kGameTypeNone = 0, + kGameTypeRobin, + kGameTypeRome +}; + +enum LilliputDebugChannels { + kDebugEngine = 1 << 0, + kDebugScript = 1 << 1, + kDebugSound = 1 << 2, + kDebugEngineTBC = 1 << 3, + kDebugScriptTBC = 1 << 4 +}; + +enum InterfaceHotspotStatus { + kHotspotOff = 0, + kHotspotDisabled = 1, + kHotspotEnabled = 2, + kHotspotSelected = 3 +}; + +#define kSeqNone 0 +#define kSeqNoInc 1 << 0 +#define kSeqRepeat 1 << 1 + +struct LilliputGameDescription; + +struct SmallAnim { + bool _active; + Common::Point _pos; + int16 _frameIndex[8]; +}; + +class LilliputEngine : public Engine { +public: + LilliputEngine(OSystem *syst, const LilliputGameDescription *gd); + ~LilliputEngine(); + + OSystem *_system; + + GUI::Debugger *getDebugger(); + + Common::RandomSource *_rnd; + LilliputScript *_scriptHandler; + LilliputSound *_soundHandler; + Graphics::Surface *_mainSurface; + + SmallAnim _smallAnims[4]; + int _smallAnimsFrameIndex; + + byte _handleOpcodeReturnCode; + byte _keyDelay; + byte _lastAnimationTick; + byte _animationTick; + Common::Point _nextDisplayCharacterPos; + byte _int8Timer; + Common::Event _lastKeyPressed; + Common::EventType _lastEventType; + byte _keyboard_nextIndex; + byte _keyboard_oldIndex; + Common::Event _keyboard_buffer[8]; + byte _byte12A05; + bool _refreshScreenFlag; + byte _byte16552; + int8 _lastInterfaceHotspotIndex; + byte _lastInterfaceHotspotButton; // Unused: set by 2 functions, but never used elsewhere + byte _debugFlag; // Mostly useless, as the associated functions are empty + byte _debugFlag2; // Unused byte, set by an opcode + + byte _codeEntered[3]; + char _homeInDirLikelyhood[4]; + byte *_bufferIsoMap; + byte *_bufferCubegfx; + byte *_bufferMen; + byte *_bufferMen2; + byte *_bufferIsoChars; + byte *_bufferIdeogram; + byte *_normalCursor; + byte *_greenCursor; + byte _curPalette[768]; + byte _displayStringBuf[160]; + + bool _saveFlag; + bool _displayMap; + + int _word10800_ERULES; + byte _numCharacters; + Common::Point _currentScriptCharacterPos; + int _nextCharacterIndex; + int8 _waitingSignal; + int8 _waitingSignalCharacterId; + uint16 _newModesEvaluatedNumber; + Common::Point _savedSurfaceUnderMousePos; + bool _displayGreenHand; + bool _isCursorGreenHand; + int _currentDisplayCharacter; + int _displayStringIndex; + int _signalTimer; + Common::Point _curCharacterTilePos; + + int16 _mapSavedPixelIndex[40]; + byte _mapSavedPixel[40]; + int16 _characterSignals[40]; + int16 _signalArr[40]; + int16 _signalArray[30]; + + byte *_sequencesArr; + int16 _currentScriptCharacter; + Common::Point _characterPos[40]; + int8 _characterPosAltitude[40]; + int16 _characterFrameArray[40]; + int8 _characterCarried[40]; + int8 _characterBehindDist[40]; + byte _characterAboveDist[40]; + byte _spriteSizeArray[40]; + byte _characterDirectionArray[40]; + byte _characterMobility[40]; + byte _characterTypes[40]; + byte _characterBehaviour[40]; + Common::Point _characterHomePos[40]; + byte _characterVariables[1400 + 3120]; + byte *_currentCharacterAttributes; + byte _poseArray[40 * 32]; + int *_packedStringIndex; + int _packedStringNumb; + char *_packedStrings; + byte *_initScript; + int _initScriptSize; + byte *_menuScript; + int _menuScriptSize; + int *_arrayGameScriptIndex; + int _gameScriptIndexSize; + byte *_arrayGameScripts; + byte _cubeFlags[60]; + byte _listNumb; + int16 *_listIndex; + byte *_listArr; + int16 _rectNumb; + Common::Rect _enclosureRect[40]; + Common::Point _keyPos[40]; + Common::Point _portalPos[40]; + int _interfaceHotspotNumb; + byte _interfaceTwoStepAction[20]; + Common::Point _interfaceHotspots[20]; + Common::KeyCode _keyboardMapping[20]; + Common::Point _characterTargetPos[40]; + byte _savedSurfaceUnderMouse[16 * 16]; + byte _charactersToDisplay[40]; + Common::Point _characterRelativePos[40]; + Common::Point _characterDisplay[40]; + int8 _characterMagicPuffFrame[40]; + Common::Point _characterSubTargetPos[40]; + byte _specialCubes[40]; + byte _doorEntranceMask[4]; + byte _doorExitMask[4]; + byte _savedSurfaceGameArea1[176 * 256]; // 45056 + byte _savedSurfaceGameArea2[176 * 256]; // 45056 + byte _savedSurfaceGameArea3[176 * 256]; // 45056 + byte _savedSurfaceSpeech[16 * 252]; + + const LilliputGameDescription *_gameDescription; + uint32 getFeatures() const; + const char *getGameId() const; + + void newInt8(); + void update(); + + void display16x16IndexedBuf(byte *buf, int index, Common::Point pos, bool transparent = true, bool updateScreen = true); + void display16x16Buf(byte *buf, Common::Point pos, bool transparent = true, bool updateScreen = true); + void fill16x16Rect(byte col, Common::Point pos); + void saveSurfaceGameArea(); + void saveSurfaceSpeech(); + void displayInterfaceHotspots(); + void displayLandscape(); + void displaySpeechBubble(); + void displaySpeech(byte *buf); + void initGameAreaDisplay(); + void displayIsometricBlock(byte *buf, int var1, int posX, int posY, int var3); + void displayGameArea(); + void prepareGameArea(); + void displayRefreshScreen(); + void restoreSurfaceSpeech(); + void displayCharacterStatBar(int8 type, int16 averagePosX, int8 score, int16 posY); + void displayCharacter(int index, Common::Point pos, int flags); + void displayString(byte *buf, Common::Point pos); + void displayChar(int index, int var1); + void displaySmallAnims(); + void displaySmallIndexedAnim(byte index, byte subIndex); + + void unselectInterfaceHotspots(); + void startNavigateFromMap(); + void resetSmallAnims(); + void paletteFadeOut(); + void paletteFadeIn(); + + void sortCharacters(); + void scrollToViewportCharacterTarget(); + void viewportScrollTo(Common::Point goalPos); + void checkSpeechClosing(); + void updateCharPosSequence(); + void evaluateDirections(int index); + byte homeInAvoidDeadEnds(int indexb, int indexs); + void signalDispatcher(byte type, byte index, int var4); + void sendMessageToCharacter(byte index, int var4); + int16 checkEnclosure(Common::Point pos); + int16 checkOuterEnclosure(Common::Point pos); + byte sequenceSetMobility(int index, Common::Point var1); + byte sequenceEnd(int index); + void homeInPathFinding(int index); + + void renderCharacters(byte *buf, Common::Point pos); + + void checkNumericCode(); + void keyboard_handleInterfaceShortcuts(bool &forceReturnFl); + byte sequenceCharacterHomeIn(int index, Common::Point param1); + byte getDirection(Common::Point param1, Common::Point param2); + void addCharToBuf(byte character); + void numberToString(int param1); + void handleCharacterTimers(); + byte sequenceMoveCharacter(int idx, int moveType, int poseType); + void setCharacterPose(int idx, int poseIdx); + void checkSpecialCubes(); + void checkInteractions(); + byte sequenceSetCharacterDirection(int index, int direction, int poseType); + void handleSignals(); + void checkInterfaceActivationDelay(); + int16 checkObstacle(int x1, int y1, int x2, int y2); + void displayCharactersOnMap(); + void restoreMapPoints(); + void displayHeroismIndicator(); + void handleGameMouseClick(); + void handleInterfaceHotspot(byte index, byte button); + void checkInterfaceHotspots(bool &forceReturnFl); + bool isMouseOverHotspot(Common::Point mousePos, Common::Point hotspotPos); + void checkClickOnCharacter(Common::Point pos, bool &forceReturnFl); + void checkClickOnGameArea(Common::Point pos); + void displaySpeechBubbleTail(Common::Point displayPos); + void displaySpeechBubbleTailLine(Common::Point pos, int var2); + void displaySpeechLine(int vgaIndex, byte *srcBuf, int &bufIndex); + void checkMapClosing(bool &forceReturnFl); + void turnCharacter1(int index); + void turnCharacter2(int index); + void moveCharacterUp1(int index); + void moveCharacterUp2(int index); + void moveCharacterDown1(int index); + void moveCharacterDown2(int index); + void moveCharacterSpeed2(int index); + void moveCharacterSpeed4(int index); + void moveCharacterBack2(int index); + void moveCharacterSpeed3(int index); + void moveCharacterForward(int index, int16 speed); + void checkCollision(int index, Common::Point pos, int direction); + byte sequenceSeekMovingCharacter(int index, Common::Point var1); + byte sequenceSound(int index, Common::Point var1); + byte sequenceRepeat(int index, Common::Point var1, int tmpVal); + void homeInChooseDirection(int index); + + void initGame(const LilliputGameDescription *gd); + byte *loadVGA(Common::String filename, int fileSize, bool loadPal); + byte *loadRaw(Common::String filename, int filesize); + void loadRules(); + + void displayVGAFile(Common::String fileName); + void initPalette(); + void fixPaletteEntries(uint8 *palette, int num); + + GameType getGameType() const; + Common::Platform getPlatform() const; + + bool hasFeature(EngineFeature f) const; + const char *getCopyrightString() const; + + Common::String getSavegameFilename(int slot); + void syncSoundSettings(); + + Common::Point _mousePos; + Common::Point _oldMousePos; + Common::Point _mouseDisplayPos; + int _mouseButton; + bool _mouseClicked; + Common::EventType _mousePreviousEventType; + Common::Point _savedMousePosDivided; + int _skipDisplayFlag1; + int _skipDisplayFlag2; + + byte _actionType; + bool _delayedReactivationAction; + int8 _selectedCharacterId; + byte _numCharactersToDisplay; + int16 _host; + bool _shouldQuit; + + void pollEvent(); + void setCurrentCharacter(int index); + void unselectInterfaceButton(); + void moveCharacters(); + void setNextDisplayCharacter(int var1); + void handleGameScripts(); + + // Added by Strangerke + byte *getCharacterAttributesPtr(int16 index); + + // Temporary stubs + Common::Event _keyboard_getch(); + bool _keyboard_checkKeyboard(); + void _keyboard_resetKeyboardBuffer(); + +protected: + Common::EventManager *_eventMan; + int _lastTime; + + // Engine APIs + Common::Error run(); + void handleMenu(); + +private: + static LilliputEngine *s_Engine; + + LilliputConsole *_console; + GameType _gameType; + Common::Platform _platform; + + void initialize(); +}; + +} // End of namespace Lilliput + +#endif diff --git a/engines/lilliput/module.mk b/engines/lilliput/module.mk new file mode 100644 index 0000000000..8a095e8045 --- /dev/null +++ b/engines/lilliput/module.mk @@ -0,0 +1,20 @@ +MODULE := engines/lilliput + +MODULE_OBJS = \ + console.o \ + detection.o \ + lilliput.o \ + script.o \ + sound.o \ + stream.o + +MODULE_DIRS += \ + engines/lilliput + +# This module can be built as a plugin +ifeq ($(ENABLE_LILLIPUT), DYNAMIC_PLUGIN) +PLUGIN := 1 +endif + +# Include common rules +include $(srcdir)/rules.mk diff --git a/engines/lilliput/script.cpp b/engines/lilliput/script.cpp new file mode 100644 index 0000000000..cc78c31d37 --- /dev/null +++ b/engines/lilliput/script.cpp @@ -0,0 +1,3355 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can 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 "lilliput/lilliput.h" +#include "lilliput/script.h" +#include "common/debug.h" + +#include "common/system.h" +#include <climits> + +namespace Lilliput { + +LilliputScript::LilliputScript(LilliputEngine *vm) : _vm(vm), _currScript(NULL) { + _cubeSet = 0; + _lastRandomValue = 0; + _scriptForVal = 0; + _textVarNumber = 0; + _speechDisplaySpeed = 3; + _speechTimer = 0; + _word16F00_characterId = -1; + _monitoredCharacter = 0; + _viewportCharacterTarget = -1; + _heroismBarX = 0; + _heroismBarBottomY = 0; + _viewportPos.x = 0; + _viewportPos.y = 0; + _currentSpeechId = 0; + _monitoredAttr[0] = 0; + _monitoredAttr[1] = 1; + _monitoredAttr[2] = 2; + _monitoredAttr[3] = 3; + _barAttrPtr = NULL; + _word1825E = Common::Point(0, 0); + + for (int i = 0; i < 20; i++) { + _interfaceHotspotStatus[i] = kHotspotOff; + _interfaceButtonActivationDelay[i] = 0; + } + + for (int i = 0; i < 32; i++) { + _newEvaluatedModes[i]._mode = 0; + _newEvaluatedModes[i]._priority = 0; + } + + for (int i = 0; i < 40; i++) { + _characterScriptEnabled[i] = 1; + _characterMapPixelColor[i] = 15; + _characterPose[i] = 0; + _characterNextSequence[i] = 16; + _characterLastSequence[i] = -1; + _characterTilePos[i] = Common::Point(0, 0); + _array122C1[i] = 0; + } + + for (int i = 0; i < 640; i++) { + _sequenceArr[i] = Common::Point(-1, -1); + } + + for (int i = 0; i < 1600; i++) + _interactions[i] = 0; + + _heroismLevel = 0; + _talkingCharacter = -1; + _byte16F05_ScriptHandler = 0; + _word18821 = 0; +} + +LilliputScript::~LilliputScript() { +} + +byte LilliputScript::handleOpcodeType1(int curWord) { + debugC(2, kDebugScript, "handleOpcodeType1(0x%x)", curWord); + switch (curWord) { + case 0x0: + return OC_checkCharacterGoalPos(); + break; + case 0x1: + return OC_comparePos(); + break; + case 0x2: + return OC_checkIsoMap3(); + break; + case 0x3: + return OC_compareCharacterVariable(); + break; + case 0x4: + return OC_CompareLastRandomValue(); + break; + case 0x5: + return OC_getRandom(); + break; + case 0x6: + return OC_for(); + break; + case 0x7: + return OC_compCurrentSpeechId(); + break; + case 0x8: + return OC_checkSaveFlag(); + break; + case 0x9: + return OC_compScriptForVal(); + break; + case 0xA: + return OC_isCarrying(); + break; + case 0xB: + return OC_CompareCharacterVariables(); + break; + case 0xC: + return OC_compareCoords_1(); + break; + case 0xD: + return OC_compareCoords_2(); + break; + case 0xE: + return OC_CompareDistanceFromCharacterToPositionWith(); + break; + case 0xF: + return OC_compareRandomCharacterId(); + break; + case 0x10: + return OC_IsCurrentCharacterIndex(); + break; + case 0x11: + return OC_hasVisibilityLevel(); + break; + case 0x12: + return OC_hasGainedVisibilityLevel(); + break; + case 0x13: + return OC_hasReducedVisibilityLevel(); + break; + case 0x14: + return OC_isHost(); + break; + case 0x15: + return OC_isSequenceActive(); + break; + case 0x16: + return OC_isSequenceFinished(); + break; + case 0x17: + return OC_CompareMapValueWith(); + break; + case 0x18: + return OC_IsCharacterValid(); + break; + case 0x19: + return OC_CheckWaitingSignal(); + break; + case 0x1A: + return OC_CurrentCharacterVar0AndVar1Equals(); + break; + case 0x1B: + return OC_CurrentCharacterVar0Equals(); + break; + case 0x1C: + return OC_checkLastInterfaceHotspotIndexMenu13(); + break; + case 0x1D: + return OC_checkLastInterfaceHotspotIndexMenu2(); + break; + case 0x1E: + return OC_CompareNumberOfCharacterWithVar0Equals(); + break; + case 0x1F: + return OC_IsPositionInViewport(); + break; + case 0x20: + return OC_CompareGameVariables(); + break; + case 0x21: + return OC_skipNextOpcode(); + break; + case 0x22: + return OC_CheckCurrentCharacterAttr2(); + break; + case 0x23: + return OC_CheckCurrentCharacterType(); + break; + case 0x24: + return OC_CheckCurrentCharacterAttr0And(); + break; + case 0x25: + return OC_IsCurrentCharacterAttr0LessEqualThan(); + break; + case 0x26: + return OC_isCarried(); + break; + case 0x27: + return OC_CheckCurrentCharacterAttr1(); + break; + case 0x28: + return OC_isCurrentCharacterSpecial(); + break; + case 0x29: + return OC_CurrentCharacterAttr3Equals1(); + break; + case 0x2A: + return OC_checkCharacterDirection(); + break; + case 0x2B: + return OC_checkLastInterfaceHotspotIndex(); + break; + case 0x2C: + return OC_checkSelectedCharacter(); + break; + case 0x2D: + return OC_checkDelayedReactivation(); + break; + case 0x2E: + return OC_checkTargetReached(); + break; + case 0x2F: + return OC_checkFunctionKeyPressed(); + break; + case 0x30: + return OC_checkCodeEntered(); + break; + case 0x31: + return OC_checkViewPortCharacterTarget(); + break; + default: + error("Unexpected opcode %d", curWord); + break; + } +} + +void LilliputScript::handleOpcodeType2(int curWord) { + debugC(2, kDebugScript, "handleOpcodeType2(0x%x)", curWord); + switch (curWord) { + case 0x0: + OC_setWord18821(); + break; + case 0x1: + OC_ChangeIsoMap(); + break; + case 0x2: + OC_startSpeech(); + break; + case 0x3: + OC_getComputedVariantSpeech(); + break; + case 0x4: + OC_getRotatingVariantSpeech(); + break; + case 0x5: + OC_startSpeechIfMute(); + break; + case 0x6: + OC_getComputedVariantSpeechIfMute(); + break; + case 0x7: + OC_startSpeechIfSilent(); + break; + case 0x8: + OC_ComputeCharacterVariable(); + break; + case 0x9: + OC_setAttributeToRandom(); + break; + case 0xA: + OC_setCharacterPosition(); + break; + case 0xB: + OC_DisableCharacter(); + break; + case 0xC: + OC_saveAndQuit(); + break; + case 0xD: + OC_nSkipOpcodes(); + break; + case 0xE: + OC_startSpeech5(); + break; + case 0xF: + OC_resetHandleOpcodeFlag(); + break; + case 0x10: + OC_deleteSavegameAndQuit(); + break; + case 0x11: + OC_incScriptForVal(); + break; + case 0x12: + OC_computeChararacterAttr(); + break; + case 0x13: + OC_setTextVarNumber(); + break; + case 0x14: + OC_callScript(); + break; + case 0x15: + OC_callScriptAndReturn(); + break; + case 0x16: + OC_setCurrentScriptCharacterPos(); + break; + case 0x17: + OC_initScriptFor(); + break; + case 0x18: + OC_setCurrentCharacterSequence(); + break; + case 0x19: + OC_setNextCharacterSequence(); + break; + case 0x1A: + OC_setHost(); + break; + case 0x1B: + OC_changeMapCube(); + break; + case 0x1C: + OC_setCharacterCarry(); + break; + case 0x1D: + OC_dropCarried(); + break; + case 0x1E: + OC_setCurrentCharacter(); + break; + case 0x1F: + OC_sendSeeSignal(); + break; + case 0x20: + OC_sendHearSignal(); + break; + case 0x21: + OC_sendVarSignal(); + break; + case 0x22: + OC_sendBroadcastSignal(); + break; + case 0x23: + OC_resetWaitingSignal(); + break; + case 0x24: + OC_enableCurrentCharacterScript(); + break; + case 0x25: + OC_IncCurrentCharacterVar1(); + break; + case 0x26: + OC_setCurrentCharacterPos(); + break; + case 0x27: + OC_setCurrentCharacterBehavior(); + break; + case 0x28: + OC_changeCurrentCharacterSprite(); + break; + case 0x29: + OC_getList(); + break; + case 0x2A: + OC_setList(); + break; + case 0x2B: + OC_setCharacterDirectionTowardsPos(); + break; + case 0x2C: + OC_turnCharacterTowardsAnother(); + break; + case 0x2D: + OC_setSeek(); + break; + case 0x2E: + OC_scrollAwayFromCharacter(); + break; + case 0x2F: + OC_skipNextVal(); + break; + case 0x30: + OC_setCurrentCharacterAttr6(); + break; + case 0x31: + OC_setCurrentCharacterPose(); + break; + case 0x32: + OC_setCharacterScriptEnabled(); + break; + case 0x33: + OC_setCurrentCharacterAttr2(); + break; + case 0x34: + OC_clearCurrentCharacterAttr2(); + break; + case 0x35: + OC_setCharacterProperties(); + break; + case 0x36: + OC_setMonitoredCharacter(); + break; + case 0x37: + OC_setNewPose(); + break; + case 0x38: + OC_setCurrentCharacterDirection(); + break; + case 0x39: + OC_setInterfaceHotspot(); + break; + case 0x3A: + OC_scrollViewPort(); + break; + case 0x3B: + OC_setViewPortPos(); + break; + case 0x3C: + OC_setCurrentCharacterAltitude(); + break; + case 0x3D: + OC_setModePriority(); + break; + case 0x3E: + OC_setComputedModePriority(); + break; + case 0x3F: + OC_selectBestMode(); + break; + case 0x40: + OC_magicPuffEntrance(); + break; + case 0x41: + OC_spawnCharacterAtPos(); + break; + case 0x42: + OC_CharacterVariableAddOrRemoveFlag(); + break; + case 0x43: + OC_PaletteFadeOut(); + break; + case 0x44: + OC_PaletteFadeIn(); + break; + case 0x45: + OC_loadAndDisplayCubesGfx(); + break; + case 0x46: + OC_setCurrentCharacterAttr3(); + break; + case 0x47: + OC_setArray122C1(); + break; + case 0x48: + OC_sub18367(); + break; + case 0x49: + OC_enableCharacterScript(); + break; + case 0x4A: + OC_setRulesBuffer2Element(); + break; + case 0x4B: + OC_setDebugFlag(); + break; + case 0x4C: + OC_setDebugFlag2(); + break; + case 0x4D: + OC_waitForEvent(); + break; + case 0x4E: + OC_disableInterfaceHotspot(); + break; + case 0x4F: + OC_loadFileAerial(); + break; + case 0x50: + OC_startSpeechIfSoundOff(); + break; + case 0x51: + OC_sub1844A(); + break; + case 0x52: + OC_displayNumericCharacterVariable(); + break; + case 0x53: + OC_displayVGAFile(); + break; + case 0x54: + OC_startSpeechWithoutSpeeker(); + break; + case 0x55: + OC_displayTitleScreen(); + break; + case 0x56: + OC_initGameAreaDisplay(); + break; + case 0x57: + OC_displayCharacterStatBar(); + break; + case 0x58: + OC_initSmallAnim(); + break; + case 0x59: + OC_setCharacterHeroismBar(); + break; + case 0x5A: + OC_setCharacterHome(); + break; + case 0x5B: + OC_setViewPortCharacterTarget(); + break; + case 0x5C: + OC_showObject(); + break; + case 0x5D: + OC_playObjectSound(); + break; + case 0x5E: + OC_startLocationSound(); + break; + case 0x5F: + OC_stopObjectSound(); + break; + case 0x60: + OC_stopLocationSound(); + break; + case 0x61: + OC_toggleSound(); + break; + case 0x62: + OC_playMusic(); + break; + case 0x63: + OC_stopMusic(); + break; + case 0x64: + OC_setCharacterMapColor(); + break; + case 0x65: + OC_initGameAreaDisplay(); + break; + default: + error("Unknown opcode %d", curWord); + break; + } +} + +static const OpCode opCodes1[] = { + { "OC_checkCharacterGoalPos", 1, kgetPosFromScript, kNone, kNone, kNone, kNone }, + { "OC_comparePos", 2, kGetValue1, kgetPosFromScript, kNone, kNone, kNone }, + { "OC_checkIsoMap3", 1, kImmediateValue, kNone, kNone, kNone, kNone }, + { "OC_compareCharacterVariable", 4, kGetValue1, kImmediateValue, kCompareOperation, kImmediateValue, kNone }, + { "OC_CompareLastRandomValue", 2, kCompareOperation, kImmediateValue, kNone, kNone, kNone }, + { "OC_getRandom", 1, kImmediateValue, kNone, kNone, kNone, kNone }, + { "OC_for", 2, kImmediateValue, kImmediateValue, kNone, kNone, kNone }, + { "OC_compCurrentSpeechId", 1, kImmediateValue, kNone, kNone, kNone, kNone }, + { "OC_checkSaveFlag", 0, kNone, kNone, kNone, kNone, kNone }, + { "OC_compScriptForVal", 2, kCompareOperation, kImmediateValue, kNone, kNone, kNone }, + { "OC_isCarrying", 2, kGetValue1, kGetValue1, kNone, kNone, kNone }, + { "OC_CompareCharacterVariables", 5, kGetValue1, kImmediateValue, kCompareOperation, kGetValue1, kImmediateValue }, + { "OC_compareCoords_1", 1, kImmediateValue, kNone, kNone, kNone, kNone }, + { "OC_compareCoords_2", 2, kGetValue1, kImmediateValue, kNone, kNone, kNone }, + { "OC_CompareDistanceFromCharacterToPositionWith", 3, kgetPosFromScript, kCompareOperation, kImmediateValue, kNone, kNone }, + { "OC_compareRandomCharacterId", 3, kGetValue1, kCompareOperation, kImmediateValue, kNone, kNone }, + { "OC_isCurrentCharacterIndex", 1, kGetValue1, kNone, kNone, kNone, kNone }, + { "OC_hasVisibilityLevel", 2, kImmediateValue, kGetValue1, kNone, kNone, kNone }, + { "OC_hasGainedVisibilityLevel", 2, kImmediateValue, kGetValue1, kNone, kNone, kNone }, + { "OC_hasReducedVisibilityLevel", 2, kImmediateValue, kGetValue1, kNone, kNone, kNone }, + { "OC_isHost", 1, kGetValue1, kNone, kNone, kNone, kNone }, + { "OC_isSequenceActive", 1, kImmediateValue, kNone, kNone, kNone, kNone }, + { "OC_isSequenceFinished", 1, kImmediateValue, kNone, kNone, kNone, kNone }, + { "OC_compareMapValueWith", 4, kgetPosFromScript, kImmediateValue, kImmediateValue, kCompareOperation, kNone }, + { "OC_isCharacterValid", 1, kGetValue1, kNone, kNone, kNone, kNone }, + { "OC_checkWaitingSignal", 1, kImmediateValue, kNone, kNone, kNone, kNone }, + { "OC_currentCharacterVar0AndVar1Equals", 2, kImmediateValue, kImmediateValue, kNone, kNone, kNone }, + { "OC_currentCharacterVar0Equals", 1, kImmediateValue, kNone, kNone, kNone, kNone }, + { "OC_checkLastInterfaceHotspotIndexMenu13", 1, kImmediateValue, kNone, kNone, kNone, kNone }, + { "OC_checkLastInterfaceHotspotIndexMenu2", 1, kImmediateValue, kNone, kNone, kNone, kNone }, + { "OC_compareNumberOfCharacterWithVar0Equals", 3, kImmediateValue, kCompareOperation, kImmediateValue, kNone, kNone }, + { "OC_isPositionInViewport", 1, kgetPosFromScript, kNone, kNone, kNone, kNone }, + { "OC_compareGameVariables", 2, kGetValue1, kGetValue1, kNone, kNone, kNone }, + { "OC_skipNextOpcode", 1, kImmediateValue, kNone, kNone, kNone, kNone }, + { "OC_CheckCurrentCharacterAttr2", 0, kNone, kNone, kNone, kNone, kNone }, + { "OC_CheckCurrentCharacterType", 2, kGetValue1, kImmediateValue, kNone, kNone, kNone }, + { "OC_CheckCurrentCharacterAttr0And", 3, kGetValue1, kImmediateValue, kImmediateValue, kNone, kNone }, + { "OC_IsCurrentCharacterAttr0LessEqualThan", 1, kImmediateValue, kNone, kNone, kNone, kNone }, + { "OC_isCarried", 1, kGetValue1, kNone, kNone, kNone, kNone }, + { "OC_CheckCurrentCharacterAttr1", 1, kImmediateValue, kNone, kNone, kNone, kNone }, + { "OC_isCurrentCharacterStung", 0, kNone, kNone, kNone, kNone, kNone }, + { "OC_CurrentCharacterAttr3Equals1", 0, kNone, kNone, kNone, kNone, kNone }, + { "OC_sub1796E", 2, kGetValue1, kImmediateValue, kNone, kNone, kNone }, + { "OC_checkLastInterfaceHotspotIndex", 2, kImmediateValue, kImmediateValue, kNone, kNone, kNone }, + { "OC_checkSelectedCharacter", 0, kNone, kNone, kNone, kNone, kNone }, + { "OC_checkDelayedReactivation", 0, kNone, kNone, kNone, kNone, kNone }, + { "OC_checkTargetReached", 1, kgetPosFromScript, kNone, kNone, kNone, kNone }, + { "OC_checkFunctionKeyPressed", 1, kImmediateValue, kNone, kNone, kNone, kNone }, + { "OC_checkCodeEntered", 3, kImmediateValue, kImmediateValue, kImmediateValue, kNone, kNone }, + { "OC_checkViewPortCharacterTarget", 1, kGetValue1, kNone, kNone, kNone, kNone }, +}; + + +static const OpCode opCodes2[] = { +/* 0x00 */ { "OC_setWord18821", 1, kGetValue1, kNone, kNone, kNone, kNone }, +/* 0x01 */ { "OC_changeIsoMap", 3, kgetPosFromScript, kImmediateValue, kImmediateValue, kNone, kNone }, +/* 0x02 */ { "OC_startSpeech", 1, kImmediateValue, kNone, kNone, kNone, kNone }, +/* 0x03 */ { "OC_getComputedVariantSpeech", 4, kGetValue1, kImmediateValue, kImmediateValue, kImmediateValue, kNone }, +/* 0x04 */ { "OC_getRotatingVariantSpeech", 2, kImmediateValue, kImmediateValue, kNone, kNone, kNone }, // todo +/* 0x05 */ { "OC_startSpeechIfMute", 1, kImmediateValue, kNone, kNone, kNone, kNone }, +/* 0x06 */ { "OC_getComputedVariantSpeechIfMute", 4, kGetValue1, kImmediateValue, kImmediateValue, kImmediateValue, kNone }, // pb +/* 0x07 */ { "OC_startSpeechIfSilent", 2, kImmediateValue, kImmediateValue, kNone, kNone, kNone }, +/* 0x08 */ { "OC_computeCharacterVariable", 4, kGetValue1, kImmediateValue, kComputeOperation, kImmediateValue, kNone }, +/* 0x09 */ { "OC_setAttributeToRandom", 3, kGetValue1, kImmediateValue, kImmediateValue, kNone, kNone }, +/* 0x0a */ { "OC_setCharacterPosition", 2, kGetValue1, kgetPosFromScript, kNone, kNone, kNone }, +/* 0x0b */ { "OC_disableCharacter", 1, kGetValue1, kNone, kNone, kNone, kNone }, +/* 0x0c */ { "OC_saveAndQuit", 0, kNone, kNone, kNone, kNone, kNone }, +/* 0x0d */ { "OC_nSkipOpcodes", 1, kImmediateValue, kNone, kNone, kNone, kNone }, // todo : jump to other opcode +/* 0x0e */ { "OC_startSpeech5", 0, kNone, kNone, kNone, kNone, kNone }, // todo +/* 0x0f */ { "OC_resetHandleOpcodeFlag", 0, kNone, kNone, kNone, kNone, kNone }, +/* 0x10 */ { "OC_deleteSavegameAndQuit", 0, kNone, kNone, kNone, kNone, kNone }, +/* 0x11 */ { "OC_incScriptForVal", 0, kNone, kNone, kNone, kNone, kNone }, +/* 0x12 */ { "OC_ComputeChararacterAttr", 5, kGetValue1, kImmediateValue,kComputeOperation, kGetValue1, kImmediateValue }, +/* 0x13 */ { "OC_setTextVarNumber", 2, kGetValue1, kImmediateValue, kNone, kNone, kNone }, +/* 0x14 */ { "OC_callScript", 2, kImmediateValue, kGetValue1, kNone, kNone, kNone }, // run script +/* 0x15 */ { "OC_callScriptAndReturn", 2, kImmediateValue, kGetValue1, kNone, kNone, kNone }, // run script then stop +/* 0x16 */ { "OC_setCurrentScriptCharacterPos", 1, kgetPosFromScript, kNone, kNone, kNone, kNone }, +/* 0x17 */ { "OC_initScriptFor", 0, kNone, kNone, kNone, kNone, kNone }, +/* 0x18 */ { "OC_setCurrentCharacterSequence", 1, kImmediateValue, kNone, kNone, kNone, kNone }, +/* 0x19 */ { "OC_setNextCharacterSequence", 1, kImmediateValue, kNone, kNone, kNone, kNone }, +/* 0x1a */ { "OC_setHost", 1, kGetValue1, kNone, kNone, kNone, kNone }, +/* 0x1b */ { "OC_changeMapCube", 0, kNone, kNone, kNone, kNone, kNone }, +/* 0x1c */ { "OC_setCharacterCarry", 4, kGetValue1, kGetValue1, kImmediateValue, kImmediateValue, kNone }, +/* 0x1d */ { "OC_dropCarried", 1, kGetValue1, kNone, kNone, kNone, kNone }, +/* 0x1e */ { "OC_setCurrentCharacter", 1, kGetValue1, kNone, kNone, kNone, kNone }, +/* 0x1f */ { "OC_sendSeeSignal", 2, kImmediateValue, kImmediateValue, kNone, kNone, kNone }, +/* 0x20 */ { "OC_sendHearSignal", 2, kImmediateValue, kImmediateValue, kNone, kNone, kNone }, +/* 0x21 */ { "OC_sendVarSignal", 3, kImmediateValue, kGetValue1, kImmediateValue, kNone, kNone }, +/* 0x22 */ { "OC_sendBroadcastSignal", 2, kImmediateValue, kImmediateValue, kNone, kNone, kNone }, +/* 0x23 */ { "OC_resetWaitingSignal", 0, kNone, kNone, kNone, kNone, kNone }, +/* 0x24 */ { "OC_enableCurrentCharacterScript", 1, kImmediateValue, kNone, kNone, kNone, kNone }, // stop script +/* 0x25 */ { "OC_incCurrentCharacterVar1", 0, kNone, kNone, kNone, kNone, kNone }, +/* 0x26 */ { "OC_setCurrentCharacterPos", 2, kImmediateValue, kgetPosFromScript, kNone, kNone, kNone }, +/* 0x27 */ { "OC_setCurrentCharacterBehavior", 1, kImmediateValue, kNone, kNone, kNone, kNone }, +/* 0x28 */ { "OC_changeCurrentCharacterSprite", 2, kImmediateValue, kImmediateValue, kNone, kNone, kNone }, +/* 0x29 */ { "OC_getList", 4, kImmediateValue, kImmediateValue, kImmediateValue, kImmediateValue, kNone }, +/* 0x2a */ { "OC_setList", 4, kImmediateValue, kImmediateValue, kImmediateValue, kImmediateValue, kNone }, +/* 0x2b */ { "OC_setCharacterDirectionTowardsPos", 1, kgetPosFromScript, kNone, kNone, kNone, kNone }, +/* 0x2c */ { "OC_turnCharacterTowardsAnother", 1, kGetValue1, kNone, kNone, kNone, kNone }, +/* 0x2d */ { "OC_setSeek", 1, kGetValue1, kNone, kNone, kNone, kNone }, +/* 0x2e */ { "OC_scrollAwayFromCharacter", 0, kNone, kNone, kNone, kNone, kNone }, +/* 0x2f */ { "OC_skipNextVal", 1, kImmediateValue, kNone, kNone, kNone, kNone }, +/* 0x30 */ { "OC_setCurrentCharacterAttr6", 1, kGetValue1, kNone, kNone, kNone, kNone }, +/* 0x31 */ { "OC_setCurrentCharacterPose", 1, kImmediateValue, kNone, kNone, kNone, kNone }, +/* 0x32 */ { "OC_setCharacterScriptEnabled", 1, kGetValue1, kNone, kNone, kNone, kNone }, +/* 0x33 */ { "OC_setCurrentCharacterAttr2", 1, kImmediateValue, kNone, kNone, kNone, kNone }, +/* 0x34 */ { "OC_ClearCurrentCharacterAttr2", 0, kNone, kNone, kNone, kNone, kNone }, +/* 0x35 */ { "OC_setCharacterProperties", 5, kGetValue1, kImmediateValue, kImmediateValue, kImmediateValue, kImmediateValue }, +/* 0x36 */ { "OC_setMonitoredCharacter", 5, kGetValue1, kImmediateValue, kImmediateValue, kImmediateValue, kImmediateValue }, +/* 0x37 */ { "OC_setNewPose", 2, kImmediateValue, kImmediateValue, kNone, kNone, kNone }, +/* 0x38 */ { "OC_setCurrentCharacterDirection", 1, kImmediateValue, kNone, kNone, kNone, kNone }, +/* 0x39 */ { "OC_setInterfaceHotspot", 2, kImmediateValue, kImmediateValue, kNone, kNone, kNone }, +/* 0x3a */ { "OC_scrollViewPort", 1, kImmediateValue, kNone, kNone, kNone, kNone }, +/* 0x3b */ { "OC_setViewPortPos", 1, kgetPosFromScript, kNone, kNone, kNone, kNone }, +/* 0x3c */ { "OC_setCurrentCharacterAltitude", 1, kImmediateValue, kNone, kNone, kNone, kNone }, +/* 0x3d */ { "OC_setModePriority", 2, kImmediateValue, kImmediateValue, kNone, kNone, kNone }, +/* 0x3e */ { "OC_setComputedModePriority", 4, kImmediateValue, kImmediateValue, kImmediateValue, kImmediateValue, kNone }, +/* 0x3f */ { "OC_selectBestMode", 1, kImmediateValue, kNone, kNone, kNone, kNone }, +/* 0x40 */ { "OC_magicPuffEntrance", 1, kGetValue1, kNone, kNone, kNone, kNone }, +/* 0x41 */ { "OC_spawnCharacterAtPos", 2, kGetValue1, kgetPosFromScript, kNone, kNone, kNone }, // TODO +/* 0x42 */ { "OC_characterVariableAddOrRemoveFlag", 4, kGetValue1, kImmediateValue, kImmediateValue, kImmediateValue, kNone }, +/* 0x43 */ { "OC_paletteFadeOut", 0, kNone, kNone, kNone, kNone, kNone }, +/* 0x44 */ { "OC_paletteFadeIn", 0, kNone, kNone, kNone, kNone, kNone }, +/* 0x45 */ { "OC_loadAndDisplayCubesGfx", 1, kImmediateValue, kNone, kNone, kNone, kNone }, +/* 0x46 */ { "OC_setCurrentCharacterAttr3", 1, kImmediateValue, kNone, kNone, kNone, kNone }, +/* 0x47 */ { "OC_setArray122C1", 1, kImmediateValue, kNone, kNone, kNone, kNone }, +/* 0x48 */ { "OC_sub18367", 0, kNone, kNone, kNone, kNone, kNone }, +/* 0x49 */ { "OC_enableCharacterScript", 2, kGetValue1, kImmediateValue, kNone, kNone, kNone }, +/* 0x4a */ { "OC_setRulesBuffer2Element", 2, kGetValue1, kImmediateValue, kNone, kNone, kNone }, +/* 0x4b */ { "OC_setDebugFlag", 0, kNone, kNone, kNone, kNone, kNone }, +/* 0x4c */ { "OC_setDebugFlag2", 0, kNone, kNone, kNone, kNone, kNone }, +/* 0x4d */ { "OC_waitForEvent", 0, kNone, kNone, kNone, kNone, kNone }, +/* 0x4e */ { "OC_disableInterfaceHotspot", 2, kImmediateValue, kImmediateValue, kNone, kNone, kNone }, // TODO +/* 0x4f */ { "OC_loadFileAerial", 1, kNone, kNone, kNone, kNone, kNone }, +/* 0x50 */ { "OC_startSpeechIfSoundOff", 1, kImmediateValue, kNone, kNone, kNone, kNone }, +/* 0x51 */ { "OC_sub1844A", 2, kGetValue1, kImmediateValue, kNone, kNone, kNone }, +/* 0x52 */ { "OC_displayNumericCharacterVariable", 5, kGetValue1, kImmediateValue, kImmediateValue, kImmediateValue, kImmediateValue }, +/* 0x53 */ { "OC_displayVGAFile", 1, kImmediateValue, kNone, kNone, kNone, kNone }, +/* 0x54 */ { "OC_startSpeechWithoutSpeeker", 1, kImmediateValue, kNone, kNone, kNone, kNone }, // TODO +/* 0x55 */ { "OC_displayTitleScreen", 1, kImmediateValue, kNone, kNone, kNone, kNone }, +/* 0x56 */ { "OC_initGameAreaDisplay", 0, kNone, kNone, kNone, kNone, kNone }, +/* 0x57 */ { "OC_displayCharacterStatBar", 6, kGetValue1, kImmediateValue, kImmediateValue, kImmediateValue, kImmediateValue}, +/* 0x58 */ { "OC_initSmallAnim", 11, kImmediateValue, kImmediateValue, kImmediateValue, kImmediateValue, kImmediateValue }, +/* 0x59 */ { "OC_setCharacterHeroismBar", 4, kGetValue1, kImmediateValue, kImmediateValue, kImmediateValue, kNone }, +/* 0x5a */ { "OC_setCharacterHome", 2, kGetValue1, kgetPosFromScript, kNone, kNone, kNone }, //TODO +/* 0x5b */ { "OC_setViewPortCharacterTarget", 1, kGetValue1, kNone, kNone, kNone, kNone }, +/* 0x5c */ { "OC_showObject", 3, kGetValue1, kImmediateValue, kImmediateValue, kNone, kNone }, //TODO +/* 0x5d */ { "OC_playObjectSound", 2, kGetValue1, kImmediateValue, kNone, kNone, kNone }, +/* 0x5e */ { "OC_startLocationSound", 2, kgetPosFromScript, kImmediateValue, kNone, kNone, kNone }, +/* 0x5f */ { "OC_stopObjectSound", 1, kGetValue1, kNone, kNone, kNone, kNone }, +/* 0x60 */ { "OC_stopLocationSound", 1, kGetValue1, kNone, kNone, kNone, kNone }, +/* 0x61 */ { "OC_toggleSound", 1, kgetPosFromScript, kNone, kNone, kNone, kNone }, +/* 0x62 */ { "OC_playMusic", 1, kImmediateValue, kNone, kNone, kNone, kNone }, +/* 0x63 */ { "OC_stopMusic", 0, kNone, kNone, kNone, kNone, kNone }, +/* 0x64 */ { "OC_setCharacterMapColor", 2, kGetValue1, kImmediateValue, kNone, kNone, kNone }, +/* 0x65 */ { "OC_initGameAreaDisplay", 0, kNone, kNone, kNone, kNone, kNone } +}; + +Common::String LilliputScript::getArgumentString(kValueType type, ScriptStream& script) { + + Common::String str; + if (type == kImmediateValue) { + str = Common::String::format("0x%x", script.readUint16LE()); + } else if (type == kGetValue1) { + int val = script.readUint16LE(); + if (val < 1000) { + str = Common::String::format("0x%x", val); + } else if (val > 1004) { + str = Common::String::format("getValue1(0x%x)", val); + } else if (val == 1000) { + str = Common::String("_selectedCharacterId"); + } else if (val == 1001) { + str = Common::String("_characterIndex"); + } else if (val == 1002) { + str = Common::String("_word16F00_characterId"); + } else if (val == 1003) { + str = Common::String("_currentCharacterVariables[6]"); + } else if (val == 1004) { + str = Common::String("_host"); + } + } else if (type == kgetPosFromScript) { + int curWord = script.readUint16LE(); + int tmpVal = curWord >> 8; + switch(tmpVal) { + case 0xFF: + str = "(_rulesBuffer2_13[currentCharacter],_rulesBuffer2_14[currentCharacter])"; + break; + case 0xFE: { + int index = curWord & 0xFF; + assert((index >= 0) && (index < 40)); + str = Common::String::format("_vm->_rulesBuffer2_13[%d],_vm->_rulesBuffer2_14[%d]", index, index); + break; + } + case 0xFD: + str = "_currentScriptCharacterPosition"; + break; + case 0xFC: { + int index = curWord & 0xFF; + assert(index < 40); + str = Common::String::format("(characterPositionTileX[%d], characterPositionTileY[%d])", index, index); + break; + } + case 0xFB: { + str = "(characterPositionTileX[_word16F00_characterId], characterPositionTileY[_word16F00_characterId])"; + break; + } + case 0xFA: + str = Common::String::format("(_characterTargetPosX[currentCharacter], _characterTargetPosY[currentCharacter])"); + break; + case 0xF9: + str = Common::String::format("(_currentCharacterVariables[4], _currentCharacterVariables[5])"); + break; + case 0xF8: { + int index = curWord & 0xFF; + assert((index >= 0) && (index < 40)); + str = Common::String::format("_vm->_rulesBuffer12Pos3[%d]", index); + break; + } + case 0xF7: { + str = Common::String::format("(_characterPositionTileX[_currentCharacterVariables[6]], _characterPositionTileY[_currentCharacterVariables[6]])"); + break; + } + case 0xF6: + str = "_savedMousePosDivided"; + break; + default: + str = Common::String::format("(0x%x,0x%x)", curWord >> 8, curWord & 0xFF); + break; + } + } else if (type == kCompareOperation) { + int comp = script.readUint16LE(); + if (comp != '<' && comp != '>') + comp = '='; + str = Common::String::format("%c", comp); + } + else if (type == kComputeOperation) { + int comp = script.readUint16LE(); + str = Common::String::format("%c", comp); + } + return str; +} + +void LilliputScript::disasmScript(ScriptStream script) { + while (!script.eos()) { + uint16 val = script.readUint16LE(); + if (val == 0xFFF6) // end of script + return; + + bool firstIf = true; + + // check the conditions. + while (val != 0xFFF8) { + bool neg = false; + + if (val >= 1000) { + val -= 1000; + // negative condition + neg = true; + } + + // op code type 1 + assert(val < sizeof(opCodes1) / sizeof(OpCode)); + const OpCode *opCode = &opCodes1[val]; + const kValueType *opArgType = &opCode->_arg1; + + Common::String str; + + if (firstIf) { + str = "if ("; + firstIf = false; + } else { + str = " "; + } + if (neg) + str += "not "; + str += Common::String(opCode->_opName); + str += "("; + + for (int p = 0; p < opCode->_numArgs; p++) { + str += getArgumentString(*opArgType, script); + if (p != opCode->_numArgs - 1) + str += ", "; + + opArgType++; + } + str += ")"; + + val = script.readUint16LE(); + + if (val == 0xFFF8) { + str += ")"; + } + + debugC(2, kDebugScript, "%s", str.c_str()); + } + + debugC(2, kDebugScript, "{ "); + + val = script.readUint16LE(); + + while (val != 0xFFF7) { + // op code type 2 + assert(val < sizeof(opCodes2) / sizeof(OpCode)); + const OpCode *opCode = &opCodes2[val]; + const kValueType *opArgType = &opCode->_arg1; + + Common::String str; + str = " "; + str += Common::String(opCode->_opName); + str += "("; + + for (int p = 0; p < opCode->_numArgs; p++) { + str += getArgumentString(*opArgType, script); + if (p != opCode->_numArgs - 1) + str += ", "; + if (p < 4) + opArgType++; + } + str += ");"; + + debugC(2, kDebugScript, "%s", str.c_str()); + + val = script.readUint16LE(); + } + + debugC(2, kDebugScript, "} "); + debugC(2, kDebugScript, " "); + } +} + +int LilliputScript::handleOpcode(ScriptStream *script) { + debugC(2, kDebugScript, "handleOpcode"); + + _currScript = script; + uint16 curWord = _currScript->readUint16LE(); + if (curWord == 0xFFF6) + return 0xFF; + + for (; curWord != 0xFFF8; curWord = _currScript->readUint16LE()) { + byte mask = 0; + if (curWord > 1000) { + curWord -= 1000; + mask = 1; + } + byte result = handleOpcodeType1(curWord); + if ((result ^ mask) == 0) { + do { + curWord = _currScript->readUint16LE(); + } while (curWord != 0xFFF7); + return 0; + } + } + + _vm->_handleOpcodeReturnCode = 1; + + for (;;) { + curWord = _currScript->readUint16LE(); + if (curWord == 0xFFF7) + return _vm->_handleOpcodeReturnCode; + + handleOpcodeType2(curWord); + } +} + +void LilliputScript::runScript(ScriptStream script) { + debugC(1, kDebugScript, "runScript"); + + _byte16F05_ScriptHandler = 1; + + while (handleOpcode(&script) != 0xFF) + _vm->update(); +} + +void LilliputScript::runMenuScript(ScriptStream script) { + debugC(1, kDebugScript, "runMenuScript"); + + _byte16F05_ScriptHandler = 0; + + while (handleOpcode(&script) == 0) + _vm->update(); +} + +byte LilliputScript::compareValues(int16 var1, uint16 oper, int16 var2) { + debugC(2, kDebugScript, "compareValues(%d, %c, %d)", var1, oper & 0xFF, var2); + + switch (oper & 0xFF) { + case '<': + if (var1 < var2) + return 1; + break; + case '>': + if (var1 > var2) + return 1; + break; + default: + if (var1 == var2) + return 1; + break; + } + + return 0; +} + +void LilliputScript::computeOperation(byte *bufPtr, uint16 oper, int16 var3) { + debugC(1, kDebugScript, "computeOperation(bufPtr, %c, %d)", oper & 0xFF, var3 & 0xFF); + + switch (oper & 0xFF) { + case '=': + bufPtr[0] = var3 & 0xFF; + break; + case '+': { + int tmpVal = bufPtr[0] + var3; + if (tmpVal > 0xFF) + bufPtr[0] = 0xFF; + else + bufPtr[0] = (byte)tmpVal; + } + break; + case '-': { + int tmpVal = bufPtr[0] - var3; + if (tmpVal < 0) + bufPtr[0] = 0; + else + bufPtr[0] = (byte)tmpVal; + } + break; + case '*': { + int tmpVal = bufPtr[0] * var3; + bufPtr[0] = tmpVal & 0xFF; + } + break; + case '/': { + if (var3 != 0) + bufPtr[0] /= var3; + } + break; + default: { + warning("computeOperation : oper %d", oper); + if (var3 != 0) { + int tmpVal = bufPtr[0] / var3; + if (tmpVal < 0) + bufPtr[0] = 0xFF; + else + bufPtr[0] = 0; + } + break; + } + } +} + +void LilliputScript::enableCharacterScript(byte index, byte var1, byte *curBufPtr) { + debugC(1, kDebugScript, "enableCharacterScript(%d, %d, curBufPtr)", index, var1); + + assert (index < 40); + _characterScriptEnabled[index] = 1; + curBufPtr[0] = var1; + curBufPtr[1] = 0; + curBufPtr[2] = 0; + curBufPtr[3] = 0; +} + +void LilliputScript::skipOpcodes(int var1) { + debugC(1, kDebugScript, "skipOpcodes(%d)", var1); + + if (var1 == 0) { + int curWord = 0; + while (curWord != 0xFFF6) + curWord = _currScript->readUint16LE(); + + _currScript->seek(_currScript->pos() - 4); + return; + } + + ++var1; + int curVal = 0; + while (curVal < var1) { + int tmpVal = _currScript->readUint16LE(); + if (tmpVal == 0xFFF7) + ++curVal; + } + + _currScript->seek(_currScript->pos() - 2); +} + +void LilliputScript::copySequence(int index, byte *buf) { + debugC(1, kDebugScript, "copySequence()"); + + _characterNextSequence[index] = 0; + + for (int i = 0; i < 16; i++) { + _sequenceArr[(index * 16) + i] = Common::Point(buf[(2 * i) + 1], buf[2 * i]); + } +} + +void LilliputScript::setSequence(int charIdx, int8 seqIdx) { + debugC(1, kDebugScript, "setSequence(%d, %d)", charIdx, seqIdx); + + assert(charIdx < 40); + _characterLastSequence[charIdx] = seqIdx; + + byte *buf = _vm->_sequencesArr; + if (seqIdx != 0) { + int count = 0; + while (count < seqIdx) { + if ((buf[0] == 0xFF) && (buf[1] == 0xFF)) + ++count; + buf += 2; + } + } + + copySequence(charIdx, buf); +} + +void LilliputScript::checkSpeechAllowed(bool &forceReturnFl) { + debugC(1, kDebugScript, "checkSpeechAllowed()"); + + forceReturnFl = false; + if ((!_vm->_displayMap) && (_vm->_characterRelativePos[_vm->_currentScriptCharacter].x != -1)) + return; + + forceReturnFl = true; + return; +} + +void LilliputScript::formatSpeechString() { + debugC(2, kDebugScript, "formatSpeechString()"); + + int index = 0; + int var2 = 0x100; + + for (;;) { + int var1 = _vm->_displayStringBuf[index++]; + if (var1 == 0) + break; + + if (var1 == '|') { + var2 &= 0xFF; + ++var2; + continue; + } + + var2 += 0x100; + if ((var2 >> 8) < 61) + continue; + + if ((var2 & 0xFF) == 1) { + _vm->_displayStringBuf[index - 1] = 0; + break; + } + + --index; + while (_vm->_displayStringBuf[index] != ' ') + --index; + + _vm->_displayStringBuf[index] = '|'; + ++var2; + var2 &= 0xFF; + ++index; + } +} + +void LilliputScript::showSpeech() { + debugC(2, kDebugScript, "showSpeech()"); + + formatSpeechString(); + int index = 0; + + for (;;) { + if (_vm->_displayStringBuf[index] == 0) + break; + ++index; + } + + index /= _speechDisplaySpeed; + index += 4; + _speechTimer = index; + _vm->displaySpeechBubble(); + _vm->displaySpeech(_vm->_displayStringBuf); +} + +void LilliputScript::decodePackedText(char *buf) { + debugC(2, kDebugScript, "decodePackedText(buf)"); + + // All the languages use the English dictionary + static const char *nounsArrayPtr = "I am |You are |you are |hou art |in the |" + "is the |is a |in a |To the |to the |by |going |here |The|the|and |" + "some |build|not |way|I |a |an |from |of |him|her|by |his |ing |tion|" + "have |you|I've |can't |up |to |he |she |down |what|What|with|are |" + "and|ent|ian|ome|ed |me|my|ai|it|is|of|oo|ea|er|es|th|we|ou|ow|or|" + "gh|go|er|st|ee|th|sh|ch|ct|on|ly|ng|nd|nt|ty|ll|le|de|as|ie|in|ss|" + "'s |'t |re|gg|tt|pp|nn|ay|ar|wh|"; + + _vm->_displayStringIndex = 0; + int index = 0; + byte var1 = 0; + for (;;) { + var1 = buf[index]; + ++index; + if (var1 == ']') + var1 = 0; + + if (var1 < 0x80) { + if (var1 == '@') { + var1 = buf[index]; + ++index; + if (var1 == '#') { + _vm->numberToString(_textVarNumber); + } + } else { + _vm->addCharToBuf(var1); + if (var1 == 0) + break; + } + } else { + int nounIndex = 0; + byte var3 = 0xFF - var1; + for (int i = 0; i < var3; i++) { + for (;;) { + var1 = nounsArrayPtr[nounIndex]; + ++nounIndex; + + if (var1 == '|') + break; + } + } + + for (;;) { + var1 = nounsArrayPtr[nounIndex]; + ++nounIndex; + + if (var1 == '|') + break; + + _vm->addCharToBuf(var1); + } + } + } + + showSpeech(); +} + +int LilliputScript::getPackedStringStartRelativeIndex(int index) { + debugC(2, kDebugScript, "getPackedStringStartRelativeIndex(%d)", index); + + int chunk4Index = _vm->_packedStringIndex[index]; + int result = 0; + while (_vm->_packedStrings[chunk4Index + result] == 0x5B) + ++result; + + return result + 1; +} + +// Part of the script decompiler +void LilliputScript::listAllTexts() { + debugC(1, kDebugScript, "listAllTexts"); + + for (int i = 0; i < _vm->_packedStringNumb; i++) { + int index = _vm->_packedStringIndex[i]; + int variantCount = 0; + while (_vm->_packedStrings[index + variantCount] == 0x5B) + ++variantCount; + /* + int it = 0; + if (variantCount != 0) { + for (int j = 0; j < variantCount; j++) { + decodePackedText(&_vm->_packedStrings[index + variantCount + it]); + warning("Text 0x%x variant %d : %s", i, j, _vm->_displayStringBuf); + do { + ++it; + } while (_vm->_packedStrings[index + variantCount + it] != 0x5B); + } + } else {*/ + decodePackedText(&_vm->_packedStrings[index + variantCount]); + debugC(1, kDebugScript, "Text 0x%x variant 0 : %s", i, _vm->_displayStringBuf); + /* }*/ + } +} + +void LilliputScript::startSpeech(int speechId) { + debugC(2, kDebugScript, "startSpeech(%d)", speechId); + + if (speechId == -1) + return; + + _currentSpeechId = speechId; + + int index = _vm->_packedStringIndex[speechId]; + int count = 0; + while (_vm->_packedStrings[index + count] == '[') + ++count; + + int i = 0; + if (count != 0) { + int tmpVal = _vm->_rnd->getRandomNumber(count); + if (tmpVal != 0) { + for (int j = 0; j < tmpVal; j++) { + do { + ++i; + } while (_vm->_packedStrings[index + count + i] != ']'); + ++i; + } + } + } + + decodePackedText(&_vm->_packedStrings[index + count + i]); +} + +int16 LilliputScript::getValue1() { + debugC(2, kDebugScript, "getValue1()"); + + int16 curWord = _currScript->readUint16LE(); + if (curWord < 1000) + return curWord; + + switch (curWord) { + case 1000: + return _vm->_selectedCharacterId; + case 1001: + return _vm->_currentScriptCharacter; + case 1002: + return _word16F00_characterId; + case 1003: + return (int16)_vm->_currentCharacterAttributes[6]; + case 1004: + return _vm->_host; + default: + warning("getValue1: Unexpected large value %d", curWord); + return curWord; + } +} + +Common::Point LilliputScript::getPosFromScript() { + debugC(2, kDebugScript, "getPosFromScript()"); + + int curWord = _currScript->readUint16LE(); + int tmpVal = curWord >> 8; + switch(tmpVal) { + case 0xFF: + assert((_vm->_currentScriptCharacter >= 0) && (_vm->_currentScriptCharacter < 40)); + return _vm->_characterHomePos[_vm->_currentScriptCharacter]; + case 0xFE: { + int8 index = curWord & 0xFF; + assert((index >= 0) && (index < 40)); + return _vm->_characterHomePos[index]; + } + case 0xFD: + return _vm->_currentScriptCharacterPos; + case 0xFC: { + int8 index = curWord & 0xFF; + assert((index >= 0) && (index < 40)); + int16 x = _vm->_characterPos[index].x >> 3; + int16 y = _vm->_characterPos[index].y >> 3; + + return Common::Point(x, y); + } + case 0xFB: { + int index = _word16F00_characterId; + assert((index >= 0) && (index < 40)); + int16 x = _vm->_characterPos[index].x >> 3; + int16 y = _vm->_characterPos[index].y >> 3; + + return Common::Point(x, y); + } + case 0xFA: + return _vm->_characterTargetPos[_vm->_currentScriptCharacter]; + case 0xF9: + return Common::Point(_vm->_currentCharacterAttributes[4], _vm->_currentCharacterAttributes[5]); + case 0xF8: { + int8 index = curWord & 0xFF; + assert((index >= 0) && (index < 40)); + return _vm->_keyPos[index]; + } + case 0xF7: { + int8 index = _vm->_currentCharacterAttributes[6]; + assert((index >= 0) && (index < 40)); + return Common::Point(_vm->_characterPos[index].x >> 3, _vm->_characterPos[index].y >> 3); + } + case 0xF6: + return _vm->_savedMousePosDivided; + default: + Common::Point pos = Common::Point(curWord >> 8, curWord & 0xFF); + // warning("getPosFromScript - High value %d -> %d %d", curWord, pos.x, pos.y); + return pos; + } +} + +byte *LilliputScript::getCharacterAttributesPtr() { + debugC(2, kDebugScript, "getCharacterVariablePtr()"); + + int8 tmpVal = (int8) (getValue1() & 0xFF); + int index = tmpVal * 32; + index += _currScript->readUint16LE(); + + return _vm->getCharacterAttributesPtr(index); +} + +byte LilliputScript::OC_checkCharacterGoalPos() { + debugC(2, kDebugScript, "OC_checkCharacterGoalPos()"); + + if (_vm->_currentScriptCharacterPos == getPosFromScript()) { + return 1; + } + return 0; +} + +byte LilliputScript::OC_comparePos() { + debugC(2, kDebugScript, "OC_comparePos()"); + + int index = getValue1(); + Common::Point var1 = getPosFromScript(); + + if (var1 == _characterTilePos[index]) + return 1; + + return 0; +} + +byte LilliputScript::OC_checkIsoMap3() { + debugC(1, kDebugScript, "OC_checkIsoMap3()"); + + Common::Point var = _vm->_currentScriptCharacterPos; + if (var == Common::Point(-1, -1)) { + _currScript->readUint16LE(); + return 0; + } + + byte *isoMapBuf = getMapPtr(var); + byte var2 = isoMapBuf[3]; + + int16 var3 = _currScript->readUint16LE(); + byte var4 = 8 >> var3; + + if ((var2 & var4) != 0) { + return 1; + } + + return 0; +} + +byte LilliputScript::OC_compareCharacterVariable() { + debugC(1, kDebugScript, "OC_compareCharacterVariable()"); + + byte *tmpArr = getCharacterAttributesPtr(); + byte var1 = tmpArr[0]; + uint16 oper = _currScript->readUint16LE(); + int16 var2 = _currScript->readUint16LE(); + + return compareValues(var1, oper, var2); +} + +byte LilliputScript::OC_CompareLastRandomValue() { + debugC(1, kDebugScript, "OC_CompareLastRandomValue()"); + + uint16 operation = _currScript->readUint16LE(); + int16 val2 = _currScript->readSint16LE(); + return compareValues(_lastRandomValue, operation, val2); +} + +byte LilliputScript::OC_getRandom() { + debugC(1, kDebugScript, "OC_getRandom()"); + + int maxVal = _currScript->readUint16LE(); + int rand = _vm->_rnd->getRandomNumber(maxVal); + _lastRandomValue = (rand & 0xFF); + + if (rand == 0) + return 1; + + return 0; +} + +byte LilliputScript::OC_for() { + debugC(1, kDebugScript, "OC_for()"); + + int var1 = _currScript->readUint16LE(); + int tmpVal = _currScript->readUint16LE() + 1; + // no need to seek later, the move is already done + _currScript->writeUint16LE(tmpVal, -2); + // overwrite the recently used "variable" in the script + if (tmpVal < var1) + return 0; + + _currScript->writeUint16LE(0, -2); + return 1; +} + +byte LilliputScript::OC_compCurrentSpeechId() { + debugC(1, kDebugScript, "OC_compCurrentSpeechId()"); + + int var1 = _currScript->readUint16LE(); + + if (var1 == _currentSpeechId) + return 1; + + return 0; +} + +byte LilliputScript::OC_checkSaveFlag() { + debugC(1, kDebugScript, "OC_checkSaveFlag()"); + + if (_vm->_saveFlag) + return 1; + + return 0; +} + +byte LilliputScript::OC_compScriptForVal() { + debugC(1, kDebugScript, "OC_compScriptForVal()"); + + uint16 oper = _currScript->readUint16LE(); + int16 var2 = _currScript->readUint16LE(); + + return compareValues(_scriptForVal, oper, var2); +} + +byte LilliputScript::OC_isCarrying() { + debugC(1, kDebugScript, "OC_isCarrying()"); + + int8 tmpVal = getValue1() & 0xFF; + uint16 curWord = _currScript->readUint16LE(); + + if (curWord == 3000) { + for (int index = 0; index < _vm->_numCharacters; index++) { + if (_vm->_characterCarried[index] == tmpVal) { + _word16F00_characterId = index; + return 1; + } + } + } else { + _currScript->seek(_currScript->pos() - 2); + int index = getValue1(); + assert(index < 40); + if (_vm->_characterCarried[index] == tmpVal) { + _word16F00_characterId = index; + return 1; + } + } + + return 0; +} + +byte LilliputScript::OC_CompareCharacterVariables() { + debugC(1, kDebugScript, "OC_CompareCharacterVariables()"); + + byte* buf1 = getCharacterAttributesPtr(); + int var1 = *buf1; + + int operation = _currScript->readUint16LE(); + + byte* buf2 = getCharacterAttributesPtr(); + int var2 = *buf2; + + return compareValues(var1, operation, var2); +} + +// TODO Rename function to "Check if current script character pos is in enclosure" +byte LilliputScript::OC_compareCoords_1() { + debugC(1, kDebugScript, "OC_compareCoords_1()"); + + int index = _currScript->readUint16LE(); + assert(index < 40); + + if (_vm->_enclosureRect[index].contains(_vm->_currentScriptCharacterPos)) + return 1; + + return 0; +} + +// TODO Rename function to "Check if given character pos is in enclosure" +byte LilliputScript::OC_compareCoords_2() { + debugC(1, kDebugScript, "OC_compareCoords_2()"); + + int16 idx1 = getValue1(); + int16 idx2 = _currScript->readUint16LE(); + + if (_vm->_enclosureRect[idx2].contains(_characterTilePos[idx1])) + return 1; + return 0; +} + +byte LilliputScript::OC_CompareDistanceFromCharacterToPositionWith() { + debugC(1, kDebugScript, "OC_CompareDistanceFromCharacterToPositionWith()"); + + Common::Point var1 = getPosFromScript(); + Common::Point pos = _vm->_currentScriptCharacterPos; + + int dx = var1.x - pos.x; + if (dx < 0) + dx = -dx; + + int dy = var1.y - pos.y; + if (dy < 0) + dy = -dy; + + int16 dist = dx + dy; + + uint16 operation = _currScript->readUint16LE(); + int16 var2 = _currScript->readSint16LE(); + + return compareValues(dist, operation, var2); +} + +byte LilliputScript::OC_compareRandomCharacterId() { + debugC(1, kDebugScript, "OC_compareRandomCharacterId()"); + + byte *tmpArr = getCharacterAttributesPtr(); + _lastRandomValue = _vm->_rnd->getRandomNumber(tmpArr[0] + 1); + uint16 oper = _currScript->readUint16LE(); + int16 var2 = _currScript->readSint16LE(); + + return compareValues(_lastRandomValue, oper, var2); +} + +byte LilliputScript::OC_IsCurrentCharacterIndex() { + debugC(1, kDebugScript, "OC_IsCurrentCharacterIndex()"); + + int tmpVal = getValue1(); + if (tmpVal == _vm->_currentScriptCharacter) + return 1; + return 0; +} + +byte LilliputScript::OC_hasVisibilityLevel() { + debugC(1, kDebugScript, "OC_hasVisibilityLevel()"); + + byte var4 = _currScript->readUint16LE() & 0xFF; + int tmpVal = _currScript->readUint16LE(); + + if (tmpVal < 2000) { + _currScript->seek(_currScript->pos() - 2); + int index = getValue1(); + int var1 = _interactions[(_vm->_currentScriptCharacter * 40) + index]; + if ((var1 & 0xFF) < var4) + return 0; + + _word16F00_characterId = index; + return 1; + } + + if (tmpVal == 3000) { + for (int i = 0; i < _vm->_numCharacters; i++) { + int var1 = _interactions[(_vm->_currentScriptCharacter * 40) + i]; + if ((var1 & 0xFF) >= var4) { + _word16F00_characterId = i; + return 1; + } + } + return 0; + } + + tmpVal -= 2000; + byte var4b = tmpVal & 0xFF; + for (int i = 0; i < _vm->_numCharacters; i++) { + int var1 = _interactions[(_vm->_currentScriptCharacter * 40) + i]; + if (((var1 & 0xFF) >= var4) && (_vm->_characterBehaviour[i] == var4b)) { + _word16F00_characterId = i; + return 1; + } + } + + return 0; +} + +byte LilliputScript::OC_hasGainedVisibilityLevel() { + debugC(1, kDebugScript, "OC_hasGainedVisibilityLevel()"); + + uint16 var4 = _currScript->readUint16LE(); + int index = _vm->_currentScriptCharacter * 40; + uint16 tmpVal = _currScript->readUint16LE(); + + if (tmpVal < 2000) { + _currScript->seek(_currScript->pos() - 2); + int subIndex = getValue1(); + tmpVal = _interactions[index + subIndex]; + byte v1 = tmpVal & 0xFF; + byte v2 = tmpVal >> 8; + if ((v1 < (var4 & 0xFF)) || (v2 >= (var4 & 0xFF))) + return 0; + _word16F00_characterId = subIndex; + return 1; + } + + int var1 = tmpVal; + if (var1 == 3000) { + for (int i = 0; i < _vm->_numCharacters; i++) { + tmpVal = _interactions[index + i]; + byte v1 = tmpVal & 0xFF; + byte v2 = tmpVal >> 8; + if ((v1 >= (var4 & 0xFF)) && (v2 < (var4 & 0xFF))) { + _word16F00_characterId = i; + return 1; + } + } + return 0; + } + + var1 -= 2000; + var4 = ((var1 & 0xFF) << 8) + (var4 & 0xFF); + for (int i = 0; i < _vm->_numCharacters; i++) { + tmpVal = _interactions[index + i]; + byte v1 = tmpVal & 0xFF; + byte v2 = tmpVal >> 8; + if ((v1 >= (var4 & 0xFF)) && (v2 < (var4 & 0xFF)) && (_vm->_characterBehaviour[i] == (var4 >> 8))) { + _word16F00_characterId = i; + return 1; + } + } + return 0; +} + +byte LilliputScript::OC_hasReducedVisibilityLevel() { + debugC(1, kDebugScript, "OC_hasReducedVisibilityLevel()"); + + byte var4 = _currScript->readUint16LE() & 0xFF; + + int tmpVal = _currScript->readUint16LE(); + + if (tmpVal < 2000) { + _currScript->seek(_currScript->pos() - 2); + int index = getValue1(); + int var1 = _interactions[(_vm->_currentScriptCharacter * 40) + index]; + if (((var1 & 0xFF) >= var4) || ((var1 >> 8) < var4)) + return 0; + + _word16F00_characterId = index; + return 1; + } + + if (tmpVal == 3000) { + for (int i = 0; i < _vm->_numCharacters; i++) { + int var1 = _interactions[(_vm->_currentScriptCharacter * 40) + i]; + if (((var1 & 0xFF) < var4) && ((var1 >> 8) >= var4)) { + _word16F00_characterId = i; + return 1; + } + } + return 0; + } + + tmpVal -= 2000; + byte var4b = tmpVal & 0xFF; + for (int i = 0; i < _vm->_numCharacters; i++) { + int var1 = _interactions[(_vm->_currentScriptCharacter * 40) + i]; + if (((var1 & 0xFF) < var4) && ((var1 >> 8) >= var4)) { + if (_vm->_characterBehaviour[i] == var4b) { + _word16F00_characterId = i; + return 1; + } + } + } + + return 0; +} + +byte LilliputScript::OC_isHost() { + debugC(1, kDebugScript, "OC_isHost()"); + + int tmpVal = getValue1(); + if (tmpVal == _vm->_host) + return 1; + + return 0; +} + +byte LilliputScript::OC_isSequenceActive() { + debugC(1, kDebugScript, "OC_isSequenceActive()"); + + int8 var1 = (_currScript->readUint16LE() & 0xFF); + if ((var1 == _characterLastSequence[_vm->_currentScriptCharacter]) && (_characterNextSequence[_vm->_currentScriptCharacter] != 16)) + return 1; + + return 0; +} + +byte LilliputScript::OC_isSequenceFinished() { + debugC(1, kDebugScript, "OC_isSequenceFinished()"); + + int8 var1 = (_currScript->readUint16LE() & 0xFF); + if ((var1 == _characterLastSequence[_vm->_currentScriptCharacter]) && (_characterNextSequence[_vm->_currentScriptCharacter] == 16)) + return 1; + + return 0; +} + +byte *LilliputScript::getMapPtr(Common::Point val) { + debugC(1, kDebugScript, "getMapPtr(%d %d)", val.x, val.y); + + return &_vm->_bufferIsoMap[(val.y * 64 + val.x) << 2]; +} + +byte LilliputScript::OC_CompareMapValueWith() { + debugC(1, kDebugScript, "OC_CompareMapValueWith()"); + + Common::Point tmpVal = getPosFromScript(); + + if (tmpVal == Common::Point(-1, -1)) { + _currScript->seek(_currScript->pos() + 6); + return 0; + } + int16 var2 = _currScript->readUint16LE(); + byte *buf = getMapPtr(tmpVal); + int16 var1 = buf[var2]; + uint16 oper = _currScript->readUint16LE(); + var2 = _currScript->readSint16LE(); + + return compareValues(var1, oper, var2); +} + +byte LilliputScript::OC_IsCharacterValid() { + debugC(1, kDebugScript, "OC_IsCharacterValid()"); + + int index = getValue1(); + if (_vm->_characterPos[index].x == -1) + return 0; + + return 1; +} + +byte LilliputScript::OC_CheckWaitingSignal() { + debugC(1, kDebugScript, "OC_CheckWaitingSignal()"); + + byte curByte = _currScript->readUint16LE() & 0xFF; + byte tmpVal = _vm->_waitingSignal; + + if (curByte != tmpVal) + return 0; + + _word16F00_characterId = _vm->_waitingSignalCharacterId; + return 1; +} + +byte LilliputScript::OC_CurrentCharacterVar0AndVar1Equals() { + debugC(1, kDebugScript, "OC_CurrentCharacterVar0AndVar1Equals()"); + + byte var1 = _currScript->readUint16LE() & 0xFF; + byte var2 = _currScript->readUint16LE() & 0xFF; + + assert(_vm->_currentCharacterAttributes != NULL); + + if ((var1 == _vm->_currentCharacterAttributes[0]) && (var2 == _vm->_currentCharacterAttributes[1])) + return 1; + + return 0; +} + +byte LilliputScript::OC_CurrentCharacterVar0Equals() { + debugC(1, kDebugScript, "OC_CurrentCharacterVar0Equals()"); + + byte curByte = (_currScript->readUint16LE() & 0xFF); + assert(_vm->_currentCharacterAttributes != NULL); + if (_vm->_currentCharacterAttributes[0] == curByte) + return 1; + return 0; +} + +byte LilliputScript::OC_checkLastInterfaceHotspotIndexMenu13() { + debugC(1, kDebugScript, "OC_checkLastInterfaceHotspotIndexMenu13()"); + + byte tmpVal = (_currScript->readUint16LE() & 0xFF); + + if ((_vm->_actionType != kButtonPressed) && (_vm->_actionType != kActionTalk)) + return 0; + + if (tmpVal == _vm->_lastInterfaceHotspotIndex) + return 1; + + return 0; +} + +byte LilliputScript::OC_checkLastInterfaceHotspotIndexMenu2() { + debugC(1, kDebugScript, "OC_checkLastInterfaceHotspotIndexMenu2()"); + + int8 hotspotIndex = (_currScript->readUint16LE() & 0xFF); + + if ((_vm->_actionType == kButtonReleased) && (hotspotIndex == _vm->_lastInterfaceHotspotIndex)) + return 1; + + return 0; +} + +byte LilliputScript::OC_CompareNumberOfCharacterWithVar0Equals() { + debugC(1, kDebugScript, "OC_CompareNumberOfCharacterWithVar0Equals()"); + + byte curByte = (_currScript->readUint16LE() & 0xFF); + int16 count = 0; + + for (int i = 0; i < _vm->_numCharacters; i++) { + if (curByte == *_vm->getCharacterAttributesPtr(32 * i)) + ++count; + } + + uint16 oper = _currScript->readUint16LE(); + int16 var2 = _currScript->readSint16LE(); + + return compareValues(count, oper, var2); +} + +byte LilliputScript::OC_IsPositionInViewport() { + debugC(1, kDebugScript, "OC_IsPositionInViewport()"); + + Common::Point var1 = getPosFromScript(); + + int16 dx = var1.x - _viewportPos.x; + int16 dy = var1.y - _viewportPos.y; + + if ((dx >= 0) && (dx < 8) && (dy >= 0) && (dy < 8)) + return 1; + return 0; +} + +byte LilliputScript::OC_CompareGameVariables() { + debugC(1, kDebugScript, "OC_CompareGameVariables()"); + + int16 var1 = getValue1(); + int16 var2 = getValue1(); + if (var1 == var2) + return 1; + return 0; +} + +byte LilliputScript::OC_skipNextOpcode() { + debugC(1, kDebugScript, "OC_skipNextOpcode()"); + + _currScript->readUint16LE(); + return 1; +} + +byte LilliputScript::OC_CheckCurrentCharacterAttr2() { + debugC(1, kDebugScript, "OC_CheckCurrentCharacterAttr2()"); + + assert(_vm->_currentCharacterAttributes != NULL); + if (_vm->_currentCharacterAttributes[2] == 1) + return 1; + return 0; +} + +byte LilliputScript::OC_CheckCurrentCharacterType() { + debugC(1, kDebugScript, "OC_CheckCurrentCharacterType()"); + + int index = getValue1(); + assert (index < 40); + + byte curByte = (_currScript->readUint16LE() & 0xFF); + if (curByte == _vm->_characterBehaviour[index]) + return 1; + + return 0; +} + +byte LilliputScript::OC_CheckCurrentCharacterAttr0And() { + debugC(1, kDebugScript, "OC_CheckCurrentCharacterAttr0And()"); + + byte *bufPtr = getCharacterAttributesPtr(); + byte var1 = bufPtr[0]; + byte curByte = (_currScript->readUint16LE() & 0xFF); + + if (var1 & curByte) + return 1; + + return 0; +} + +byte LilliputScript::OC_IsCurrentCharacterAttr0LessEqualThan() { + debugC(1, kDebugScript, "OC_IsCurrentCharacterAttr0LessEqualThan()"); + + assert(_vm->_currentCharacterAttributes != NULL); + byte curByte = (_currScript->readUint16LE() & 0xFF); + + if (curByte <= _vm->_currentCharacterAttributes[0]) + return 1; + return 0; +} + +byte LilliputScript::OC_isCarried() { + debugC(1, kDebugScript, "OC_isCarried()"); + + int16 index = getValue1(); + assert((index >= 0) && (index < 40)); + if (_vm->_characterCarried[index] == -1) + return 0; + + _word16F00_characterId = _vm->_characterCarried[index]; + + return 1; +} + +byte LilliputScript::OC_CheckCurrentCharacterAttr1() { + debugC(1, kDebugScript, "OC_CheckCurrentCharacterAttr1()"); + + assert(_vm->_currentCharacterAttributes != NULL); + byte curByte = (_currScript->readUint16LE() & 0xFF); + + if (_vm->_currentCharacterAttributes[1] == curByte) + return 1; + + return 0; +} + +byte LilliputScript::OC_isCurrentCharacterSpecial() { + debugC(1, kDebugScript, "OC_isCurrentCharacterSpecial()"); + + if (_vm->_currentScriptCharacterPos == Common::Point(-1, -1)) + return 0; + + if (_vm->_specialCubes[_vm->_currentScriptCharacter] == 0) + return 0; + + return 1; +} + +byte LilliputScript::OC_CurrentCharacterAttr3Equals1() { + debugC(1, kDebugScript, "OC_CurrentCharacterAttr3Equals1()"); + + assert(_vm->_currentCharacterAttributes != NULL); + if (_vm->_currentCharacterAttributes[3] == 1) + return 1; + + return 0; +} + +byte LilliputScript::OC_checkCharacterDirection() { + debugC(1, kDebugScript, "OC_checkCharacterDirection()"); + + int16 index = getValue1(); + byte expectedVal = (_currScript->readUint16LE() & 0xFF); + + if (_vm->_characterDirectionArray[index] == expectedVal) + return 1; + return 0; +} + +byte LilliputScript::OC_checkLastInterfaceHotspotIndex() { + debugC(1, kDebugScript, "OC_checkLastInterfaceHotspotIndex()"); + + uint16 index = _currScript->readUint16LE(); + int8 var2 = (_currScript->readUint16LE() & 0xFF); + + assert(index < 20); + + if (_interfaceHotspotStatus[index] == var2) + return 1; + + return 0; +} + +byte LilliputScript::OC_checkSelectedCharacter() { + debugC(1, kDebugScript, "OC_checkSelectedCharacter()"); + + if ((_vm->_selectedCharacterId != -1) || (_vm->_savedMousePosDivided == Common::Point(-1, -1))) + return 0; + + return 1; +} + +byte LilliputScript::OC_checkDelayedReactivation() { + debugC(1, kDebugScript, "OC_checkDelayedReactivation()"); + + if (_vm->_delayedReactivationAction || (_vm->_selectedCharacterId == -1)) + return 0; + + return 1; +} + +byte LilliputScript::OC_checkTargetReached() { + debugC(1, kDebugScript, "OC_checkTargetReached()"); + Common::Point pos = getPosFromScript(); + + if (_vm->_characterTargetPos[_vm->_currentScriptCharacter] == pos) + return 1; + + return 0; +} + +byte LilliputScript::OC_checkFunctionKeyPressed() { + debugC(1, kDebugScript, "OC_checkFunctionKeyPressed()"); + + static const Common::KeyCode specialKeys[10] = { + Common::KEYCODE_F10, Common::KEYCODE_F1, Common::KEYCODE_F2, Common::KEYCODE_F3, Common::KEYCODE_F4, + Common::KEYCODE_F5, Common::KEYCODE_F6, Common::KEYCODE_F7, Common::KEYCODE_F8, Common::KEYCODE_F9}; + + int8 index = (_currScript->readUint16LE() & 0xFF) - 0x30; + + if (specialKeys[index] == _vm->_lastKeyPressed.kbd.keycode) + return 1; + + return 0; +} + +byte LilliputScript::OC_checkCodeEntered() { + debugC(1, kDebugScript, "OC_checkCodeEntered()"); + + static const byte solutionArr[10] = {11, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + + if (_vm->_actionType == kCodeEntered) { + uint16 index = _currScript->readUint16LE(); + if (solutionArr[index] == _vm->_codeEntered[0]) { + index = _currScript->readUint16LE(); + if (solutionArr[index] == _vm->_codeEntered[1]) { + index = _currScript->readUint16LE(); + if (solutionArr[index] == _vm->_codeEntered[2]) { + return 1; + } + } else + // skip last index check + _currScript->seek(_currScript->pos() + 2); + } else + // skip two last index checks + _currScript->seek(_currScript->pos() + 4); + } else + // skip the three index checks + _currScript->seek(_currScript->pos() + 6); + + return 0; +} + +byte LilliputScript::OC_checkViewPortCharacterTarget() { + debugC(1, kDebugScript, "OC_checkViewPortCharacterTarget()"); + + int var1 = getValue1(); + if (var1 == _viewportCharacterTarget) + return 1; + + return 0; +} + +void LilliputScript::OC_setWord18821() { + debugC(1, kDebugScriptTBC, "OC_setWord18821()"); + + _word18821 = getValue1(); +} + +void LilliputScript::OC_ChangeIsoMap() { + debugC(1, kDebugScript, "OC_ChangeIsoMap()"); + + Common::Point var1 = getPosFromScript(); + int var2 = _currScript->readUint16LE(); + int var3 = _currScript->readUint16LE(); + byte* mapPtr = getMapPtr(var1); + + int mask = 8 >> var2; + mask = ~mask; + mapPtr[3] &= mask; + + if (var3 > 0) { + mask = ~mask; + mapPtr[3] |= mask; + } +} + +void LilliputScript::OC_startSpeech() { + debugC(1, kDebugScript, "OC_startSpeech()"); + + int curWord = _currScript->readUint16LE(); + + bool forceReturnFl = false; + checkSpeechAllowed(forceReturnFl); + if (forceReturnFl) + return; + + _talkingCharacter = _vm->_currentScriptCharacter; + startSpeech(curWord); +} + +void LilliputScript::getSpeechVariant(int speechIndex, int speechVariant) { + debugC(2, kDebugScript, "getSpeechVariant(%d, %d)", speechIndex, speechVariant); + + // The packed strings are stored by variants, enclosed by imbricated brackets. + // So the different possibilities are: + // text + // [text1]text2 + // [[text1]text2]text3 + // etc etc + + if (speechIndex == -1) + return; + + _currentSpeechId = speechIndex; + int index = _vm->_packedStringIndex[speechIndex]; + + // Skip the speech variant opening characters + while (_vm->_packedStrings[index] == '[') + ++index; + + for (int i = 0; i < speechVariant; i++) { + byte tmpVal = ' '; + // Skip a speech variant + while (tmpVal != ']') { + tmpVal = _vm->_packedStrings[index]; + ++index; + } + } + + if (_vm->_packedStrings[index] == 0) + return; + + decodePackedText(&_vm->_packedStrings[index]); +} + +void LilliputScript::OC_getComputedVariantSpeech() { + debugC(1, kDebugScript, "OC_getComputedVariantSpeech()"); + + int tmpVal1 = getCharacterAttributesPtr()[0]; + int tmpVal2 = (_currScript->readUint16LE() & 0xFF); + int speechVariant = tmpVal1 / tmpVal2; + + int speechIndex = _currScript->readUint16LE(); + + bool forceReturnFl = false; + checkSpeechAllowed(forceReturnFl); + if (forceReturnFl) + return; + + _talkingCharacter = _vm->_currentScriptCharacter; + getSpeechVariant(speechIndex, speechVariant); +} + +void LilliputScript::OC_getRotatingVariantSpeech() { + debugC(1, kDebugScript, "OC_getRotatingVariantSpeech()"); + + int index = _currScript->readUint16LE(); + int maxValue = getPackedStringStartRelativeIndex(index); + + int currVariant = _currScript->readUint16LE(); + int nextVariant = currVariant + 1; + if (nextVariant >= maxValue) + nextVariant = 0; + _currScript->writeUint16LE(nextVariant, -2); + + bool forceReturnFl = false; + checkSpeechAllowed(forceReturnFl); + if (forceReturnFl) + return; + + _talkingCharacter = _vm->_currentScriptCharacter; + + getSpeechVariant(index, currVariant); + +} + +void LilliputScript::OC_startSpeechIfMute() { + debugC(1, kDebugScript, "OC_startSpeechIfMute()"); + + if (_talkingCharacter == -1) { + OC_startSpeech(); + return; + } + + _currScript->readUint16LE(); +} + +void LilliputScript::OC_getComputedVariantSpeechIfMute() { + debugC(1, kDebugScript, "OC_getComputedVariantSpeechIfMute()"); + + if (_talkingCharacter == -1) { + OC_getComputedVariantSpeech(); + return; + } + _currScript->readUint16LE(); + _currScript->readUint16LE(); + _currScript->readUint16LE(); + _currScript->readUint16LE(); + +} + +void LilliputScript::OC_startSpeechIfSilent() { + debugC(1, kDebugScript, "OC_startSpeechIfSilent()"); + + if (_talkingCharacter == -1) { + OC_getRotatingVariantSpeech(); + return; + } + _currScript->readUint16LE(); + _currScript->readUint16LE(); +} + +void LilliputScript::OC_ComputeCharacterVariable() { + debugC(1, kDebugScript, "OC_ComputeCharacterVariable()"); + + byte *bufPtr = getCharacterAttributesPtr(); + uint16 oper = _currScript->readUint16LE(); + int16 var3 = _currScript->readSint16LE(); + + computeOperation(bufPtr, oper, var3); +} + +void LilliputScript::OC_setAttributeToRandom() { + debugC(1, kDebugScript, "OC_setAttributeToRandom()"); + + byte *bufPtr = getCharacterAttributesPtr(); + int maxVal = _currScript->readUint16LE(); + int randomVal = _vm->_rnd->getRandomNumber(maxVal); + *bufPtr = randomVal; +} + +void LilliputScript::OC_setCharacterPosition() { + debugC(1, kDebugScript, "OC_setCharacterPosition()"); + + int index = getValue1(); + assert((index >= 0) && (index < 40)); + Common::Point tmpVal = getPosFromScript(); + + int charPosX = (tmpVal.x << 3) + 4; + int charPosY = (tmpVal.y << 3) + 4; + + _vm->_characterPos[index] = Common::Point(charPosX, charPosY); +} + +void LilliputScript::OC_DisableCharacter() { + debugC(1, kDebugScript, "OC_DisableCharacter()"); + + int characterIndex = getValue1(); + assert(characterIndex < 40); + + if (characterIndex == _vm->_host) + _viewportCharacterTarget = -1; + + _vm->_characterPos[characterIndex] = Common::Point(-1, -1); +} + +void LilliputScript::OC_saveAndQuit() { + warning("TODO: OC_saveAndQuit"); + _vm->_soundHandler->remove(); // Kill music + // TODO: Save game + _vm->_shouldQuit = true; +} + +void LilliputScript::OC_nSkipOpcodes() { + debugC(1, kDebugScript, "OC_nSkipOpcodes()"); + + int var1 = _currScript->readUint16LE(); + skipOpcodes(var1); +} + +void LilliputScript::OC_startSpeech5() { + debugC(1, kDebugScript, "OC_startSpeech5()"); + + bool forceReturnFl = false; + checkSpeechAllowed(forceReturnFl); + if (forceReturnFl) + return; + + _talkingCharacter = _vm->_currentScriptCharacter; + startSpeech(5); +} + +void LilliputScript::OC_resetHandleOpcodeFlag() { + debugC(1, kDebugScript, "OC_resetHandleOpcodeFlag()"); + + _vm->_handleOpcodeReturnCode = 0; +} + +void LilliputScript::OC_deleteSavegameAndQuit() { + warning("TODO: OC_deleteSavegameAndQuit"); + _vm->_shouldQuit = true; +} + +void LilliputScript::OC_incScriptForVal() { + debugC(1, kDebugScript, "OC_incScriptForVal()"); + + ++_scriptForVal; +} + +void LilliputScript::OC_computeChararacterAttr() { + debugC(1, kDebugScript, "OC_ComputeChararacterAttr()"); + + byte *tmpArr = getCharacterAttributesPtr(); + uint16 oper = _currScript->readUint16LE(); + int16 var3 = getCharacterAttributesPtr()[0]; + computeOperation(tmpArr, oper, var3); +} + +void LilliputScript::OC_setTextVarNumber() { + debugC(1, kDebugScript, "OC_setTextVarNumber()"); + + byte *tmpArr = getCharacterAttributesPtr(); + _textVarNumber = *tmpArr; +} + +void LilliputScript::OC_callScript() { + debugC(1, kDebugScript, "OC_callScript()"); + + int index = _currScript->readUint16LE(); + int charIndex = getValue1(); + _vm->setCurrentCharacter(charIndex); + + int tmpIndex = _vm->_currentScriptCharacter; + + assert(index < _vm->_gameScriptIndexSize); + int scriptIndex = _vm->_arrayGameScriptIndex[index]; + + _scriptStack.push(_currScript); + + if (_byte16F05_ScriptHandler == 0) { + _vm->_handleOpcodeReturnCode = 0; + debugC(1, kDebugScript, "========================== Menu Script %d==============================", scriptIndex); + runMenuScript(ScriptStream(&_vm->_arrayGameScripts[scriptIndex], _vm->_arrayGameScriptIndex[index + 1] - _vm->_arrayGameScriptIndex[index])); + debugC(1, kDebugScript, "========================== End of Menu Script=============================="); + } else { + runScript(ScriptStream(&_vm->_arrayGameScripts[scriptIndex], _vm->_arrayGameScriptIndex[index + 1] - _vm->_arrayGameScriptIndex[index])); + } + + _currScript = _scriptStack.pop(); + + _vm->setCurrentCharacter(tmpIndex); +} + +void LilliputScript::OC_callScriptAndReturn() { + debugC(1, kDebugScript, "OC_callScriptAndReturn()"); + + OC_callScript(); + skipOpcodes(0); +} + +void LilliputScript::OC_setCurrentScriptCharacterPos() { + debugC(1, kDebugScript, "OC_setCurrentScriptCharacterPos()"); + + Common::Point pos = getPosFromScript(); + _vm->_characterTargetPos[_vm->_currentScriptCharacter] = pos; + _vm->_characterSubTargetPos[_vm->_currentScriptCharacter].x = -1; +} + +void LilliputScript::OC_initScriptFor() { + debugC(1, kDebugScript, "OC_initScriptFor()"); + + _scriptForVal = 0; +} + +void LilliputScript::OC_setCurrentCharacterSequence() { + debugC(1, kDebugScript, "OC_setCurrentCharacterSequence()"); + + int8 seqIdx = (_currScript->readUint16LE() & 0xFF); + setSequence(_vm->_currentScriptCharacter, seqIdx); +} + +void LilliputScript::OC_setNextCharacterSequence() { + debugC(1, kDebugScript, "OC_setNextCharacterSequence()"); + + int8 seqIdx = (_currScript->readUint16LE() & 0xFF); + setSequence(_vm->_currentScriptCharacter + 1, seqIdx); +} + +void LilliputScript::OC_setHost() { + debugC(1, kDebugScript, "OC_setHost()"); + + _vm->_host = getValue1(); +} + +void LilliputScript::OC_changeMapCube() { + debugC(1, kDebugScript, "OC_changeMapCube()"); + + assert(_vm->_currentCharacterAttributes != NULL); + Common::Point var1 = Common::Point(_vm->_currentCharacterAttributes[4], _vm->_currentCharacterAttributes[5]); + byte var2 = _vm->_currentCharacterAttributes[6]; + + byte *mapPtr = getMapPtr(var1); + mapPtr[var2] = _vm->_currentCharacterAttributes[7]; + mapPtr[3] = _vm->_currentCharacterAttributes[8]; + + if (var2 == 0) { + _vm->_refreshScreenFlag = true; + _vm->displayLandscape(); + _vm->_refreshScreenFlag = false; + } +} + +void LilliputScript::OC_setCharacterCarry() { + debugC(1, kDebugScript, "OC_setCharacterCarry()"); + + int8 carriedIdx = (getValue1() & 0xFF); + int16 index = getValue1(); + + int8 distBehind = (_currScript->readSint16LE() & 0xFF); + byte distAbove = (_currScript->readUint16LE() & 0xFF); + + assert((index >= 0) && (index < 40)); + _vm->_characterCarried[index] = carriedIdx; + _vm->_characterBehindDist[index] = distBehind; + _vm->_characterAboveDist[index] = distAbove; +} + +void LilliputScript::OC_dropCarried() { + debugC(1, kDebugScript, "OC_dropCarried()"); + + int index = getValue1(); + _vm->_characterCarried[index] = -1; + _vm->_characterPosAltitude[index] = 0; + _characterScriptEnabled[index] = 1; +} + +void LilliputScript::OC_setCurrentCharacter() { + debugC(1, kDebugScript, "OC_setCurrentCharacter()"); + int index = getValue1(); + _vm->setCurrentCharacter(index); +} + +void LilliputScript::sendSignal(int16 var1, byte var2h, byte characterId, int16 var4) { + debugC(2, kDebugScript, "sendSignal(%d, %d, %d, %d)", var1, var2h, characterId, var4); + + int index = 0; + for (int i = 0; i < 10; i++) { + if (_vm->_signalArray[index + 1] == -1) { + _vm->_signalArray[index + 1] = var1; + _vm->_signalArray[index + 2] = (var2h << 8) + characterId; + _vm->_signalArray[index + 0] = _vm->_signalTimer + var4; + return; + } + index += 3; + } +} + +void LilliputScript::OC_sendSeeSignal() { + debugC(1, kDebugScript, "OC_sendSeeSignal()"); + + int16 type = 2 << 8; // SEE + int16 var4 = _currScript->readSint16LE(); + byte var2h = (_currScript->readUint16LE() & 0xFF); + + sendSignal(type, var2h, _vm->_currentScriptCharacter, var4); +} + +void LilliputScript::OC_sendHearSignal() { + debugC(1, kDebugScript, "OC_sendHearSignal()"); + + int16 type = 1 << 8; // HEAR + int16 var4 = _currScript->readSint16LE(); + byte var2h = (_currScript->readUint16LE() & 0xFF); + + sendSignal(type, var2h, _vm->_currentScriptCharacter, var4); +} + +void LilliputScript::OC_sendVarSignal() { + debugC(1, kDebugScript, "OC_sendVarSignal()"); + + int16 var4 = _currScript->readSint16LE(); + int16 type = getValue1(); + byte var2h = (_currScript->readUint16LE() & 0xFF); + + sendSignal(type, var2h, _vm->_currentScriptCharacter, var4); +} + +void LilliputScript::OC_sendBroadcastSignal() { + debugC(1, kDebugScript, "OC_sendBroadcastSignal()"); + + int16 type = 3 << 8; + int16 var4 = _currScript->readSint16LE(); + byte var2h = (_currScript->readUint16LE() & 0xFF); + + sendSignal(type, var2h, _vm->_currentScriptCharacter, var4); +} + +void LilliputScript::OC_resetWaitingSignal() { + debugC(1, kDebugScript, "OC_resetWaitingSignal()"); + + _vm->_waitingSignal = -1; + _vm->_waitingSignalCharacterId = -1; +} + +void LilliputScript::OC_enableCurrentCharacterScript() { + debugC(1, kDebugScript, "OC_enableCurrentCharacterScript()"); + + uint8 var1 = (_currScript->readUint16LE() & 0xFF); + enableCharacterScript(_vm->_currentScriptCharacter , var1, _vm->_currentCharacterAttributes); + skipOpcodes(0); +} + +void LilliputScript::OC_IncCurrentCharacterVar1() { + debugC(1, kDebugScript, "OC_IncCurrentCharacterVar1()"); + + assert(_vm->_currentCharacterAttributes != NULL); + ++_vm->_currentCharacterAttributes[1]; +} + +void LilliputScript::OC_setCurrentCharacterPos() { + debugC(1, kDebugScript, "OC_setCurrentCharacterPos()"); + + uint16 oper = _currScript->readUint16LE(); + Common::Point var1 = getPosFromScript(); + byte* buf = _vm->_currentCharacterAttributes + 4; + computeOperation(buf, oper, var1.x); + computeOperation(buf + 1, oper, var1.y); +} + +void LilliputScript::OC_setCurrentCharacterBehavior() { + debugC(1, kDebugScript, "OC_setCurrentCharacterBehavior()"); + + uint16 var1 = _currScript->readUint16LE(); + _vm->_characterBehaviour[_vm->_currentScriptCharacter] = (var1 - 2000) & 0xFF; +} + +void LilliputScript::OC_changeCurrentCharacterSprite() { + debugC(2, kDebugScript, "OC_changeCurrentCharacterSprite()"); + + int16 var1 = _currScript->readSint16LE(); + byte var2 = (_currScript->readUint16LE() & 0xFF); + _vm->_characterFrameArray[_vm->_currentScriptCharacter] = var1; + _vm->_spriteSizeArray[_vm->_currentScriptCharacter] = var2; + +} + +byte *LilliputScript::getCurrentCharacterVarFromScript() { + debugC(2, kDebugScript, "getCurrentCharacterVarFromScript()"); + + int index = _currScript->readUint16LE(); + return &_vm->_currentCharacterAttributes[index]; +} + +void LilliputScript::OC_getList() { + debugC(1, kDebugScript, "OC_getList()"); + + byte *compBuf = getCurrentCharacterVarFromScript(); + uint16 oper = _currScript->readUint16LE(); + int index = _currScript->readUint16LE(); + + byte *buf = getCurrentCharacterVarFromScript(); + byte var1 = buf[0]; + byte var3 = _vm->_listArr[var1 + _vm->_listIndex[index]]; + + computeOperation(compBuf, oper, var3); +} + +void LilliputScript::OC_setList() { + debugC(1, kDebugScript, "OC_setList()"); + + int indexChunk10 = _currScript->readUint16LE(); + + byte *compBuf = getCurrentCharacterVarFromScript(); + int indexChunk11 = _vm->_listIndex[indexChunk10] + compBuf[0]; + + uint16 oper = _currScript->readUint16LE(); + + byte *tmpBuf = getCurrentCharacterVarFromScript(); + int16 var3 = tmpBuf[0]; + + computeOperation(&_vm->_listArr[indexChunk11], oper, var3); +} + +Common::Point LilliputScript::getCharacterTilePos(int index) { + debugC(2, kDebugScript, "getCharacterTilePos(%d)", index); + + return Common::Point(_vm->_characterPos[index].x >> 3, _vm->_characterPos[index].y >> 3); +} + +void LilliputScript::OC_setCharacterDirectionTowardsPos() { + debugC(1, kDebugScript, "OC_setCharacterDirectionTowardsPos()"); + + Common::Point pos1 = getPosFromScript(); + Common::Point tilePos = getCharacterTilePos(_vm->_currentScriptCharacter); + + _vm->_characterDirectionArray[_vm->_currentScriptCharacter] = _vm->getDirection(tilePos, pos1); +} + +void LilliputScript::OC_turnCharacterTowardsAnother() { + debugC(1, kDebugScript, "OC_turnCharacterTowardsAnother()"); + + int index = getValue1(); + + static const byte _directionsArray[] = { 0, 2, 0, 1, 3, 2, 3, 1 }; + + int dx = _vm->_characterPos[index].x - _vm->_characterPos[_vm->_currentScriptCharacter].x; + int dy = _vm->_characterPos[index].y - _vm->_characterPos[_vm->_currentScriptCharacter].y; + + int flag = 0; + if (dx < 0) { + dx = -dx; + flag |= 4; + } + if (dy < 0) { + dy = -dy; + flag |= 2; + } + if (dx < dy) { + flag |= 1; + } + + _vm->_characterDirectionArray[_vm->_currentScriptCharacter] = _directionsArray[flag]; +} + +void LilliputScript::OC_setSeek() { + debugC(1, kDebugScript, "OC_setSeek()"); + + int16 var = getValue1(); + _characterSeek[_vm->_currentScriptCharacter] = (byte)(var & 0xFF); + _vm->_characterSubTargetPos[_vm->_currentScriptCharacter].x = -1; +} + +void LilliputScript::OC_scrollAwayFromCharacter() { + debugC(1, kDebugScript, "OC_scrollAwayFromCharacter()"); + + if (_vm->_currentScriptCharacter != _viewportCharacterTarget) + return; + + static const int8 speedX[] = {-1, -3, -3, -6}; + static const int8 speedY[] = {-3, -6, -1, -3}; + + int cx = speedX[_vm->_characterDirectionArray[_vm->_currentScriptCharacter]]; + int cy = speedY[_vm->_characterDirectionArray[_vm->_currentScriptCharacter]]; + + Common::Point pos = getCharacterTilePos(_vm->_currentScriptCharacter); + + int newPosX = pos.x + cx; + int newPosY = pos.y + cy; + + newPosX = CLIP(newPosX, 0, 56); + newPosY = CLIP(newPosY, 0, 56); + + _vm->_refreshScreenFlag = true; + _vm->viewportScrollTo(Common::Point(newPosX, newPosY)); + _vm->_refreshScreenFlag = false; + +} + +void LilliputScript::OC_skipNextVal() { + debugC(1, kDebugScript, "OC_skipNextVal()"); + + _currScript->readUint16LE(); +} + +void LilliputScript::OC_setCurrentCharacterAttr6() { + debugC(1, kDebugScript, "OC_setCurrentCharacterAttr6()"); + + uint16 var1 = (uint16)getValue1(); + _vm->_currentCharacterAttributes[6] = var1 & 0xFF; +} + +void LilliputScript::OC_setCurrentCharacterPose() { + debugC(1, kDebugScript, "OC_setCurrentCharacterPose()"); + + int index = _currScript->readUint16LE(); + + int tmpVal = (_vm->_currentScriptCharacter * 32) + index; + assert (tmpVal < 40 * 32); + _characterPose[_vm->_currentScriptCharacter] = _vm->_poseArray[tmpVal]; + _characterNextSequence[_vm->_currentScriptCharacter] = 16; +} + +void LilliputScript::OC_setCharacterScriptEnabled() { + debugC(1, kDebugScript, "OC_setCharacterScriptEnabled()"); + + int16 index = getValue1(); + _characterScriptEnabled[index] = 1; +} + +void LilliputScript::OC_setCurrentCharacterAttr2() { + debugC(1, kDebugScript, "OC_setCurrentCharacterAttr2()"); + + int curWord = _currScript->readUint16LE(); + assert(_vm->_currentCharacterAttributes != NULL); + _vm->_currentCharacterAttributes[2] = curWord & 0xFF; +} + +void LilliputScript::OC_clearCurrentCharacterAttr2() { + debugC(1, kDebugScript, "OC_clearCurrentCharacterAttr2()"); + + assert(_vm->_currentCharacterAttributes != NULL); + _vm->_currentCharacterAttributes[2] = 0; +} + +void LilliputScript::OC_setCharacterProperties() { + debugC(1, kDebugScript, "OC_setCharacterProperties()"); + + int16 index = getValue1(); + + int16 x = _vm->_characterPos[index].x & 0xFFF8; + x += _currScript->readSint16LE(); + _vm->_characterPos[index].x = x; + + int16 y = _vm->_characterPos[index].y & 0xFFF8; + y += _currScript->readSint16LE(); + _vm->_characterPos[index].y = y; + + _vm->_characterPosAltitude[index] = (int8)(_currScript->readUint16LE() & 0xFF); + _vm->_characterDirectionArray[index] = _currScript->readUint16LE() & 0xFF; +} + +void LilliputScript::OC_setMonitoredCharacter() { + debugC(1, kDebugScript, "OC_setMonitoredCharacter()"); + + _monitoredCharacter = getValue1(); + for (int i = 0; i < 4; i++) + _monitoredAttr[i] = _currScript->readUint16LE() & 0xFF; +} + +void LilliputScript::OC_setNewPose() { + debugC(1, kDebugScript, "OC_setNewPose()"); + + int var2 = _currScript->readUint16LE(); + byte var1 = (_currScript->readUint16LE() & 0xFF); + + _vm->_poseArray[(_vm->_currentScriptCharacter * 32) + var2] = var1; +} + +void LilliputScript::OC_setCurrentCharacterDirection() { + debugC(1, kDebugScript, "OC_setCurrentCharacterDirection()"); + + _vm->_characterDirectionArray[_vm->_currentScriptCharacter] = (_currScript->readUint16LE() & 0xFF); +} + +void LilliputScript::OC_setInterfaceHotspot() { + debugC(1, kDebugScript, "OC_setInterfaceHotspot()"); + + int16 index = _currScript->readSint16LE(); + assert((index >= 0) && (index < 20)); + + uint16 curWord = _currScript->readUint16LE(); + _interfaceHotspotStatus[index] = (curWord & 0xFF); + _interfaceButtonActivationDelay[index] = (curWord >> 8); + + _vm->displayInterfaceHotspots(); +} + +void LilliputScript::OC_scrollViewPort() { + debugC(1, kDebugScript, "OC_scrollViewPort()"); + + _viewportCharacterTarget = -1; + + int direction = _currScript->readUint16LE(); + + static const int8 scrollValX[] = { 6, 0, 0, -6 }; + static const int8 scrollValY[] = { 0, -6, 6, 0 }; + + int x = _viewportPos.x + scrollValX[direction]; + int y = _viewportPos.y + scrollValY[direction]; + + x = CLIP(x, 0, 56); + y = CLIP(y, 0, 56); + + _vm->_refreshScreenFlag = true; + _vm->viewportScrollTo(Common::Point(x, y)); + _vm->_refreshScreenFlag = false; +} + +void LilliputScript::OC_setViewPortPos() { + debugC(1, kDebugScript, "OC_setViewPortPos()"); + + _viewportCharacterTarget = -1; + _viewportPos = getPosFromScript(); + + _vm->displayLandscape(); + _vm->prepareGameArea(); +} + +void LilliputScript::OC_setCurrentCharacterAltitude() { + debugC(1, kDebugScript, "OC_setCurrentCharacterAltitude()"); + + _vm->_characterPosAltitude[_vm->_currentScriptCharacter] = (_currScript->readUint16LE() & 0xFF); +} + +void LilliputScript::OC_setModePriority() { + debugC(1, kDebugScript, "OC_setModePriority()"); + + EvaluatedMode newMode; + + newMode._mode = _currScript->readUint16LE() & 0xFF; + newMode._priority = _currScript->readUint16LE() & 0xFF; + + setMode(newMode); +} + +void LilliputScript::setMode(EvaluatedMode newMode) { + debugC(2, kDebugScript, "setMode(%d - %d)", newMode._mode, newMode._priority); + + for (int i = 0; i < _vm->_newModesEvaluatedNumber; i++) { + if (_newEvaluatedModes[i]._mode == newMode._mode) { + int newPriority = newMode._priority + _newEvaluatedModes[i]._priority; + newPriority = CLIP(newPriority, 0, 255); + + _newEvaluatedModes[i]._priority = newPriority; + return; + } + } + + _newEvaluatedModes[_vm->_newModesEvaluatedNumber] = newMode; + ++_vm->_newModesEvaluatedNumber; +} + +void LilliputScript::OC_setComputedModePriority() { + debugC(1, kDebugScript, "OC_setComputedModePriority()"); + + int8 mode = (int8)(_currScript->readUint16LE() & 0xFF); + byte oper = _currScript->readUint16LE() & 0xFF; + uint16 index = _currScript->readUint16LE(); + int16 c = _vm->_currentCharacterAttributes[index]; + + switch (oper) { + case '-': + c = -1 - c; + break; + case '>': + c -= 128; + if (c < 0) + c = 0; + c *= 2; + break; + case '<': + c = -1 - c - 128; + if (c < 0) + c = 0; + c *= 2; + break; + case '+': + break; + default: + warning("OC_setComputedModePriority: skipped oper %c", oper); + break; + } + if (c > 0xFF) + warning("OC_setComputedModePriority- Abnormal value c = %d, should put back c &= 0xFF;", c); + + int priority = (_currScript->readSint16LE() * c) + c; + priority >>= 8; + + EvaluatedMode newMode; + newMode._mode = mode; + newMode._priority = priority; + + setMode(newMode); +} + +void LilliputScript::OC_selectBestMode() { + debugC(1, kDebugScript, "OC_selectBestMode()"); + + uint16 var1 = _currScript->readUint16LE(); + + int maxValue = 0; + int maxItem = var1 & 0xFF; + + for (int i = 0; i < _vm->_newModesEvaluatedNumber; i++) { + if (_newEvaluatedModes[i]._priority > maxValue) { + maxValue = _newEvaluatedModes[i]._priority; + maxItem = _newEvaluatedModes[i]._mode; + } + } + enableCharacterScript(_vm->_currentScriptCharacter, maxItem, _vm->_currentCharacterAttributes); +} + +void LilliputScript::OC_magicPuffEntrance() { + debugC(1, kDebugScript, "OC_magicPuffEntrance()"); + + int16 index = getValue1(); + assert((index >0) && (index < 40)); + + _vm->_characterMagicPuffFrame[index] = 4; +} + +void LilliputScript::OC_spawnCharacterAtPos() { + debugC(1, kDebugScript, "OC_spawnCharacterAtPos()"); + + int index = getValue1(); + Common::Point var4 = getPosFromScript(); + + Common::Point pt = var4 + _viewportPos; + byte *isoMapBuf = getMapPtr(pt); + + if (isoMapBuf[1] != 0xFF) { + int minVal = INT_MAX; + for (int var2 = 7; var2 >= 0; var2--) { + for (int var3 = 7; var3 >= 0; var3--) { + Common::Point(_viewportPos.x + var2, _viewportPos.y + var3); + isoMapBuf = getMapPtr(pt); + + if (isoMapBuf[1] == 0xFF) { + int x = abs(var2 - var4.x); + int y = abs(var3 - var4.y); + if (x + y < minVal) { + minVal = x + y; + _word1825E = Common::Point(var2, var3); + } + } + } + } + var4 = _word1825E; + } + + _vm->_characterPos[index].x = (var4.x + _viewportPos.x) * 8; + _vm->_characterPos[index].y = (var4.y + _viewportPos.y) * 8; +} + +void LilliputScript::OC_CharacterVariableAddOrRemoveFlag() { + debugC(1, kDebugScript, "OC_CharacterVariableAddOrRemoveFlag()"); + + byte *tmpArr = getCharacterAttributesPtr(); + + byte var1 = (_currScript->readUint16LE() & 0xFF); + byte var2 = (_currScript->readUint16LE() & 0xFF); + + if (var2 == 0) + tmpArr[0] &= ~var1; + else + tmpArr[0] |= var1; +} + +void LilliputScript::OC_PaletteFadeOut() { + debugC(1, kDebugScript, "OC_PaletteFadeOut()"); + + _vm->_refreshScreenFlag = true; + _vm->paletteFadeOut(); + _vm->_refreshScreenFlag = false; +} + +void LilliputScript::OC_PaletteFadeIn() { + debugC(1, kDebugScript, "OC_PaletteFadeIn()"); + + _vm->_refreshScreenFlag = true; + _vm->paletteFadeIn(); + _vm->_refreshScreenFlag = false; +} + +void LilliputScript::OC_loadAndDisplayCubesGfx() { + debugC(1, kDebugScript, "OC_loadAndDisplayCubesGfx()"); + + int setNumb = (_currScript->readUint16LE() & 0xFF); + assert((setNumb >= 0) && (setNumb <= 9)); + Common::String fileName = Common::String::format("CUBES%d.GFX", setNumb); + _cubeSet = setNumb; // Useless in this variant, keep for the moment for Rome + + _vm->_bufferCubegfx = _vm->loadVGA(fileName, 61440, false); + _vm->displayLandscape(); + _vm->prepareGameArea(); +} + +void LilliputScript::OC_setCurrentCharacterAttr3() { + debugC(1, kDebugScript, "OC_setCurrentCharacterAttr3()"); + + byte var1 = _currScript->readUint16LE() & 0xFF; + assert(_vm->_currentCharacterAttributes != NULL); + + _vm->_currentCharacterAttributes[3] = var1; +} + +void LilliputScript::OC_setArray122C1() { + debugC(1, kDebugScript, "OC_setArray122C1()"); + + byte var1 = (_currScript->readUint16LE() & 0xFF); + _array122C1[_vm->_currentScriptCharacter] = var1; +} + +void LilliputScript::OC_sub18367() { + debugC(1, kDebugScriptTBC, "OC_sub18367()"); + + _characterScriptEnabled[_vm->_currentScriptCharacter] = 1; + _vm->_currentCharacterAttributes[0] = _array122C1[_vm->_currentScriptCharacter]; + _vm->_currentCharacterAttributes[1] = 0; + _vm->_currentCharacterAttributes[2] = 0; + _vm->_currentCharacterAttributes[3] = 0; +} + +void LilliputScript::OC_enableCharacterScript() { + debugC(1, kDebugScript, "OC_enableCharacterScript()"); + + int16 index = getValue1(); + byte var2 = _currScript->readUint16LE() & 0xFF; + + enableCharacterScript(index, var2, _vm->getCharacterAttributesPtr(index * 32)); +} + +void LilliputScript::OC_setRulesBuffer2Element() { + debugC(1, kDebugScript, "OC_setRulesBuffer2Element()"); + + int index = getValue1(); + byte var1 = _currScript->readUint16LE() & 0xFF; + + assert((index >= 0) && (index < 40)); + _vm->_characterMobility[index] = var1; +} + +void LilliputScript::OC_setDebugFlag() { + debugC(1, kDebugScript, "OC_setDebugFlag()"); + + _vm->_debugFlag = 1; +} + +void LilliputScript::OC_setDebugFlag2() { + debugC(1, kDebugScript, "OC_setDebugFlag2()"); + + _vm->_debugFlag2 = 1; +} + +void LilliputScript::OC_waitForEvent() { + debugC(1, kDebugScript, "OC_waitForEvent()"); + + _vm->_refreshScreenFlag = true; + while (true) { + if (_vm->_keyboard_checkKeyboard()) { + _vm->_keyboard_getch(); + break;; + } + if (_vm->_mouseButton == 1) + break; + + _vm->update(); + } + + _vm->_mouseButton = 0; + _vm->_refreshScreenFlag = false; +} + +void LilliputScript::OC_disableInterfaceHotspot() { + debugC(1, kDebugScript, "OC_disableInterfaceHotspot()"); + + int index = _currScript->readUint16LE(); + _interfaceButtonActivationDelay[index] = (_currScript->readUint16LE() & 0xFF); + _interfaceHotspotStatus[index] = kHotspotDisabled; + + _vm->displayInterfaceHotspots(); +} + +void LilliputScript::OC_loadFileAerial() { + debugC(1, kDebugScript, "OC_loadFileAerial()"); + + // Unused variable, and the script position is restored afterwards + // TODO: Check if this part of the code is present in Rome, else remove it + // int var1 = _currScript->readUint16LE() & 0xFF; + // byte _byte15EAD = var1; + + _vm->_refreshScreenFlag = true; + _talkingCharacter = -1; + OC_PaletteFadeOut(); + _vm->_displayGreenHand = true; + _vm->displayVGAFile("AERIAL.GFX"); + OC_PaletteFadeIn(); + + _vm->displayCharactersOnMap(); + _vm->_displayMap = true; + + _vm->_keyboard_resetKeyboardBuffer(); + + _vm->_refreshScreenFlag = false; +} + +void LilliputScript::OC_startSpeechIfSoundOff() { + debugC(1, kDebugScript, "OC_startSpeechIfSoundOff()"); + + // HACK: In the original, OC_startSpeechIfSoundOff() only calls + // OC_startSpeech if sound is off. For the moment, it's always called + + OC_startSpeech(); +} + +void LilliputScript::OC_sub1844A() { + debugC(1, kDebugScriptTBC, "OC_sub1844A()"); + + int characterIndex = getValue1(); + int var2 = _currScript->readUint16LE(); + + _vm->_characterTypes[characterIndex] = (var2 & 0xFF); + + for (int i = 0; i < 40; i++) { + _interactions[40 * characterIndex + i] = 0; + _interactions[characterIndex + 40 * i] = 0; + } +} + +void LilliputScript::OC_displayNumericCharacterVariable() { + debugC(1, kDebugScript, "OC_displayNumericCharacterVariable()"); + + byte *charAttrArr = getCharacterAttributesPtr(); + byte attr = charAttrArr[0]; + int divisor = _currScript->readUint16LE(); + assert(divisor != 0); + int displayVal = attr / (divisor & 0xFF); + int posX = _currScript->readSint16LE(); + int posY = _currScript->readSint16LE(); + + if (!_vm->_displayMap) + displayNumber(displayVal, Common::Point(posX, posY)); +} + +void LilliputScript::displayNumber(byte var1, Common::Point pos) { + debugC(1, kDebugScript, "displayNumber(%d, %d - %d)", var1, pos.x, pos.y); + + _vm->_displayStringIndex = 0; + _vm->_displayStringBuf[0] = 32; + _vm->_displayStringBuf[1] = 32; + _vm->_displayStringBuf[2] = 32; + _vm->_displayStringBuf[3] = 0; + + _vm->numberToString(var1); + _vm->displayString(_vm->_displayStringBuf, pos); +} + +void LilliputScript::OC_displayVGAFile() { + debugC(1, kDebugScript, "OC_displayVGAFile()"); + + _vm->_refreshScreenFlag = true; + _vm->paletteFadeOut(); + int curWord = _currScript->readUint16LE(); + int index = _vm->_packedStringIndex[curWord]; + Common::String fileName = Common::String((const char *)&_vm->_packedStrings[index]); + _talkingCharacter = -1; + _vm->displayVGAFile(fileName); + _vm->paletteFadeIn(); +} + +void LilliputScript::OC_startSpeechWithoutSpeeker() { + debugC(1, kDebugScript, "OC_startSpeechWithoutSpeeker()"); + + int16 speechId = _currScript->readUint16LE(); + startSpeech(speechId); +} + +void LilliputScript::OC_displayTitleScreen() { + debugC(1, kDebugScript, "OC_displayTitleScreen()"); + + _vm->_keyDelay = (_currScript->readUint16LE() & 0xFF); + _vm->_int8Timer = _vm->_keyDelay; + + _vm->_keyboard_resetKeyboardBuffer(); + + _vm->_mouseButton = 0; + _vm->_lastKeyPressed = Common::Event(); + + while (!_vm->_shouldQuit) { + _vm->displaySmallAnims(); + _vm->update(); + _vm->pollEvent(); + if (_vm->_keyboard_checkKeyboard()) { + Common::Event event = _vm->_keyboard_getch(); + _vm->_lastKeyPressed = event; + if (event.type == Common::EVENT_KEYDOWN) + _vm->_keyboard_getch(); + break; + } + + if (_vm->_mouseButton == 1) + break; + + if ((_vm->_keyDelay != 0) && (_vm->_int8Timer == 0)) + break; + + _vm->_system->delayMillis(1); + } + + _vm->_mouseButton = 0; +} + +void LilliputScript::OC_initGameAreaDisplay() { + debugC(1, kDebugScript, "OC_initGameAreaDisplay()"); + + OC_PaletteFadeOut(); + _vm->_displayMap = false; + _heroismLevel = 0; + _vm->unselectInterfaceHotspots(); + + _vm->initGameAreaDisplay(); + + OC_PaletteFadeIn(); + _vm->_refreshScreenFlag = false; + + _vm->_soundHandler->update(); +} + +void LilliputScript::OC_displayCharacterStatBar() { + debugC(1, kDebugScript, "OC_displayCharacterStatBar()"); + + byte *tmpArr = getCharacterAttributesPtr(); + int8 type = (_currScript->readUint16LE() & 0xFF); + int8 score = (((70 * tmpArr[0]) / (_currScript->readUint16LE() & 0xFF)) & 0xFF); + int16 posX = _currScript->readSint16LE(); + int16 posY = _currScript->readSint16LE(); + + _vm->displayCharacterStatBar(type, posX, score, posY); +} + +void LilliputScript::OC_initSmallAnim() { + debugC(1, kDebugScript, "OC_initSmallAnim()"); + + int index = _currScript->readUint16LE(); + assert (index < 4); + _vm->_smallAnims[index]._active = true; + _vm->_smallAnims[index]._pos.x = _currScript->readSint16LE(); + _vm->_smallAnims[index]._pos.y = _currScript->readSint16LE(); + + for (int i = 0; i < 8; i++) + _vm->_smallAnims[index]._frameIndex[i] = _currScript->readUint16LE(); +} + +void LilliputScript::OC_setCharacterHeroismBar() { + debugC(1, kDebugScript, "OC_setCharacterHeroismBar()"); + + _barAttrPtr = getCharacterAttributesPtr(); + _heroismBarX = _currScript->readUint16LE(); + _heroismBarBottomY = _currScript->readUint16LE(); +} + +void LilliputScript::OC_setCharacterHome() { + debugC(1, kDebugScript, "OC_setCharacterHome()"); + + int index = getValue1(); + _vm->_characterHomePos[index] = getPosFromScript(); +} + +void LilliputScript::OC_setViewPortCharacterTarget() { + debugC(1, kDebugScript, "OC_setViewPortCharacterTarget()"); + + _viewportCharacterTarget = getValue1(); +} + +void LilliputScript::OC_showObject() { + debugC(1, kDebugScript, "OC_showObject()"); + + int frameIdx = getValue1(); + int posX = _currScript->readUint16LE(); + int posY = _currScript->readUint16LE(); + Common::Point pos = Common::Point(posX, posY); + + _vm->fill16x16Rect(16, pos); + + int frame = _vm->_characterFrameArray[frameIdx]; + byte* buf = _vm->_bufferMen; + + if (frame > 240) { + buf = _vm->_bufferMen2; + frame -= 240; + } + + _vm->display16x16IndexedBuf(buf, frame, pos); +} + +void LilliputScript::OC_playObjectSound() { + debugC(1, kDebugScript, "OC_playObjectSound()"); + int index = getValue1(); + assert(index < 40); + + Common::Point var4 = Common::Point(0xFF, index & 0xFF); + int soundId = (_currScript->readUint16LE() & 0xFF); + + _vm->_soundHandler->play(soundId, _viewportPos, _characterTilePos[index], var4); +} + +void LilliputScript::OC_startLocationSound() { + debugC(1, kDebugScript, "OC_startLocationSound()"); + + Common::Point var3 = getPosFromScript(); + Common::Point var4 = var3; + Common::Point var2 = _viewportPos; + int var1 = (_currScript->readUint16LE() & 0xFF); + + _vm->_soundHandler->play(var1, var2, var3, var4); +} + +void LilliputScript::OC_stopObjectSound() { + debugC(1, kDebugScript, "OC_stopObjectSound()"); + + Common::Point var4 = Common::Point(-1, getValue1() & 0xFF); + + _vm->_soundHandler->stop(var4); // Stop Sound +} + +void LilliputScript::OC_stopLocationSound() { + debugC(1, kDebugScript, "OC_stopLocationSound()"); + + Common::Point var4 = getPosFromScript(); + + _vm->_soundHandler->stop(var4); +} + +void LilliputScript::OC_toggleSound() { + debugC(1, kDebugScript, "OC_toggleSound()"); + + _vm->_soundHandler->toggleOnOff(); +} + +void LilliputScript::OC_playMusic() { + debugC(1, kDebugScript, "OC_playMusic()"); + + Common::Point var4 = Common::Point(-1, -1); + Common::Point var2 = _viewportPos; + int var1 = _currScript->readSint16LE() & 0xFF; + warning("OC_playMusic: unknown value for var3"); + Common::Point var3 = Common::Point(-1, -1); + + _vm->_soundHandler->play(var1, var2, var3, var4); +} + +void LilliputScript::OC_stopMusic() { + debugC(1, kDebugScript, "OC_stopMusic()"); + + _vm->_soundHandler->remove(); +} + +void LilliputScript::OC_setCharacterMapColor() { + debugC(1, kDebugScript, "OC_setCharacterMapColor()"); + + byte index = (getValue1() & 0xFF); + int color = _currScript->readUint16LE(); + + assert(index < 40); + _characterMapPixelColor[index] = (color & 0xFF); +} + +} // End of namespace diff --git a/engines/lilliput/script.h b/engines/lilliput/script.h new file mode 100644 index 0000000000..d75d24e3cf --- /dev/null +++ b/engines/lilliput/script.h @@ -0,0 +1,316 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can 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 LILLIPUT_SCRIPT_H +#define LILLIPUT_SCRIPT_H + +#include "common/memstream.h" +#include "common/stack.h" +#include "common/random.h" +#include "common/rect.h" + +#include "lilliput/stream.h" + +namespace Lilliput { + +class LilliputEngine; + +enum kActionType { + kActionNone = 0, + kButtonPressed = 1, + kButtonReleased = 2, + kActionTalk = 3, + kActionGoto = 4, + kCubeSelected = 5, + kCodeEntered = 6 +}; + +enum kValueType { + kNone, + kImmediateValue, + kCompareOperation, + kComputeOperation, + kGetValue1, + kgetPosFromScript +}; + +struct OpCode { + const char* _opName; + int _numArgs; + kValueType _arg1; + kValueType _arg2; + kValueType _arg3; + kValueType _arg4; + kValueType _arg5; +}; + +struct EvaluatedMode { + int _mode; + int _priority; +}; + +class LilliputScript { +public: + byte _heroismLevel; + byte _speechTimer; + + byte _characterScriptEnabled[40]; + int8 _interfaceHotspotStatus[20]; + Common::Point _characterTilePos[40]; + int8 _characterNextSequence[40]; + int8 _characterPose[40]; + byte _interfaceButtonActivationDelay[20]; + byte _array122C1[40]; + byte _characterSeek[40]; + int16 _interactions[40 * 40]; + + byte *_barAttrPtr; + + Common::Point _viewportPos; + int16 _viewportCharacterTarget; + int16 _talkingCharacter; + int _heroismBarX; + int _heroismBarBottomY; + + Common::Point _sequenceArr[640]; + byte _characterMapPixelColor[40]; + int8 _characterLastSequence[40]; + EvaluatedMode _newEvaluatedModes[32]; + + LilliputScript(LilliputEngine *vm); + ~LilliputScript(); + + void disasmScript(ScriptStream script); + void listAllTexts(); + static Common::String getArgumentString(kValueType type, ScriptStream& script); + void runScript(ScriptStream script); + void runMenuScript(ScriptStream script); +private: + LilliputEngine *_vm; + + ScriptStream *_currScript; + Common::Stack<ScriptStream *> _scriptStack; + + byte _byte16F05_ScriptHandler; + byte _cubeSet; + byte _lastRandomValue; + byte _scriptForVal; + byte _textVarNumber; + byte _speechDisplaySpeed; + + int16 _word16F00_characterId; + int _currentSpeechId; + int _word18821; + int _monitoredCharacter; + Common::Point _word1825E; + + char _monitoredAttr[4]; + + int handleOpcode(ScriptStream *script); + byte handleOpcodeType1(int curWord); + void handleOpcodeType2(int curWord); + + void enableCharacterScript(byte index, byte var1, byte *curBufPtr); + void skipOpcodes(int var1); + void copySequence(int index, byte *buf); + void setSequence(int charIdx, int8 seqIdx); + void checkSpeechAllowed(bool &forceReturnFl); + void decodePackedText(char *buf); + void startSpeech(int var); + void displayNumber(byte var1, Common::Point pos); + byte *getMapPtr(Common::Point val); + byte *getCurrentCharacterVarFromScript(); + void sendSignal(int16 var1, byte var2h, byte characterId, int16 var4); + void getSpeechVariant(int speechIndex, int speechVariant); + void showSpeech(); + void formatSpeechString(); + Common::Point getCharacterTilePos(int index); + int getPackedStringStartRelativeIndex(int index); + + int16 getValue1(); + Common::Point getPosFromScript(); + + byte *getCharacterAttributesPtr(); + byte compareValues(int16 var1, uint16 oper, int16 var2); + void computeOperation(byte *bufPtr, uint16 oper, int16 var2); + + //Opcodes Type 1 + byte OC_checkCharacterGoalPos(); + byte OC_comparePos(); + byte OC_checkIsoMap3(); + byte OC_compareCharacterVariable(); + byte OC_CompareLastRandomValue(); + byte OC_getRandom(); + byte OC_for(); + byte OC_compCurrentSpeechId(); + byte OC_checkSaveFlag(); + byte OC_compScriptForVal(); + byte OC_isCarrying(); + byte OC_CompareCharacterVariables(); + byte OC_compareCoords_1(); + byte OC_compareCoords_2(); + byte OC_CompareDistanceFromCharacterToPositionWith(); + byte OC_compareRandomCharacterId(); + byte OC_IsCurrentCharacterIndex(); + byte OC_hasVisibilityLevel(); + byte OC_hasGainedVisibilityLevel(); + byte OC_hasReducedVisibilityLevel(); + byte OC_isHost(); + byte OC_isSequenceActive(); + byte OC_isSequenceFinished(); + byte OC_CompareMapValueWith(); + byte OC_IsCharacterValid(); + byte OC_CheckWaitingSignal(); + byte OC_CurrentCharacterVar0AndVar1Equals(); + byte OC_CurrentCharacterVar0Equals(); + byte OC_checkLastInterfaceHotspotIndexMenu13(); + byte OC_checkLastInterfaceHotspotIndexMenu2(); + byte OC_CompareNumberOfCharacterWithVar0Equals(); + byte OC_IsPositionInViewport(); + byte OC_CompareGameVariables(); + byte OC_skipNextOpcode(); + byte OC_CheckCurrentCharacterAttr2(); + byte OC_CheckCurrentCharacterType(); + byte OC_CheckCurrentCharacterAttr0And(); + byte OC_IsCurrentCharacterAttr0LessEqualThan(); + byte OC_isCarried(); + byte OC_CheckCurrentCharacterAttr1(); + byte OC_isCurrentCharacterSpecial(); + byte OC_CurrentCharacterAttr3Equals1(); + byte OC_checkCharacterDirection(); + byte OC_checkLastInterfaceHotspotIndex(); + byte OC_checkSelectedCharacter(); + byte OC_checkDelayedReactivation(); + byte OC_checkTargetReached(); + byte OC_checkFunctionKeyPressed(); + byte OC_checkCodeEntered(); + byte OC_checkViewPortCharacterTarget(); + + // Opcodes Type 2 + void OC_setWord18821(); + void OC_ChangeIsoMap(); + void OC_startSpeech(); + void OC_getComputedVariantSpeech(); + void OC_getRotatingVariantSpeech(); + void OC_startSpeechIfMute(); + void OC_getComputedVariantSpeechIfMute(); + void OC_startSpeechIfSilent(); + void OC_ComputeCharacterVariable(); + void OC_setAttributeToRandom(); + void OC_setCharacterPosition(); + void OC_DisableCharacter(); + void OC_saveAndQuit(); + void OC_nSkipOpcodes(); + void OC_startSpeech5(); + void OC_resetHandleOpcodeFlag(); + void OC_deleteSavegameAndQuit(); + void OC_incScriptForVal(); + void OC_computeChararacterAttr(); + void OC_setTextVarNumber(); + void OC_callScript(); + void OC_callScriptAndReturn(); + void OC_setCurrentScriptCharacterPos(); + void OC_initScriptFor(); + void OC_setCurrentCharacterSequence(); + void OC_setNextCharacterSequence(); + void OC_setHost(); + void OC_changeMapCube(); + void OC_setCharacterCarry(); + void OC_dropCarried(); + void OC_setCurrentCharacter(); + void OC_sendSeeSignal(); + void OC_sendHearSignal(); + void OC_sendVarSignal(); + void OC_sendBroadcastSignal(); + void OC_resetWaitingSignal(); + void OC_enableCurrentCharacterScript(); + void OC_IncCurrentCharacterVar1(); + void OC_setCurrentCharacterPos(); + void OC_setCurrentCharacterBehavior(); + void OC_changeCurrentCharacterSprite(); + void OC_getList(); + void OC_setList(); + void OC_setCharacterDirectionTowardsPos(); + void OC_turnCharacterTowardsAnother(); + void OC_setSeek(); + void OC_scrollAwayFromCharacter(); + void OC_skipNextVal(); + void OC_setCurrentCharacterAttr6(); + void OC_setCurrentCharacterPose(); + void OC_setCharacterScriptEnabled(); + void OC_setCurrentCharacterAttr2(); + void OC_clearCurrentCharacterAttr2(); + void OC_setCharacterProperties(); + void OC_setMonitoredCharacter(); + void OC_setNewPose(); + void OC_setCurrentCharacterDirection(); + void OC_setInterfaceHotspot(); + void OC_scrollViewPort(); + void OC_setViewPortPos(); + void OC_setCurrentCharacterAltitude(); + void OC_setModePriority(); + void setMode(EvaluatedMode newMode); + void OC_setComputedModePriority(); + void OC_selectBestMode(); + void OC_magicPuffEntrance(); + void OC_spawnCharacterAtPos(); + void OC_CharacterVariableAddOrRemoveFlag(); + void OC_PaletteFadeOut(); + void OC_PaletteFadeIn(); + void OC_loadAndDisplayCubesGfx(); + void OC_setCurrentCharacterAttr3(); + void OC_setArray122C1(); + void OC_sub18367(); + void OC_enableCharacterScript(); + void OC_setRulesBuffer2Element(); + void OC_setDebugFlag(); + void OC_setDebugFlag2(); + void OC_waitForEvent(); + void OC_disableInterfaceHotspot(); + void OC_loadFileAerial(); + void OC_startSpeechIfSoundOff(); + void OC_sub1844A(); + void OC_displayNumericCharacterVariable(); + void OC_displayVGAFile(); + void OC_startSpeechWithoutSpeeker(); + void OC_displayTitleScreen(); + void OC_initGameAreaDisplay(); + void OC_displayCharacterStatBar(); + void OC_initSmallAnim(); + void OC_setCharacterHeroismBar(); + void OC_setCharacterHome(); + void OC_setViewPortCharacterTarget(); + void OC_showObject(); + void OC_playObjectSound(); + void OC_startLocationSound(); + void OC_stopObjectSound(); + void OC_stopLocationSound(); + void OC_toggleSound(); + void OC_playMusic(); + void OC_stopMusic(); + void OC_setCharacterMapColor(); +}; + +} // End of namespace Lilliput + +#endif + diff --git a/engines/lilliput/sound.cpp b/engines/lilliput/sound.cpp new file mode 100644 index 0000000000..9045e47c9a --- /dev/null +++ b/engines/lilliput/sound.cpp @@ -0,0 +1,248 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "lilliput/lilliput.h" +#include "lilliput/sound.h" + +#include "common/debug.h" + +namespace Lilliput { + +static const byte _aliasArr[40] = { + 44, 0, 1, 2, 37, 3, 24, 45, 20, 19, + 16, 10, 11, 12, 41, 39, 40, 21, 22, 23, + 4, 5, 6, 52, 7, 8, 9, 33, 13, 14, + 15, 18, 26, 25, 38, 29, 36, 0xFF, 28, 40 +}; + +static const bool _loopArr[40] = { + 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static const byte _soundType [40] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 +}; + +LilliputSound::LilliputSound(LilliputEngine *vm) : _vm(vm) { + _unpackedFiles = nullptr; + _unpackedSizes = nullptr; + _fileNumb = 0; + + _isGM = false; + + MidiPlayer::createDriver(); + + int ret = _driver->open(); + if (ret == 0) { + if (_nativeMT32) + _driver->sendMT32Reset(); + else + _driver->sendGMReset(); + + _driver->setTimerCallback(this, &timerCallback); + } +} + +LilliputSound::~LilliputSound() { + Audio::MidiPlayer::stop(); + + if (_unpackedFiles) { + for (int i = 0; i < _fileNumb; i++) + free(_unpackedFiles[i]); + } + free(_unpackedFiles); + free(_unpackedSizes); +} + +byte LilliputSound::readByte(const byte *data, uint32 offset) { + uint16 al = data[0x201 + (offset >> 1)]; + return data[1 + (offset & 1) + (al << 1)]; +} + +uint32 LilliputSound::decode(const byte *src, byte *dst, uint32 len, uint32 start) { + uint32 i = start; + for (; i < len; ++i) { + *dst++ = readByte(src, i); + } + return i; +} + +void LilliputSound::loadMusic(Common::String filename) { + debugC(1, kDebugSound, "loadMusic(%s)", filename.c_str()); + + Common::File f; + + if (!f.open(filename)) + error("Missing music file %s", filename.c_str()); + + _fileNumb = f.readUint16LE(); + + int *fileSizes = new int[_fileNumb + 1]; + for (int i = 0; i < _fileNumb; ++i) + fileSizes[i] = f.readUint16LE(); + f.seek(0, SEEK_END); + fileSizes[_fileNumb] = f.pos(); + + _unpackedFiles = new byte *[_fileNumb]; + _unpackedSizes = new uint16[_fileNumb]; + int pos = (_fileNumb + 1) * 2; // file number + file sizes + for (int i = 0; i < _fileNumb; ++i) { + int packedSize = fileSizes[i + 1] - fileSizes[i]; + byte *srcBuf = new byte[packedSize]; + f.seek(pos, SEEK_SET); + f.read(srcBuf, packedSize); + if (srcBuf[0] == 'c' || srcBuf[0] == 'C') { + int shift = (srcBuf[0] == 'c') ? 1 : 0; + _unpackedSizes[i] = (1 + packedSize - 0x201) * 2 - shift; + byte *dstBuf = new byte[_unpackedSizes[i]]; + decode(srcBuf, dstBuf, _unpackedSizes[i], shift); + _unpackedFiles[i] = dstBuf; + } else { + _unpackedSizes[i] = packedSize; + byte *dstBuf = new byte[packedSize]; + for (int j = 0; j < packedSize; ++j) + dstBuf[j] = srcBuf[j]; + _unpackedFiles[i] = dstBuf; + } + delete[] srcBuf; + pos += packedSize; + } + + delete[] fileSizes; + f.close(); + + /* Debug code + for (int i = 0; i < _fileNumb; ++i) { + Common::DumpFile dmp; + Common::String name = Common::String::format("dmp%d.mid", i); + dmp.open(name); + dmp.write(_unpackedFiles[i], _unpackedSizes[i]); + dmp.close(); + } + */ +} + +void LilliputSound::send(uint32 b) { + if (((b & 0xF0) == 0xC0) && !_isGM && !_nativeMT32) { + b = (b & 0xFFFF00FF) | MidiDriver::_mt32ToGm[(b >> 8) & 0xFF] << 8; + } + + Audio::MidiPlayer::send(b); +} + +void LilliputSound::sendToChannel(byte channel, uint32 b) { + if (!_channelsTable[channel]) { + _channelsTable[channel] = (channel == 9) ? _driver->getPercussionChannel() : _driver->allocateChannel(); + // If a new channel is allocated during the playback, make sure + // its volume is correctly initialized. + if (_channelsTable[channel]) + _channelsTable[channel]->volume(_channelsVolume[channel] * _masterVolume / 255); + } + + if (_channelsTable[channel]) + _channelsTable[channel]->send(b); +} + +// Used during initialization +void LilliputSound::init() { + debugC(1, kDebugSound, "LilliputSound::init()"); + + loadMusic("ROBIN.MUS"); +} + +void LilliputSound::refresh() { + debugC(1, kDebugSound, "LilliputSound::refresh()"); +} + +void LilliputSound::play(int var1, Common::Point var2, Common::Point var3, Common::Point var4) { + debugC(1, kDebugSound, "LilliputSound::play(%d, %d - %d, %d - %d, %d - %d)", var1, var2.x, var2.y, var3.x, var3.y, var4.x, var4.y); + // warning("LilliputSound::play(%d, %d - %d, %d - %d, %d - %d)", var1, var2.x, var2.y, var3.x, var3.y, var4.x, var4.y); + + // save camera (var2) + if (_aliasArr[var1] == 0xFF) { + return; + } + + if (var3 == Common::Point(-1, -1)) { + playMusic(var1); + } else if (_soundType[var1] == 0) { + warning("Transient"); + } else { + warning("longterm"); + } + + return; +} +void LilliputSound::playMusic(int var1) { + int idx = _aliasArr[var1]; + bool loop = _loopArr[var1]; + + _isGM = true; + + if (_parser) + _parser->stopPlaying(); + + MidiParser *parser = MidiParser::createParser_SMF(); + if (parser->loadMusic(_unpackedFiles[idx], _unpackedSizes[idx])) { + parser->setTrack(0); + parser->setMidiDriver(this); + parser->setTimerRate(_driver->getBaseTempo()); + parser->property(MidiParser::mpAutoLoop, loop); + parser->property(MidiParser::mpCenterPitchWheelOnUnload, 1); + + _parser = parser; + + syncVolume(); + + _isLooping = loop; + _isPlaying = true; + } +} + +void LilliputSound::stop(Common::Point pos) { + debugC(1, kDebugSound, "LilliputSound::stop(%d - %d)", pos.x, pos.y); + warning("LilliputSound::stop(%d - %d)", pos.x, pos.y); +} + +void LilliputSound::toggleOnOff() { + debugC(1, kDebugSound, "LilliputSound::toggleOnOff()"); + warning("LilliputSound::toggleOnOff()"); +} + +void LilliputSound::update() { + debugC(1, kDebugSound, "LilliputSound::update()"); + warning("LilliputSound::update()"); +} + +void LilliputSound::remove() { + debugC(1, kDebugSound, "Lilliput::remove()"); + + _parser->stopPlaying(); +} + +} // End of namespace diff --git a/engines/lilliput/sound.h b/engines/lilliput/sound.h new file mode 100644 index 0000000000..f56fd58a6f --- /dev/null +++ b/engines/lilliput/sound.h @@ -0,0 +1,71 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef LILLIPUT_SOUND_H +#define LILLIPUT_SOUND_H + +#include "audio/audiostream.h" +#include "audio/decoders/wave.h" +#include "audio/mididrv.h" +#include "audio/midiparser.h" +#include "audio/midiplayer.h" +#include "audio/mixer.h" + +namespace Lilliput { + +class LilliputEngine; + +class LilliputSound: public Audio::MidiPlayer { +public: + LilliputSound(LilliputEngine *vm); + ~LilliputSound(); + + void init(); + void refresh(); + void play(int var1, Common::Point var2, Common::Point var3, Common::Point var4); + void stop(Common::Point pos); + void toggleOnOff(); + void update(); + void remove(); + +private: + LilliputEngine *_vm; + + int _fileNumb; + byte **_unpackedFiles; + uint16 *_unpackedSizes; + bool _isGM; + + uint32 decode(const byte *src, byte *dst, uint32 len, uint32 start); + byte readByte(const byte *data, uint32 offset); + + void loadMusic(Common::String filename); + void playMusic(int var1); + + virtual void send(uint32 b); + virtual void sendToChannel(byte channel, uint32 b); +}; + +} // End of namespace Lilliput + +#endif + diff --git a/engines/lilliput/stream.cpp b/engines/lilliput/stream.cpp new file mode 100644 index 0000000000..6b33c9d357 --- /dev/null +++ b/engines/lilliput/stream.cpp @@ -0,0 +1,42 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can 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 "lilliput/stream.h" + +namespace Lilliput { + +ScriptStream::ScriptStream(byte *buf, int bufSize) : Common::MemoryReadStream(buf, bufSize) { + _orgPtr = buf; +} + +ScriptStream::~ScriptStream() { +} + +void ScriptStream::writeUint16LE(int value, int relativePos) { + int writePos = pos() + relativePos; + assert((writePos >= 0) && (writePos + 2 < size())); + + Common::MemoryWriteStream tmpStream = Common::MemoryWriteStream(_orgPtr + writePos, size() - writePos); + tmpStream.writeUint16LE(value); +} + +} // End of namespace Lilliput diff --git a/engines/lilliput/stream.h b/engines/lilliput/stream.h new file mode 100644 index 0000000000..52b80e3524 --- /dev/null +++ b/engines/lilliput/stream.h @@ -0,0 +1,42 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can 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 LILLIPUT_STREAM_H +#define LILLIPUT_STREAM_H + +#include "common/memstream.h" + +namespace Lilliput { + +class ScriptStream : public Common::MemoryReadStream { +private: + byte *_orgPtr; +public: + ScriptStream(byte *buf, int bufSize); + virtual ~ScriptStream(); + + void writeUint16LE(int value, int relativePos = 0); +}; + +} // End of namespace Lilliput + +#endif diff --git a/engines/lure/sound.cpp b/engines/lure/sound.cpp index 25d68c762d..5acd6d81a0 100644 --- a/engines/lure/sound.cpp +++ b/engines/lure/sound.cpp @@ -649,10 +649,7 @@ MidiMusic::~MidiMusic() { } void MidiMusic::setVolume(int volume) { - if (volume < 0) - volume = 0; - else if (volume > 255) - volume = 255; + volume = CLIP(volume, 0, 255); if (_volume == volume) return; diff --git a/engines/macventure/detection.cpp b/engines/macventure/detection.cpp index ba583ef743..5eda420cb2 100644 --- a/engines/macventure/detection.cpp +++ b/engines/macventure/detection.cpp @@ -54,7 +54,7 @@ static const PlainGameDescriptor macventureGames[] = { namespace MacVenture { -SaveStateDescriptor loadMetaData(Common::SeekableReadStream *s, int slot); +SaveStateDescriptor loadMetaData(Common::SeekableReadStream *s, int slot, bool skipThumbnail = true); class MacVentureMetaEngine : public AdvancedMetaEngine { public: @@ -63,18 +63,19 @@ public: _md5Bytes = 5000000; // TODO: Upper limit, adjust it once all games are added } - virtual const char *getName() const override { + const char *getName() const { return "MacVenture"; } - virtual const char *getOriginalCopyright() const override { + const char *getOriginalCopyright() const { return "(C) ICOM Simulations"; } - virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const; - virtual bool hasFeature(MetaEngineFeature f) const; - virtual SaveStateList listSaves(const char *target) const; - virtual int getMaximumSaveSlot() const; - virtual void removeSaveState(const char *target, int slot) const; +protected: + bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const; + bool hasFeature(MetaEngineFeature f) const; + SaveStateList listSaves(const char *target) const; + int getMaximumSaveSlot() const; + void removeSaveState(const char *target, int slot) const; SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const; }; @@ -163,7 +164,7 @@ SaveStateDescriptor MacVentureMetaEngine::querySaveMetaInfos(const char *target, Common::InSaveFile *in = saveFileMan->openForLoading(saveFileName); if (in) { - desc = loadMetaData(in, slot); + desc = loadMetaData(in, slot, false); delete in; return desc; } diff --git a/engines/macventure/saveload.cpp b/engines/macventure/saveload.cpp index 89a6301318..c63b6a6951 100644 --- a/engines/macventure/saveload.cpp +++ b/engines/macventure/saveload.cpp @@ -42,7 +42,7 @@ namespace MacVenture { #define MACVENTURE_SAVE_VERSION 1 //1 BYTE #define MACVENTURE_DESC_LENGTH 4 //4 BYTE for the metadata length -SaveStateDescriptor loadMetaData(Common::SeekableReadStream *s, int slot) { +SaveStateDescriptor loadMetaData(Common::SeekableReadStream *s, int slot, bool skipThumbnail) { // Metadata is stored at the end of the file // |THUMBNAIL | // | | @@ -65,8 +65,11 @@ SaveStateDescriptor loadMetaData(Common::SeekableReadStream *s, int slot) { s->seek(-(5 + MACVENTURE_DESC_LENGTH + metaSize), SEEK_END); // Load the thumbnail - Graphics::Surface *thumb = Graphics::loadThumbnail(*s); - desc.setThumbnail(thumb); + Graphics::Surface *thumbnail; + if (!Graphics::loadThumbnail(*s, thumbnail, skipThumbnail)) { + return desc; + } + desc.setThumbnail(thumbnail); // Load the description Common::String name; diff --git a/engines/made/detection.cpp b/engines/made/detection.cpp index 636c2d147c..bf05385c88 100644 --- a/engines/made/detection.cpp +++ b/engines/made/detection.cpp @@ -535,7 +535,7 @@ public: virtual bool hasFeature(MetaEngineFeature f) const; virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const; - const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const; + ADDetectedGame fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const override; }; @@ -557,7 +557,7 @@ bool MadeMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGame return gd != 0; } -const ADGameDescription *MadeMetaEngine::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const { +ADDetectedGame MadeMetaEngine::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const { // Set the default values for the fallback descriptor's ADGameDescription part. Made::g_fallbackDesc.desc.language = Common::UNK_LANG; Made::g_fallbackDesc.desc.platform = Common::kPlatformDOS; @@ -569,7 +569,7 @@ const ADGameDescription *MadeMetaEngine::fallbackDetect(const FileMap &allFiles, Made::g_fallbackDesc.version = 3; //return (const ADGameDescription *)&Made::g_fallbackDesc; - return NULL; + return ADDetectedGame(); } #if PLUGIN_ENABLED_DYNAMIC(MADE) diff --git a/engines/made/resource.cpp b/engines/made/resource.cpp index a9734ed47d..d8ceb87bb6 100644 --- a/engines/made/resource.cpp +++ b/engines/made/resource.cpp @@ -400,7 +400,8 @@ ResourceReader::~ResourceReader() { // V2 void ResourceReader::open(const char *filename) { _fd = new Common::File(); - _fd->open(filename); + if (!_fd->open(filename)) + error("ResourceReader::open() Could not open '%s'", filename); _fd->skip(0x18); // skip header for now diff --git a/engines/mads/detection.cpp b/engines/mads/detection.cpp index 4fb8b82eb3..8eb3b4eee9 100644 --- a/engines/mads/detection.cpp +++ b/engines/mads/detection.cpp @@ -203,11 +203,8 @@ SaveStateList MADSMetaEngine::listSaves(const char *target) const { Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(*file); if (in) { - MADS::Game::readSavegameHeader(in, header); - saveList.push_back(SaveStateDescriptor(slot, header._saveName)); - - header._thumbnail->free(); - delete header._thumbnail; + if (MADS::Game::readSavegameHeader(in, header)) + saveList.push_back(SaveStateDescriptor(slot, header._saveName)); delete in; } } @@ -233,7 +230,10 @@ SaveStateDescriptor MADSMetaEngine::querySaveMetaInfos(const char *target, int s if (f) { MADS::MADSSavegameHeader header; - MADS::Game::readSavegameHeader(f, header); + if (!MADS::Game::readSavegameHeader(f, header, false)) { + delete f; + return SaveStateDescriptor(); + } delete f; // Create the return descriptor diff --git a/engines/mads/game.cpp b/engines/mads/game.cpp index 0a6741ba7a..bea0ea3bb4 100644 --- a/engines/mads/game.cpp +++ b/engines/mads/game.cpp @@ -485,11 +485,6 @@ void Game::loadGame(int slotNumber) { if (!readSavegameHeader(_saveFile, header)) error("Invalid savegame"); - if (header._thumbnail) { - header._thumbnail->free(); - delete header._thumbnail; - } - // Load most of the savegame data with the exception of scene specific info synchronize(s, true); @@ -527,9 +522,8 @@ void Game::saveGame(int slotNumber, const Common::String &saveName) { const char *const SAVEGAME_STR = "MADS"; #define SAVEGAME_STR_SIZE 4 -bool Game::readSavegameHeader(Common::InSaveFile *in, MADSSavegameHeader &header) { +WARN_UNUSED_RESULT bool Game::readSavegameHeader(Common::InSaveFile *in, MADSSavegameHeader &header, bool skipThumbnail) { char saveIdentBuffer[SAVEGAME_STR_SIZE + 1]; - header._thumbnail = nullptr; // Validate the header Id in->read(saveIdentBuffer, SAVEGAME_STR_SIZE + 1); @@ -546,9 +540,9 @@ bool Game::readSavegameHeader(Common::InSaveFile *in, MADSSavegameHeader &header while ((ch = (char)in->readByte()) != '\0') header._saveName += ch; // Get the thumbnail - header._thumbnail = Graphics::loadThumbnail(*in); - if (!header._thumbnail) + if (!Graphics::loadThumbnail(*in, header._thumbnail, skipThumbnail)) { return false; + } // Read in save date/time header._year = in->readSint16LE(); diff --git a/engines/mads/game.h b/engines/mads/game.h index 9defb58b1a..b979160f3d 100644 --- a/engines/mads/game.h +++ b/engines/mads/game.h @@ -237,7 +237,7 @@ public: /** * Read in a savegame header */ - static bool readSavegameHeader(Common::InSaveFile *in, MADSSavegameHeader &header); + WARN_UNUSED_RESULT static bool readSavegameHeader(Common::InSaveFile *in, MADSSavegameHeader &header, bool skipThumbnail = true); /** * Creates a temporary thumbnail for use in saving games diff --git a/engines/mads/nebular/game_nebular.cpp b/engines/mads/nebular/game_nebular.cpp index 1db5eaea00..99402748b8 100644 --- a/engines/mads/nebular/game_nebular.cpp +++ b/engines/mads/nebular/game_nebular.cpp @@ -45,8 +45,9 @@ GameNebular::GameNebular(MADSEngine *vm) } ProtectionResult GameNebular::checkCopyProtection() { - //if (!ConfMan.getBool("copy_protection")) - // return PROTECTION_SUCCEED; + // Only show copy protection dialog if explicitly wanted + if (!ConfMan.getBool("copy_protection")) + return PROTECTION_SUCCEED; CopyProtectionDialog *dlg; bool correctAnswer; diff --git a/engines/mads/nebular/nebular_scenes4.cpp b/engines/mads/nebular/nebular_scenes4.cpp index a4c6a3ebe1..8b7eb1a9ec 100644 --- a/engines/mads/nebular/nebular_scenes4.cpp +++ b/engines/mads/nebular/nebular_scenes4.cpp @@ -2443,7 +2443,7 @@ void Scene405::step() { } if (_game._trigger == 70) { - _game._player._priorTimer = _scene->_frameStartTime + _game._player._ticksAmount ; + _game._player._priorTimer = _scene->_frameStartTime + _game._player._ticksAmount; _game._player._visible = true; _globals._sequenceIndexes[1] = _scene->_sequences.addReverseSpriteCycle(_globals._spriteIndexes[1], false, 6, 1, 0, 0); _scene->_sequences.addSubEntry(_globals._sequenceIndexes[1], SEQUENCE_TRIGGER_EXPIRE, 0, 71); @@ -2458,7 +2458,7 @@ void Scene405::step() { } if (_game._trigger == 75) { - _game._player._priorTimer = _scene->_frameStartTime + _game._player._ticksAmount ; + _game._player._priorTimer = _scene->_frameStartTime + _game._player._ticksAmount; _game._player._visible = true; _scene->_sequences.remove(_globals._sequenceIndexes[1]); _globals._sequenceIndexes[1] = _scene->_sequences.addSpriteCycle(_globals._spriteIndexes[1], false, 6, 1, 0, 0); diff --git a/engines/metaengine.h b/engines/metaengine.h index b3aaa96a8f..a95ff1593e 100644 --- a/engines/metaengine.h +++ b/engines/metaengine.h @@ -69,17 +69,17 @@ public: virtual const char *getOriginalCopyright() const = 0; /** Returns a list of games supported by this engine. */ - virtual GameList getSupportedGames() const = 0; + virtual PlainGameList getSupportedGames() const = 0; - /** Query the engine for a GameDescriptor for the specified gameid, if any. */ - virtual GameDescriptor findGame(const char *gameid) const = 0; + /** Query the engine for a PlainGameDescriptor for the specified gameid, if any. */ + virtual PlainGameDescriptor findGame(const char *gameId) const = 0; /** * Runs the engine's game detector on the given list of files, and returns a * (possibly empty) list of games supported by the engine which it was able * to detect amongst the given files. */ - virtual GameList detectGames(const Common::FSList &fslist) const = 0; + virtual DetectedGames detectGames(const Common::FSList &fslist) const = 0; /** * Tries to instantiate an engine instance based on the settings of @@ -267,10 +267,17 @@ public: */ class EngineManager : public Common::Singleton<EngineManager> { public: - GameDescriptor findGameInLoadedPlugins(const Common::String &gameName, const Plugin **plugin = NULL) const; - GameDescriptor findGame(const Common::String &gameName, const Plugin **plugin = NULL) const; - GameList detectGames(const Common::FSList &fslist) const; + PlainGameDescriptor findGameInLoadedPlugins(const Common::String &gameName, const Plugin **plugin = NULL) const; + PlainGameDescriptor findGame(const Common::String &gameName, const Plugin **plugin = NULL) const; + DetectionResults detectGames(const Common::FSList &fslist) const; const PluginList &getPlugins() const; + + /** + * Create a target from the supplied game descriptor + * + * Returns the created target name. + */ + Common::String createTargetForGame(const DetectedGame &game); }; /** Convenience shortcut for accessing the engine manager. */ diff --git a/engines/mohawk/bitmap.cpp b/engines/mohawk/bitmap.cpp index d8c6d6aacd..205feb824f 100644 --- a/engines/mohawk/bitmap.cpp +++ b/engines/mohawk/bitmap.cpp @@ -70,7 +70,7 @@ MohawkBitmap::~MohawkBitmap() { void MohawkBitmap::decodeImageData(Common::SeekableReadStream *stream) { _data = stream; - _header.colorTable.palette = NULL; + _header.colorTable.palette = nullptr; // NOTE: Only the bottom 12 bits of width/height/bytesPerRow are // considered valid and bytesPerRow has to be an even number. @@ -650,7 +650,7 @@ MohawkSurface *MystBitmap::decodeImage(Common::SeekableReadStream *stream) { error("Could not decode Myst bitmap"); const Graphics::Surface *bmpSurface = bitmapDecoder.getSurface(); - Graphics::Surface *newSurface = 0; + Graphics::Surface *newSurface = nullptr; if (bmpSurface->format.bytesPerPixel == 1) { _bitsPerPixel = 8; @@ -662,7 +662,7 @@ MohawkSurface *MystBitmap::decodeImage(Common::SeekableReadStream *stream) { } // Copy the palette to one of our own - byte *newPal = 0; + byte *newPal = nullptr; if (bitmapDecoder.hasPalette()) { const byte *palette = bitmapDecoder.getPalette(); @@ -729,7 +729,7 @@ MohawkSurface *LivingBooksBitmap_v1::decodeImage(Common::SeekableReadStream *str leRLE8 = true; _data = stream; - stream = NULL; + stream = nullptr; } Graphics::Surface *surface = createSurface(_header.width, _header.height); diff --git a/engines/mohawk/bitmap.h b/engines/mohawk/bitmap.h index ea8664f39d..18ea72b3ee 100644 --- a/engines/mohawk/bitmap.h +++ b/engines/mohawk/bitmap.h @@ -148,16 +148,16 @@ private: // Mohawk Bitmap format. class MystBitmap : public MohawkBitmap { public: - MystBitmap() : MohawkBitmap() {} - ~MystBitmap() {} + MystBitmap() : MohawkBitmap(), _bitsPerPixel(8) {} + ~MystBitmap() override {} - MohawkSurface *decodeImage(Common::SeekableReadStream *stream); + MohawkSurface *decodeImage(Common::SeekableReadStream *stream) override; protected: - byte getBitsPerPixel() { return _bitsPerPixel; } + byte getBitsPerPixel() override { return _bitsPerPixel; } private: - uint16 _bitsPerPixel; + byte _bitsPerPixel; }; #endif @@ -165,23 +165,23 @@ private: class LivingBooksBitmap_v1 : public MohawkBitmap { public: LivingBooksBitmap_v1() : MohawkBitmap() {} - ~LivingBooksBitmap_v1() {} + ~LivingBooksBitmap_v1() override {} - MohawkSurface *decodeImage(Common::SeekableReadStream *stream); + MohawkSurface *decodeImage(Common::SeekableReadStream *stream) override; protected: - byte getBitsPerPixel() { return 8; } + byte getBitsPerPixel() override { return 8; } }; class DOSBitmap : public MohawkBitmap { public: DOSBitmap() : MohawkBitmap() {} - ~DOSBitmap() {} + ~DOSBitmap() override {} - MohawkSurface *decodeImage(Common::SeekableReadStream *stream); + MohawkSurface *decodeImage(Common::SeekableReadStream *stream) override; protected: - byte getBitsPerPixel() { return ((_header.format & 0x30) >> 4) + 1; } + byte getBitsPerPixel() override { return ((_header.format & 0x30) >> 4) + 1; } private: void expandMonochromePlane(Graphics::Surface *surface, Common::SeekableReadStream *rawStream); diff --git a/engines/mohawk/console.h b/engines/mohawk/console.h index 0cae87da51..7d94bf576f 100644 --- a/engines/mohawk/console.h +++ b/engines/mohawk/console.h @@ -35,8 +35,8 @@ class MohawkEngine_Myst; class MystConsole : public GUI::Debugger { public: - MystConsole(MohawkEngine_Myst *vm); - virtual ~MystConsole(void); + explicit MystConsole(MohawkEngine_Myst *vm); + ~MystConsole() override; private: MohawkEngine_Myst *_vm; @@ -66,8 +66,8 @@ class MohawkEngine_Riven; class RivenConsole : public GUI::Debugger { public: - RivenConsole(MohawkEngine_Riven *vm); - virtual ~RivenConsole(void); + explicit RivenConsole(MohawkEngine_Riven *vm); + ~RivenConsole() override; private: MohawkEngine_Riven *_vm; @@ -95,8 +95,8 @@ private: class LivingBooksConsole : public GUI::Debugger { public: - LivingBooksConsole(MohawkEngine_LivingBooks *vm); - virtual ~LivingBooksConsole(void); + explicit LivingBooksConsole(MohawkEngine_LivingBooks *vm); + ~LivingBooksConsole() override; private: MohawkEngine_LivingBooks *_vm; diff --git a/engines/mohawk/cstime.h b/engines/mohawk/cstime.h index 1c39a86ca0..9edd185085 100644 --- a/engines/mohawk/cstime.h +++ b/engines/mohawk/cstime.h @@ -129,7 +129,7 @@ enum CSTimeState { class MohawkEngine_CSTime : public MohawkEngine { protected: - Common::Error run(); + Common::Error run() override; public: MohawkEngine_CSTime(OSystem *syst, const MohawkGameDescription *gamedesc); @@ -142,7 +142,7 @@ public: CSTimeGraphics *_gfx; bool _needsUpdate; - GUI::Debugger *getDebugger() { return _console; } + GUI::Debugger *getDebugger() override { return _console; } CSTimeView *getView() { return _view; } CSTimeCase *getCase() { return _case; } CSTimeInterface *getInterface() { return _interface; } diff --git a/engines/mohawk/cursors.cpp b/engines/mohawk/cursors.cpp index cef24e14e5..84b1c73e04 100644 --- a/engines/mohawk/cursors.cpp +++ b/engines/mohawk/cursors.cpp @@ -151,7 +151,7 @@ NECursorManager::NECursorManager(const Common::String &appName) { if (!_exe->loadFromEXE(appName)) { // Not all have cursors anyway, so this is not a problem delete _exe; - _exe = 0; + _exe = nullptr; } } @@ -183,10 +183,10 @@ MacCursorManager::MacCursorManager(const Common::String &appName) { if (!_resFork->open(appName)) { // Not all have cursors anyway, so this is not a problem delete _resFork; - _resFork = 0; + _resFork = nullptr; } } else { - _resFork = 0; + _resFork = nullptr; } } @@ -219,7 +219,7 @@ LivingBooksCursorManager_v2::LivingBooksCursorManager_v2() { if (!_sysArchive->openFile("system.mhk")) { delete _sysArchive; - _sysArchive = 0; + _sysArchive = nullptr; } } diff --git a/engines/mohawk/cursors.h b/engines/mohawk/cursors.h index d0d38c9b46..ff5db5b59c 100644 --- a/engines/mohawk/cursors.h +++ b/engines/mohawk/cursors.h @@ -72,11 +72,11 @@ protected: // Uses standard tCUR resources class DefaultCursorManager : public CursorManager { public: - DefaultCursorManager(MohawkEngine *vm, uint32 tag = ID_TCUR) : _vm(vm), _tag(tag) {} - ~DefaultCursorManager() {} + explicit DefaultCursorManager(MohawkEngine *vm, uint32 tag = ID_TCUR) : _vm(vm), _tag(tag) {} + ~DefaultCursorManager() override {} - void setCursor(uint16 id); - bool hasSource() const { return true; } + void setCursor(uint16 id) override; + bool hasSource() const override { return true; } private: MohawkEngine *_vm; @@ -110,14 +110,14 @@ class MohawkEngine_Myst; // Uses WDIB + CLRC resources class MystCursorManager : public CursorManager { public: - MystCursorManager(MohawkEngine_Myst *vm); - ~MystCursorManager(); + explicit MystCursorManager(MohawkEngine_Myst *vm); + ~MystCursorManager() override; - void showCursor(); - void hideCursor(); - void setCursor(uint16 id); - void setDefaultCursor(); - bool hasSource() const { return true; } + void showCursor() override; + void hideCursor() override; + void setCursor(uint16 id) override; + void setDefaultCursor() override; + bool hasSource() const override { return true; } private: MohawkEngine_Myst *_vm; @@ -128,11 +128,11 @@ private: // The cursor manager for NE EXE's class NECursorManager : public CursorManager { public: - NECursorManager(const Common::String &appName); - ~NECursorManager(); + explicit NECursorManager(const Common::String &appName); + ~NECursorManager() override; - void setCursor(uint16 id); - bool hasSource() const { return _exe != 0; } + void setCursor(uint16 id) override; + bool hasSource() const override { return _exe != nullptr; } private: Common::NEResources *_exe; @@ -141,11 +141,11 @@ private: // The cursor manager for Mac applications class MacCursorManager : public CursorManager { public: - MacCursorManager(const Common::String &appName); - ~MacCursorManager(); + explicit MacCursorManager(const Common::String &appName); + ~MacCursorManager() override; - void setCursor(uint16 id); - bool hasSource() const { return _resFork != 0; } + void setCursor(uint16 id) override; + bool hasSource() const override { return _resFork != nullptr; } private: Common::MacResManager *_resFork; @@ -156,11 +156,11 @@ private: class LivingBooksCursorManager_v2 : public CursorManager { public: LivingBooksCursorManager_v2(); - ~LivingBooksCursorManager_v2(); + ~LivingBooksCursorManager_v2() override; - void setCursor(uint16 id); - void setCursor(const Common::String &name); - bool hasSource() const { return _sysArchive != 0; } + void setCursor(uint16 id) override; + void setCursor(const Common::String &name) override; + bool hasSource() const override { return _sysArchive != nullptr; } private: MohawkArchive *_sysArchive; @@ -169,11 +169,11 @@ private: // The cursor manager for PE EXE's class PECursorManager : public CursorManager { public: - PECursorManager(const Common::String &appName); - ~PECursorManager(); + explicit PECursorManager(const Common::String &appName); + ~PECursorManager() override; - void setCursor(uint16 id); - bool hasSource() const { return !_cursors.empty(); } + void setCursor(uint16 id) override; + bool hasSource() const override { return !_cursors.empty(); } private: struct CursorItem { diff --git a/engines/mohawk/detection.cpp b/engines/mohawk/detection.cpp index 439ea152c4..58d1483bee 100644 --- a/engines/mohawk/detection.cpp +++ b/engines/mohawk/detection.cpp @@ -116,19 +116,12 @@ static const PlainGameDescriptor mohawkGames[] = { {"myst", "Myst"}, {"makingofmyst", "The Making of Myst"}, {"riven", "Riven: The Sequel to Myst"}, - {"zoombini", "Logical Journey of the Zoombinis"}, {"cstime", "Where in Time is Carmen Sandiego?"}, - {"csworld", "Where in the World is Carmen Sandiego?"}, - {"csamtrak", "Where in America is Carmen Sandiego? (The Great Amtrak Train Adventure)"}, {"carmentq", "Carmen Sandiego's ThinkQuick Challenge"}, {"carmentqc", "Carmen Sandiego's ThinkQuick Challenge Custom Question Creator"}, {"maggiesfa", "Maggie's Farmyard Adventure"}, - {"jamesmath", "James Discovers/Explores Math"}, - {"treehouse", "The Treehouse"}, {"greeneggs", "Green Eggs and Ham"}, {"seussabc", "Dr Seuss's ABC"}, - {"1stdegree", "In the 1st Degree"}, - {"csusa", "Where in the USA is Carmen Sandiego?"}, {"tortoise", "Aesop's Fables: The Tortoise and the Hare"}, {"arthur", "Arthur's Teacher Trouble"}, {"grandma", "Just Grandma and Me"}, @@ -147,7 +140,7 @@ static const PlainGameDescriptor mohawkGames[] = { {"stellaluna", "Stellaluna"}, {"sheila", "Sheila Rae, the Brave"}, {"rugratsps", "Rugrats Print Shop" }, - {0, 0} + {nullptr, nullptr} }; #include "mohawk/detection_tables.h" @@ -159,7 +152,7 @@ static const char *directoryGlobs[] = { "program", "95instal", "Rugrats Adventure Game", - 0 + nullptr }; static const ADExtraGuiOptionsMap optionsList[] = { @@ -184,25 +177,25 @@ public: _directoryGlobs = directoryGlobs; } - virtual const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const { + ADDetectedGame fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const override { return detectGameFilebased(allFiles, fslist, Mohawk::fileBased); } - virtual const char *getName() const { + const char *getName() const override { return "Mohawk"; } - virtual const char *getOriginalCopyright() const { + const char *getOriginalCopyright() const override { return "Myst and Riven (C) Cyan Worlds\nMohawk OS (C) Ubisoft"; } - virtual bool hasFeature(MetaEngineFeature f) const; - virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const; - virtual SaveStateList listSaves(const char *target) const; + bool hasFeature(MetaEngineFeature f) const override; + bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const override; + SaveStateList listSaves(const char *target) const override; SaveStateList listSavesForPrefix(const char *prefix, const char *extension) const; - virtual int getMaximumSaveSlot() const { return 999; } - virtual void removeSaveState(const char *target, int slot) const; - virtual SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const; + int getMaximumSaveSlot() const override { return 999; } + void removeSaveState(const char *target, int slot) const override; + SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const override; }; bool MohawkMetaEngine::hasFeature(MetaEngineFeature f) const { @@ -340,21 +333,12 @@ bool MohawkMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGa warning("CSTime support not compiled in"); return false; #endif - case Mohawk::GType_ZOOMBINI: - case Mohawk::GType_CSWORLD: - case Mohawk::GType_CSAMTRAK: - case Mohawk::GType_JAMESMATH: - case Mohawk::GType_TREEHOUSE: - case Mohawk::GType_1STDEGREE: - case Mohawk::GType_CSUSA: - warning("Unsupported Mohawk Engine"); - return false; default: error("Unknown Mohawk Engine"); } } - return (gd != 0); + return (gd != nullptr); } #if PLUGIN_ENABLED_DYNAMIC(MOHAWK) diff --git a/engines/mohawk/detection_tables.h b/engines/mohawk/detection_tables.h index a06b814fbd..e887c11051 100644 --- a/engines/mohawk/detection_tables.h +++ b/engines/mohawk/detection_tables.h @@ -500,6 +500,24 @@ static const MohawkGameDescription gameDescriptions[] = { }, // Riven: The Sequel to Myst + // Version 1.1 (DVD, Pressing code rvd 2811 ab, RVD8AB-BI RVD2811AB) + // From wouwehand in #10519 + { + { + "riven", + "DVD", + AD_ENTRY1("a_Data.MHK", "3370cd9a9696814365a2b7fd7a7b726e"), + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_NO_FLAGS, + GUI_OPTIONS_RIVEN + }, + GType_RIVEN, + GF_DVD, + 0, + }, + + // Riven: The Sequel to Myst // Version 1.0 (DVD, From "Myst: Die Trilogie") // From DrMcCoy { @@ -600,127 +618,6 @@ static const MohawkGameDescription gameDescriptions[] = { 0 }, - - { - { - "zoombini", - "", - AD_ENTRY1("ZOOMBINI.MHK", "98b758fec55104c096cfd129048be9a6"), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO1(GUIO_NOASPECT) - }, - GType_ZOOMBINI, - GF_HASMIDI, - 0 - }, - - { - { - "zoombini", - "", - AD_ENTRY1("ZOOMBINI.MHK", "0672f65c40dd065840c896e41c13f980"), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO1(GUIO_NOASPECT) - }, - GType_ZOOMBINI, - GF_HASMIDI, - 0 - }, - - { - { - "zoombini", - "v2.0", - AD_ENTRY1("ZOOMBINI.MHK", "506b1122ffa740e2566cf0b583d24478"), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_NO_FLAGS, - GUIO1(GUIO_NOASPECT) - }, - GType_ZOOMBINI, - GF_HASMIDI, - 0 - }, - - { - { - "zoombini", - "", - AD_ENTRY1("ZOOMBINI.MHK", "6ae0bdf791266b1fe3d4fabbf44c3faa"), - Common::DE_DEU, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO1(GUIO_NOASPECT) - }, - GType_ZOOMBINI, - GF_HASMIDI, - 0 - }, - - { - { - "zoombini", - "", - AD_ENTRY1("ZOOMBINI.MHK", "8231e58525143ccf6e8b747df34b139f"), - Common::FR_FRA, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO1(GUIO_NOASPECT) - }, - GType_ZOOMBINI, - GF_HASMIDI, - 0 - }, - - { - { - "csworld", - "v3.0", - AD_ENTRY1("C2K.MHK", "605fe88380848031bbd0ff84ade6fe40"), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO1(GUIO_NOASPECT) - }, - GType_CSWORLD, - 0, - 0 - }, - - { - { - "csworld", - "v3.5", - AD_ENTRY1("C2K.MHK", "d4857aeb0f5e2e0c4ac556aa74f38c23"), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO1(GUIO_NOASPECT) - }, - GType_CSWORLD, - 0, - 0 - }, - - { - { - "csamtrak", - "", - AD_ENTRY1("AMTRAK.MHK", "2f95301f0bb950d555bb7b0e3b1b7eb1"), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO1(GUIO_NOASPECT) - }, - GType_CSAMTRAK, - 0, - 0 - }, - // Harry and the Haunted House v1.0E // English Windows 3.11 // From strangerke @@ -935,52 +832,6 @@ static const MohawkGameDescription gameDescriptions[] = { { { - "jamesmath", - "", - AD_ENTRY1("BRODER.MHK", "007299da8b2c6e8ec1cde9598c243024"), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO1(GUIO_NOASPECT) - }, - GType_JAMESMATH, - GF_HASMIDI, - 0 - }, - - // This is in the NEWDATA folder, so I assume it's a newer version ;) - { - { - "jamesmath", - "", - AD_ENTRY1("BRODER.MHK", "53c000938a50dca92860fd9b546dd276"), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO1(GUIO_NOASPECT) - }, - GType_JAMESMATH, - GF_HASMIDI, - 0 - }, - - { - { - "treehouse", - "", - AD_ENTRY1("MAINROOM.MHK", "12f51894d7f838af639ea9bf1bc8f45b"), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO1(GUIO_NOASPECT) - }, - GType_TREEHOUSE, - GF_HASMIDI, - 0 - }, - - { - { "greeneggs", "", AD_ENTRY1("GREEN.LB", "5df8438138186f89e71299d7b4f88d06"), @@ -1088,54 +939,6 @@ static const MohawkGameDescription gameDescriptions[] = { { { - "1stdegree", - "", - AD_ENTRY1("AL236_1.MHK", "3ba145492a7b8b4dee0ef4222c5639c3"), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO1(GUIO_NOASPECT) - }, - GType_1STDEGREE, - GF_HASMIDI, - 0 - }, - - // In The 1st Degree - // French Windows - // From Strangerke - { - { - "1stdegree", - "", - AD_ENTRY1("AL236_1.MHK", "0e0c70b1b702b6ddca61a1192ada1282"), - Common::FR_FRA, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO1(GUIO_NOASPECT) - }, - GType_1STDEGREE, - GF_HASMIDI, - 0 - }, - - { - { - "csusa", - "", - AD_ENTRY1("USAC2K.MHK", "b8c9d3a2586f62bce3a48b50d7a700e9"), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO1(GUIO_NOASPECT) - }, - GType_CSUSA, - 0, - 0 - }, - - { - { "tortoise", "", AD_ENTRY1("TORTOISE.512", "dfcf7bff3d0f187832c9897497efde0e"), diff --git a/engines/mohawk/dialogs.cpp b/engines/mohawk/dialogs.cpp index 029867f6a6..5700a4641b 100644 --- a/engines/mohawk/dialogs.cpp +++ b/engines/mohawk/dialogs.cpp @@ -180,22 +180,22 @@ void MohawkOptionsDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, MystOptionsDialog::MystOptionsDialog(MohawkEngine_Myst* vm) : MohawkOptionsDialog(vm), _vm(vm) { // I18N: Option for fast scene switching - _zipModeCheckbox = new GUI::CheckboxWidget(this, 15, 10, 220, 15, _("~Z~ip Mode Activated"), 0, kZipCmd); - _transitionsCheckbox = new GUI::CheckboxWidget(this, 15, 30, 220, 15, _("~T~ransitions Enabled"), 0, kTransCmd); + _zipModeCheckbox = new GUI::CheckboxWidget(this, 15, 10, 220, 15, _("~Z~ip Mode Activated"), nullptr, kZipCmd); + _transitionsCheckbox = new GUI::CheckboxWidget(this, 15, 30, 220, 15, _("~T~ransitions Enabled"), nullptr, kTransCmd); // I18N: Drop book page - _dropPageButton = new GUI::ButtonWidget(this, 15, 60, 100, 25, _("~D~rop Page"), 0, kDropCmd); + _dropPageButton = new GUI::ButtonWidget(this, 15, 60, 100, 25, _("~D~rop Page"), nullptr, kDropCmd); // Myst ME only has maps if (_vm->getFeatures() & GF_ME) - _showMapButton = new GUI::ButtonWidget(this, 15, 95, 100, 25, _("Show ~M~ap"), 0, kMapCmd); + _showMapButton = new GUI::ButtonWidget(this, 15, 95, 100, 25, _("Show ~M~ap"), nullptr, kMapCmd); else - _showMapButton = 0; + _showMapButton = nullptr; // Myst demo only has a menu if (_vm->getFeatures() & GF_DEMO) - _returnToMenuButton = new GUI::ButtonWidget(this, 15, 95, 100, 25, _("Main Men~u~"), 0, kMenuCmd); + _returnToMenuButton = new GUI::ButtonWidget(this, 15, 95, 100, 25, _("Main Men~u~"), nullptr, kMenuCmd); else - _returnToMenuButton = 0; + _returnToMenuButton = nullptr; } MystOptionsDialog::~MystOptionsDialog() { @@ -204,16 +204,19 @@ MystOptionsDialog::~MystOptionsDialog() { void MystOptionsDialog::open() { MohawkOptionsDialog::open(); - _dropPageButton->setEnabled(_vm->_gameState->_globals.heldPage != 0); + bool canDropPage = _vm->isInteractive() && _vm->_gameState->_globals.heldPage != kNoPage; + _dropPageButton->setEnabled(canDropPage); - if (_showMapButton) - _showMapButton->setEnabled(_vm->_scriptParser && - _vm->_scriptParser->getMap()); + if (_showMapButton) { + bool canShowMap = _vm->isInteractive() && _vm->_scriptParser->getMap(); + _showMapButton->setEnabled(canShowMap); + } - // Return to menu button is not enabled on the menu - if (_returnToMenuButton) - _returnToMenuButton->setEnabled(_vm->_scriptParser && - _vm->getCurStack() != kDemoStack); + if (_returnToMenuButton) { + // Return to menu button is not enabled on the menu + bool canReturnToMenu = _vm->isInteractive() && _vm->getCurStack() != kDemoStack; + _returnToMenuButton->setEnabled(canReturnToMenu); + } // Zip mode is disabled in the demo if (_vm->getFeatures() & GF_DEMO) @@ -266,8 +269,8 @@ void MystOptionsDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, ui RivenOptionsDialog::RivenOptionsDialog(MohawkEngine_Riven* vm) : MohawkOptionsDialog(vm), _vm(vm) { - _zipModeCheckbox = new GUI::CheckboxWidget(this, 15, 10, 220, 15, _("~Z~ip Mode Activated"), 0, kZipCmd); - _waterEffectCheckbox = new GUI::CheckboxWidget(this, 15, 30, 220, 15, _("~W~ater Effect Enabled"), 0, kWaterCmd); + _zipModeCheckbox = new GUI::CheckboxWidget(this, 15, 10, 220, 15, _("~Z~ip Mode Activated"), nullptr, kZipCmd); + _waterEffectCheckbox = new GUI::CheckboxWidget(this, 15, 30, 220, 15, _("~W~ater Effect Enabled"), nullptr, kWaterCmd); _transitionModeCaption = new GUI::StaticTextWidget(this, 15, 50, 90, 20, _("Transitions:"), Graphics::kTextAlignRight); _transitionModePopUp = new GUI::PopUpWidget(this, 115, 50, 120, 20); diff --git a/engines/mohawk/dialogs.h b/engines/mohawk/dialogs.h index efc1005737..9e892d768d 100644 --- a/engines/mohawk/dialogs.h +++ b/engines/mohawk/dialogs.h @@ -53,35 +53,35 @@ public: void setInfoText(const Common::String &message); - virtual void handleMouseDown(int x, int y, int button, int clickCount) { + void handleMouseDown(int x, int y, int button, int clickCount) override { setResult(0); close(); } - virtual void handleKeyDown(Common::KeyState state) { + void handleKeyDown(Common::KeyState state) override { setResult(state.ascii); close(); } - virtual void reflowLayout(); + void reflowLayout() override; }; class PauseDialog : public InfoDialog { public: PauseDialog(MohawkEngine* vm, const Common::String &message); - virtual void handleKeyDown(Common::KeyState state); + void handleKeyDown(Common::KeyState state) override; }; #if defined(ENABLE_MYST) || defined(ENABLE_RIVEN) class MohawkOptionsDialog : public GUI::Dialog { public: - MohawkOptionsDialog(MohawkEngine *_vm); - virtual ~MohawkOptionsDialog(); + explicit MohawkOptionsDialog(MohawkEngine *_vm); + ~MohawkOptionsDialog() override; - virtual void open() override; - virtual void reflowLayout() override; - virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) override; + void open() override; + void reflowLayout() override; + void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) override; int getLoadSlot() const { return _loadSlot; } int getSaveSlot() const { return _saveSlot; } @@ -112,11 +112,11 @@ class MohawkEngine_Myst; class MystOptionsDialog : public MohawkOptionsDialog { public: - MystOptionsDialog(MohawkEngine_Myst *vm); - virtual ~MystOptionsDialog(); + explicit MystOptionsDialog(MohawkEngine_Myst *vm); + ~MystOptionsDialog() override; - virtual void open() override; - virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data); + void open() override; + void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) override; private: MohawkEngine_Myst *_vm; @@ -137,11 +137,11 @@ class MohawkEngine_Riven; class RivenOptionsDialog : public MohawkOptionsDialog { public: - RivenOptionsDialog(MohawkEngine_Riven *vm); - virtual ~RivenOptionsDialog(); + explicit RivenOptionsDialog(MohawkEngine_Riven *vm); + ~RivenOptionsDialog() override; - virtual void open() override; - virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) override; + void open() override; + void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) override; private: MohawkEngine_Riven *_vm; diff --git a/engines/mohawk/graphics.cpp b/engines/mohawk/graphics.cpp index ea9b57ae17..fe675235c1 100644 --- a/engines/mohawk/graphics.cpp +++ b/engines/mohawk/graphics.cpp @@ -30,7 +30,7 @@ namespace Mohawk { -MohawkSurface::MohawkSurface() : _surface(0), _palette(0) { +MohawkSurface::MohawkSurface() : _surface(nullptr), _palette(nullptr) { _offsetX = 0; _offsetY = 0; } @@ -64,7 +64,7 @@ void MohawkSurface::convertToTrueColor() { _surface->free(); delete _surface; free(_palette); - _palette = 0; + _palette = nullptr; _surface = surface; } diff --git a/engines/mohawk/graphics.h b/engines/mohawk/graphics.h index f9fdeea15f..797fc1c080 100644 --- a/engines/mohawk/graphics.h +++ b/engines/mohawk/graphics.h @@ -41,7 +41,7 @@ class MohawkBitmap; class MohawkSurface { public: MohawkSurface(); - MohawkSurface(Graphics::Surface *surface, byte *palette = NULL, int offsetX = 0, int offsetY = 0); + explicit MohawkSurface(Graphics::Surface *surface, byte *palette = nullptr, int offsetX = 0, int offsetY = 0); ~MohawkSurface(); // getSurface() returns the surface in the current format diff --git a/engines/mohawk/installer_archive.cpp b/engines/mohawk/installer_archive.cpp index 0abc930683..62fc953f53 100644 --- a/engines/mohawk/installer_archive.cpp +++ b/engines/mohawk/installer_archive.cpp @@ -28,7 +28,7 @@ namespace Mohawk { InstallerArchive::InstallerArchive() : Common::Archive() { - _stream = 0; + _stream = nullptr; } InstallerArchive::~InstallerArchive() { @@ -103,7 +103,7 @@ bool InstallerArchive::open(const Common::String &filename) { } void InstallerArchive::close() { - delete _stream; _stream = 0; + delete _stream; _stream = nullptr; _map.clear(); } @@ -124,7 +124,7 @@ const Common::ArchiveMemberPtr InstallerArchive::getMember(const Common::String Common::SeekableReadStream *InstallerArchive::createReadStreamForMember(const Common::String &name) const { if (!_stream || !_map.contains(name)) - return 0; + return nullptr; const FileEntry &entry = _map[name]; diff --git a/engines/mohawk/installer_archive.h b/engines/mohawk/installer_archive.h index c3212d7f7c..19f6343b80 100644 --- a/engines/mohawk/installer_archive.h +++ b/engines/mohawk/installer_archive.h @@ -36,17 +36,17 @@ namespace Mohawk { class InstallerArchive : public Common::Archive { public: InstallerArchive(); - ~InstallerArchive(); + ~InstallerArchive() override; bool open(const Common::String &filename); void close(); - bool isOpen() const { return _stream != 0; } + bool isOpen() const { return _stream != nullptr; } // Common::Archive API implementation - bool hasFile(const Common::String &name) const; - int listMembers(Common::ArchiveMemberList &list) const; - const Common::ArchiveMemberPtr getMember(const Common::String &name) const; - Common::SeekableReadStream *createReadStreamForMember(const Common::String &name) const; + bool hasFile(const Common::String &name) const override; + int listMembers(Common::ArchiveMemberList &list) const override; + const Common::ArchiveMemberPtr getMember(const Common::String &name) const override; + Common::SeekableReadStream *createReadStreamForMember(const Common::String &name) const override; private: struct FileEntry { diff --git a/engines/mohawk/livingbooks.cpp b/engines/mohawk/livingbooks.cpp index 95b4722d81..b9e54e4e20 100644 --- a/engines/mohawk/livingbooks.cpp +++ b/engines/mohawk/livingbooks.cpp @@ -837,6 +837,7 @@ void LBPage::loadBITL(uint16 resourceId) { break; default: warning("Unknown item type %04x", type); + // fall through case 3: // often used for buttons res = new LBItem(_vm, this, rect); break; diff --git a/engines/mohawk/livingbooks.h b/engines/mohawk/livingbooks.h index 4b87b6464f..4649b9f5c9 100644 --- a/engines/mohawk/livingbooks.h +++ b/engines/mohawk/livingbooks.h @@ -707,7 +707,7 @@ protected: class MohawkEngine_LivingBooks : public MohawkEngine { protected: - Common::Error run(); + Common::Error run() override; public: MohawkEngine_LivingBooks(OSystem *syst, const MohawkGameDescription *gamedesc); @@ -725,7 +725,7 @@ public: Common::SeekableSubReadStreamEndian *wrapStreamEndian(uint32 tag, uint16 id); Common::String readString(Common::ReadStream *stream); Common::Rect readRect(Common::ReadStreamEndian *stream); - GUI::Debugger *getDebugger() { return _console; } + GUI::Debugger *getDebugger() override { return _console; } void addArchive(Archive *archive); void removeArchive(Archive *archive); diff --git a/engines/mohawk/livingbooks_code.cpp b/engines/mohawk/livingbooks_code.cpp index b5ea547414..7eb5a0cc3a 100644 --- a/engines/mohawk/livingbooks_code.cpp +++ b/engines/mohawk/livingbooks_code.cpp @@ -1349,14 +1349,17 @@ void LBCode::cmdSetPlayParams(const Common::Array<LBValue> ¶ms) { switch (params.size()) { case 8: target->_soundMode = params[7].integer; + // fall through case 7: target->_controlMode = params[6].integer; + // fall through case 6: // TODO: _relocPoint? case 5: // TODO: _periodMin/Max case 4: target->_timingMode = params[3].integer; + // fall through case 3: // TODO: _delayMin/Max case 2: diff --git a/engines/mohawk/mohawk.cpp b/engines/mohawk/mohawk.cpp index 53481af8a7..52f73dbbb1 100644 --- a/engines/mohawk/mohawk.cpp +++ b/engines/mohawk/mohawk.cpp @@ -41,8 +41,8 @@ MohawkEngine::MohawkEngine(OSystem *syst, const MohawkGameDescription *gamedesc) // Setup mixer syncSoundSettings(); - _pauseDialog = 0; - _cursor = 0; + _pauseDialog = nullptr; + _cursor = nullptr; } MohawkEngine::~MohawkEngine() { @@ -70,7 +70,6 @@ Common::SeekableReadStream *MohawkEngine::getResource(uint32 tag, uint16 id) { return _mhk[i]->getResource(tag, id); error("Could not find a '%s' resource with ID %04x", tag2str(tag), id); - return NULL; } bool MohawkEngine::hasResource(uint32 tag, uint16 id) { @@ -95,7 +94,6 @@ uint32 MohawkEngine::getResourceOffset(uint32 tag, uint16 id) { return _mhk[i]->getOffset(tag, id); error("Could not find a '%s' resource with ID %04x", tag2str(tag), id); - return 0; } uint16 MohawkEngine::findResourceID(uint32 tag, const Common::String &resName) { @@ -104,7 +102,6 @@ uint16 MohawkEngine::findResourceID(uint32 tag, const Common::String &resName) { return _mhk[i]->findResourceID(tag, resName); error("Could not find a '%s' resource matching name '%s'", tag2str(tag), resName.c_str()); - return 0xFFFF; } Common::String MohawkEngine::getResourceName(uint32 tag, uint16 id) { @@ -114,7 +111,6 @@ Common::String MohawkEngine::getResourceName(uint32 tag, uint16 id) { } error("Could not find a \'%s\' resource with ID %04x", tag2str(tag), id); - return 0; } } // End of namespace Mohawk diff --git a/engines/mohawk/mohawk.h b/engines/mohawk/mohawk.h index c6781ae448..8184f46bad 100644 --- a/engines/mohawk/mohawk.h +++ b/engines/mohawk/mohawk.h @@ -49,14 +49,7 @@ enum MohawkGameType { GType_MYST, GType_MAKINGOF, GType_RIVEN, - GType_ZOOMBINI, GType_CSTIME, - GType_CSWORLD, - GType_CSAMTRAK, - GType_JAMESMATH, - GType_TREEHOUSE, - GType_1STDEGREE, - GType_CSUSA, GType_LIVINGBOOKSV1, GType_LIVINGBOOKSV2, GType_LIVINGBOOKSV3, @@ -68,8 +61,7 @@ enum MohawkGameFeatures { GF_ME = (1 << 0), // Myst Masterpiece Edition GF_DVD = (1 << 1), GF_DEMO = (1 << 2), - GF_HASMIDI = (1 << 3), - GF_LB_10 = (1 << 4) // very early Living Books 1.0 games + GF_LB_10 = (1 << 3) // very early Living Books 1.0 games }; struct MohawkGameDescription; @@ -80,23 +72,22 @@ class CursorManager; class MohawkEngine : public ::Engine { protected: - virtual Common::Error run(); + Common::Error run() override; public: MohawkEngine(OSystem *syst, const MohawkGameDescription *gamedesc); - virtual ~MohawkEngine(); + ~MohawkEngine() override; // Detection related functions const MohawkGameDescription *_gameDescription; const char *getGameId() const; uint32 getFeatures() const; const char *getAppName() const; - uint16 getVersion() const; Common::Platform getPlatform() const; uint8 getGameType() const; Common::Language getLanguage() const; - bool hasFeature(EngineFeature f) const; + bool hasFeature(EngineFeature f) const override; CursorManager *_cursor; diff --git a/engines/mohawk/myst.cpp b/engines/mohawk/myst.cpp index 5baa89cea8..a01cfdd343 100644 --- a/engines/mohawk/myst.cpp +++ b/engines/mohawk/myst.cpp @@ -54,7 +54,8 @@ namespace Mohawk { -MohawkEngine_Myst::MohawkEngine_Myst(OSystem *syst, const MohawkGameDescription *gamedesc) : MohawkEngine(syst, gamedesc) { +MohawkEngine_Myst::MohawkEngine_Myst(OSystem *syst, const MohawkGameDescription *gamedesc) : + MohawkEngine(syst, gamedesc) { DebugMan.addDebugChannel(kDebugVariable, "Variable", "Track Variable Accesses"); DebugMan.addDebugChannel(kDebugSaveLoad, "SaveLoad", "Track Save/Load Function"); DebugMan.addDebugChannel(kDebugView, "View", "Track Card File (VIEW) Parsing"); @@ -69,7 +70,9 @@ MohawkEngine_Myst::MohawkEngine_Myst(OSystem *syst, const MohawkGameDescription _currentCursor = 0; _mainCursor = kDefaultMystCursor; _showResourceRects = false; + _curStack = 0; _curCard = 0; + _lastSaveTime = 0; _hoverResource = nullptr; _activeResource = nullptr; @@ -82,13 +85,20 @@ MohawkEngine_Myst::MohawkEngine_Myst(OSystem *syst, const MohawkGameDescription _scriptParser = nullptr; _gameState = nullptr; _optionsDialog = nullptr; + _rnd = nullptr; _prevStack = nullptr; _mouseClicked = false; _mouseMoved = false; _escapePressed = false; - _interactive = true; + _waitingOnBlockingOperation = false; + _runExitScript = true; + + _needsPageDrop = false; + _needsShowCredits = false; + _needsShowDemoMenu = false; + _needsShowMap = false; } MohawkEngine_Myst::~MohawkEngine_Myst() { @@ -253,8 +263,40 @@ void MohawkEngine_Myst::playMovieBlocking(const Common::String &name, MystStack waitUntilMovieEnds(video); } -void MohawkEngine_Myst::playFlybyMovie(const Common::String &name) { - Common::String filename = wrapMovieFilename(name, kMasterpieceOnly); +void MohawkEngine_Myst::playFlybyMovie(uint16 stack, uint16 card) { + static const uint16 kMasterpieceOnly = 0xFFFF; + + // Play Flyby Entry Movie on Masterpiece Edition. + const char *flyby = nullptr; + + switch (stack) { + case kSeleniticStack: + flyby = "selenitic flyby"; + break; + case kStoneshipStack: + flyby = "stoneship flyby"; + break; + // Myst Flyby Movie not used in Original Masterpiece Edition Engine + // We play it when first arriving on Myst, and if the user has chosen so. + case kMystStack: + if (ConfMan.getBool("playmystflyby")) + flyby = "myst flyby"; + break; + case kMechanicalStack: + flyby = "mech age flyby"; + break; + case kChannelwoodStack: + flyby = "channelwood flyby"; + break; + default: + break; + } + + if (!flyby) { + return; + } + + Common::String filename = wrapMovieFilename(flyby, kMasterpieceOnly); VideoEntryPtr video = _video->playMovie(filename, Audio::Mixer::kSFXSoundType); if (!video) { error("Failed to open the '%s' movie", filename.c_str()); @@ -271,7 +313,7 @@ void MohawkEngine_Myst::waitUntilMovieEnds(const VideoEntryPtr &video) { if (!video) return; - _interactive = false; + _waitingOnBlockingOperation = true; // Sanity check if (video->isLooping()) @@ -289,17 +331,17 @@ void MohawkEngine_Myst::waitUntilMovieEnds(const VideoEntryPtr &video) { // Ensure it's removed _video->removeEntry(video); - _interactive = true; + _waitingOnBlockingOperation = false; } void MohawkEngine_Myst::playSoundBlocking(uint16 id) { - _interactive = false; + _waitingOnBlockingOperation = true; _sound->playEffect(id); while (_sound->isEffectPlaying() && !shouldQuit()) { doFrame(); } - _interactive = true; + _waitingOnBlockingOperation = false; } Common::Error MohawkEngine_Myst::run() { @@ -340,9 +382,6 @@ Common::Error MohawkEngine_Myst::run() { _mhk.push_back(mhk); } - // Test Load Function... - loadHelp(10000); - while (!shouldQuit()) { doFrame(); } @@ -353,10 +392,14 @@ Common::Error MohawkEngine_Myst::run() { void MohawkEngine_Myst::doFrame() { // Update any background videos _video->updateMovies(); - if (!_scriptParser->isScriptRunning() && _interactive) { - _interactive = false; + if (isInteractive()) { + _waitingOnBlockingOperation = true; _scriptParser->runPersistentScripts(); - _interactive = true; + _waitingOnBlockingOperation = false; + } + + if (shouldPerformAutoSave(_lastSaveTime)) { + tryAutoSaving(); } Common::Event event; @@ -410,9 +453,19 @@ void MohawkEngine_Myst::doFrame() { } if (_needsShowCredits) { - _cursor->hideCursor(); - changeToStack(kCreditsStack, 10000, 0, 0); - _needsShowCredits = false; + if (isInteractive()) { + // Attempt to autosave before exiting + tryAutoSaving(); + + _cursor->hideCursor(); + changeToStack(kCreditsStack, 10000, 0, 0); + _needsShowCredits = false; + } else { + // Showing the credits in the middle of a script is not possible + // because it unloads the previous age, removing data needed by the + // rest of the script. Instead we just quit without showing the credits. + quitGame(); + } } break; case Common::KEYCODE_ESCAPE: @@ -431,12 +484,17 @@ void MohawkEngine_Myst::doFrame() { break; } break; + case Common::EVENT_QUIT: + case Common::EVENT_RTL: + // Attempt to autosave before exiting + tryAutoSaving(); + break; default: break; } } - if (!_scriptParser->isScriptRunning() && _interactive) { + if (isInteractive()) { updateActiveResource(); checkCurrentResource(); } @@ -448,7 +506,7 @@ void MohawkEngine_Myst::doFrame() { } bool MohawkEngine_Myst::wait(uint32 duration, bool skippable) { - _interactive = false; + _waitingOnBlockingOperation = true; uint32 end = getTotalPlayTime() + duration; do { @@ -460,7 +518,7 @@ bool MohawkEngine_Myst::wait(uint32 duration, bool skippable) { } } while (getTotalPlayTime() < end && !shouldQuit()); - _interactive = true; + _waitingOnBlockingOperation = false; return false; } @@ -480,20 +538,27 @@ void MohawkEngine_Myst::pauseEngineIntern(bool pause) { void MohawkEngine_Myst::changeToStack(uint16 stack, uint16 card, uint16 linkSrcSound, uint16 linkDstSound) { debug(2, "changeToStack(%d)", stack); - _curStack = stack; - // Fill screen with black and empty cursor _cursor->setCursor(0); _currentCursor = 0; + _sound->stopEffect(); + _video->stopVideos(); + + // In Myst ME, play a fullscreen flyby movie, except when loading saves. + // Also play a flyby when first linking to Myst. + if (getFeatures() & GF_ME + && (_curStack != kIntroStack || (stack == kMystStack && card == 4134))) { + playFlybyMovie(stack, card); + } + + _sound->stopBackground(); + if (getFeatures() & GF_ME) _system->fillScreen(_system->getScreenFormat().RGBToColor(0, 0, 0)); else _gfx->clearScreenPalette(); - _sound->stopEffect(); - _sound->stopBackground(); - _video->stopVideos(); if (linkSrcSound) playSoundBlocking(linkSrcSound); @@ -503,20 +568,22 @@ void MohawkEngine_Myst::changeToStack(uint16 stack, uint16 card, uint16 linkSrcS delete _prevStack; _prevStack = _scriptParser; + _curStack = stack; + switch (_curStack) { case kChannelwoodStack: - _gameState->_globals.currentAge = 4; + _gameState->_globals.currentAge = kChannelwood; _scriptParser = new MystStacks::Channelwood(this); break; case kCreditsStack: _scriptParser = new MystStacks::Credits(this); break; case kDemoStack: - _gameState->_globals.currentAge = 0; + _gameState->_globals.currentAge = kSelenitic; _scriptParser = new MystStacks::Demo(this); break; case kDniStack: - _gameState->_globals.currentAge = 6; + _gameState->_globals.currentAge = kDni; _scriptParser = new MystStacks::Dni(this); break; case kIntroStack: @@ -526,26 +593,26 @@ void MohawkEngine_Myst::changeToStack(uint16 stack, uint16 card, uint16 linkSrcS _scriptParser = new MystStacks::MakingOf(this); break; case kMechanicalStack: - _gameState->_globals.currentAge = 3; + _gameState->_globals.currentAge = kMechanical; _scriptParser = new MystStacks::Mechanical(this); break; case kMystStack: - _gameState->_globals.currentAge = 2; + _gameState->_globals.currentAge = kMystLibrary; _scriptParser = new MystStacks::Myst(this); break; case kDemoPreviewStack: _scriptParser = new MystStacks::Preview(this); break; case kSeleniticStack: - _gameState->_globals.currentAge = 0; + _gameState->_globals.currentAge = kSelenitic; _scriptParser = new MystStacks::Selenitic(this); break; case kDemoSlidesStack: - _gameState->_globals.currentAge = 1; + _gameState->_globals.currentAge = kStoneship; _scriptParser = new MystStacks::Slides(this); break; case kStoneshipStack: - _gameState->_globals.currentAge = 1; + _gameState->_globals.currentAge = kStoneship; _scriptParser = new MystStacks::Stoneship(this); break; default: @@ -570,38 +637,6 @@ void MohawkEngine_Myst::changeToStack(uint16 stack, uint16 card, uint16 linkSrcS _cache.clear(); _gfx->clearCache(); - if (getFeatures() & GF_ME) { - // Play Flyby Entry Movie on Masterpiece Edition. - const char *flyby = nullptr; - - switch (_curStack) { - case kSeleniticStack: - flyby = "selenitic flyby"; - break; - case kStoneshipStack: - flyby = "stoneship flyby"; - break; - // Myst Flyby Movie not used in Original Masterpiece Edition Engine - // We play it when first arriving on Myst, and if the user has chosen so. - case kMystStack: - if (ConfMan.getBool("playmystflyby") && card == 4134) - flyby = "myst flyby"; - break; - case kMechanicalStack: - flyby = "mech age flyby"; - break; - case kChannelwoodStack: - flyby = "channelwood flyby"; - break; - default: - break; - } - - if (flyby) { - playFlybyMovie(flyby); - } - } - changeToCard(card, kTransitionCopy); if (linkDstSound) @@ -677,7 +712,7 @@ void MohawkEngine_Myst::changeToCard(uint16 card, TransitionType transition) { // The demo resets the cursor at each card change except when in the library if (getFeatures() & GF_DEMO - && _gameState->_globals.currentAge != 2) { + && _gameState->_globals.currentAge != kMystLibrary) { _cursor->setDefaultCursor(); } @@ -724,7 +759,7 @@ void MohawkEngine_Myst::checkCurrentResource() { } for (uint16 i = 0; i < _resources.size(); i++) { - if (_resources[i]->contains(mouse) && _resources[i]->type == kMystAreaHover + if (_resources[i]->contains(mouse) && _resources[i]->hasType(kMystAreaHover) && _hoverResource != _resources[i]) { _hoverResource = static_cast<MystAreaHover *>(_resources[i]); _hoverResource->handleMouseEnter(); @@ -955,50 +990,6 @@ void MohawkEngine_Myst::runExitScript() { _scriptParser->runScript(script); } -void MohawkEngine_Myst::loadHelp(uint16 id) { - // The original version did not have the help system - if (!(getFeatures() & GF_ME)) - return; - - // TODO: Help File contains 5 cards i.e. VIEW, RLST, etc. - // in addition to HELP resources. - // These are Ids 9930 to 9934 - // Need to deal with loading and displaying these.. - // Current engine structure only supports display of - // card from primary stack MHK - - debugC(kDebugHelp, "Loading Help System Data"); - - Common::SeekableReadStream *helpStream = getResource(ID_HELP, id); - - uint16 count = helpStream->readUint16LE(); - uint16 *u0 = new uint16[count]; - Common::String helpText; - - debugC(kDebugHelp, "\tcount: %d", count); - - for (uint16 i = 0; i < count; i++) { - u0[i] = helpStream->readUint16LE(); - debugC(kDebugHelp, "\tu0[%d]: %d", i, u0[i]); - } - - // TODO: Previous values i.e. u0[0] to u0[count - 2] - // appear to be resource ids in the help.dat file.. - if (u0[count - 1] != count) - warning("loadHelp(): last u0 value is not equal to count"); - - do { - helpText += helpStream->readByte(); - } while (helpText.lastChar() != 0); - helpText.deleteLastChar(); - - debugC(kDebugHelp, "\thelpText: \"%s\"", helpText.c_str()); - - delete[] u0; - - delete helpStream; -} - void MohawkEngine_Myst::loadCursorHints() { _cursorHints.clear(); @@ -1059,7 +1050,7 @@ void MohawkEngine_Myst::checkCursorHints() { // Check all the cursor hints to see if we're in a hotspot that contains a hint. for (uint16 i = 0; i < _cursorHints.size(); i++) - if (_resources[_cursorHints[i].id] == _activeResource && _activeResource->isEnabled()) { + if (_activeResource && _resources[_cursorHints[i].id] == _activeResource && _activeResource->isEnabled()) { if (_cursorHints[i].cursor == -1) { uint16 var_value = _scriptParser->getVar(_cursorHints[i].variableHint.var); @@ -1107,7 +1098,7 @@ void MohawkEngine_Myst::redrawResource(MystAreaImageSwitch *resource, bool updat void MohawkEngine_Myst::redrawArea(uint16 var, bool update) { for (uint16 i = 0; i < _resources.size(); i++) - if (_resources[i]->type == kMystAreaImageSwitch && _resources[i]->getImageSwitchVar() == var) + if (_resources[i]->hasType(kMystAreaImageSwitch) && _resources[i]->getImageSwitchVar() == var) redrawResource(static_cast<MystAreaImageSwitch *>(_resources[i]), update); } @@ -1120,36 +1111,34 @@ MystArea *MohawkEngine_Myst::loadResource(Common::SeekableReadStream *rlstStream switch (type) { case kMystAreaAction: - resource = new MystAreaAction(this, rlstStream, parent); + resource = new MystAreaAction(this, type, rlstStream, parent); break; case kMystAreaVideo: - resource = new MystAreaVideo(this, rlstStream, parent); + resource = new MystAreaVideo(this, type, rlstStream, parent); break; case kMystAreaActionSwitch: - resource = new MystAreaActionSwitch(this, rlstStream, parent); + resource = new MystAreaActionSwitch(this, type, rlstStream, parent); break; case kMystAreaImageSwitch: - resource = new MystAreaImageSwitch(this, rlstStream, parent); + resource = new MystAreaImageSwitch(this, type, rlstStream, parent); break; case kMystAreaSlider: - resource = new MystAreaSlider(this, rlstStream, parent); + resource = new MystAreaSlider(this, type, rlstStream, parent); break; case kMystAreaDrag: - resource = new MystAreaDrag(this, rlstStream, parent); + resource = new MystAreaDrag(this, type, rlstStream, parent); break; case kMystVideoInfo: - resource = new MystVideoInfo(this, rlstStream, parent); + resource = new MystVideoInfo(this, type, rlstStream, parent); break; case kMystAreaHover: - resource = new MystAreaHover(this, rlstStream, parent); + resource = new MystAreaHover(this, type, rlstStream, parent); break; default: - resource = new MystArea(this, rlstStream, parent); + resource = new MystArea(this, type, rlstStream, parent); break; } - resource->type = type; - return resource; } @@ -1184,15 +1173,34 @@ Common::Error MohawkEngine_Myst::loadGameState(int slot) { } Common::Error MohawkEngine_Myst::saveGameState(int slot, const Common::String &desc) { - return _gameState->save(slot, desc) ? Common::kNoError : Common::kUnknownError; + return _gameState->save(slot, desc, false) ? Common::kNoError : Common::kUnknownError; +} + +void MohawkEngine_Myst::tryAutoSaving() { + if (!canSaveGameStateCurrently()) { + return; // Can't save right now, try again on the next frame + } + + _lastSaveTime = _system->getMillis(); + + if (!_gameState->isAutoSaveAllowed()) { + return; // Can't autosave ever, try again after the next autosave delay + } + + if (!_gameState->save(MystGameState::kAutoSaveSlot, "Autosave", true)) + warning("Attempt to autosave has failed."); } bool MohawkEngine_Myst::hasGameSaveSupport() const { return !(getFeatures() & GF_DEMO) && getGameType() != GType_MAKINGOF; } +bool MohawkEngine_Myst::isInteractive() { + return !_scriptParser->isScriptRunning() && !_waitingOnBlockingOperation; +} + bool MohawkEngine_Myst::canLoadGameStateCurrently() { - if (_scriptParser->isScriptRunning() || !_interactive) { + if (!isInteractive()) { return false; } @@ -1229,8 +1237,8 @@ bool MohawkEngine_Myst::canSaveGameStateCurrently() { } void MohawkEngine_Myst::dropPage() { - uint16 page = _gameState->_globals.heldPage; - bool whitePage = page == 13; + HeldPage page = _gameState->_globals.heldPage; + bool whitePage = page == kWhitePage; bool bluePage = page - 1 < 6; bool redPage = page - 7 < 6; @@ -1238,25 +1246,25 @@ void MohawkEngine_Myst::dropPage() { _sound->playEffect(800); // Drop page - _gameState->_globals.heldPage = 0; + _gameState->_globals.heldPage = kNoPage; // Redraw page area - if (whitePage && _gameState->_globals.currentAge == 2) { + if (whitePage && _gameState->_globals.currentAge == kMystLibrary) { _scriptParser->toggleVar(41); redrawArea(41); } else if (bluePage) { - if (page == 6) { - if (_gameState->_globals.currentAge == 2) + if (page == kBlueFirePlacePage) { + if (_gameState->_globals.currentAge == kMystLibrary) redrawArea(24); } else { redrawArea(103); } } else if (redPage) { - if (page == 12) { - if (_gameState->_globals.currentAge == 2) + if (page == kRedFirePlacePage) { + if (_gameState->_globals.currentAge == kMystLibrary) redrawArea(25); - } else if (page == 10) { - if (_gameState->_globals.currentAge == 1) + } else if (page == kRedStoneshipPage) { + if (_gameState->_globals.currentAge == kStoneship) redrawArea(35); } else { redrawArea(102); @@ -1277,9 +1285,9 @@ MystSoundBlock MohawkEngine_Myst::readSoundBlock(Common::ReadStream *stream) con debugC(kDebugView, "\tSound: %d", soundBlock.sound); soundBlock.soundVolume = stream->readUint16LE(); debugC(kDebugView, "\tVolume: %d", soundBlock.soundVolume); - } else if (soundBlock.sound == kMystSoundActionContinue) + } else if (soundBlock.sound == kMystSoundActionContinue) { debugC(kDebugView, "Continue current sound"); - else if (soundBlock.sound == kMystSoundActionChangeVolume) { + } else if (soundBlock.sound == kMystSoundActionChangeVolume) { debugC(kDebugView, "Continue current sound, change volume"); soundBlock.soundVolume = stream->readUint16LE(); debugC(kDebugView, "\tVolume: %d", soundBlock.soundVolume); @@ -1305,8 +1313,7 @@ MystSoundBlock MohawkEngine_Myst::readSoundBlock(Common::ReadStream *stream) con soundBlock.soundList.push_back(sound); } } else { - debugC(kDebugView, "Unknown"); - warning("Unknown sound control value '%d' in card '%d'", soundBlock.sound, _curCard); + error("Unknown sound control value '%d' in card '%d'", soundBlock.sound, _curCard); } return soundBlock; diff --git a/engines/mohawk/myst.h b/engines/mohawk/myst.h index 3f756faee0..64fee10fde 100644 --- a/engines/mohawk/myst.h +++ b/engines/mohawk/myst.h @@ -95,8 +95,6 @@ enum TransitionType { kNoTransition = 999 }; -const uint16 kMasterpieceOnly = 0xFFFF; - struct MystCondition { uint16 var; Common::Array<uint16> values; @@ -176,7 +174,7 @@ protected: public: MohawkEngine_Myst(OSystem *syst, const MohawkGameDescription *gamedesc); - virtual ~MohawkEngine_Myst(); + ~MohawkEngine_Myst() override; Common::SeekableReadStream *getResource(uint32 tag, uint16 id) override; Common::Array<uint16> getResourceIDList(uint32 type) const; @@ -229,17 +227,25 @@ public: VideoEntryPtr playMovie(const Common::String &name, MystStack stack); VideoEntryPtr findVideo(const Common::String &name, MystStack stack); void playMovieBlocking(const Common::String &name, MystStack stack, uint16 x, uint16 y); - void playFlybyMovie(const Common::String &name); + void playFlybyMovie(uint16 stack, uint16 card); void waitUntilMovieEnds(const VideoEntryPtr &video); void playSoundBlocking(uint16 id); GUI::Debugger *getDebugger() override { return _console; } + /** + * Is the game currently interactive + * + * When the game is interactive, the user can interact with the game world + * and perform other operations such as loading saved games, ... + */ + bool isInteractive(); bool canLoadGameStateCurrently() override; bool canSaveGameStateCurrently() override; Common::Error loadGameState(int slot) override; Common::Error saveGameState(int slot, const Common::String &desc) override; + void tryAutoSaving(); bool hasFeature(EngineFeature f) const override; private: @@ -251,6 +257,7 @@ private: uint16 _curStack; uint16 _curCard; + uint32 _lastSaveTime; MystView _view; bool _runExitScript; @@ -264,8 +271,6 @@ private: void runInitScript(); void runExitScript(); - void loadHelp(uint16 id); - void loadResources(); void drawResourceRects(); void checkCurrentResource(); @@ -286,7 +291,7 @@ private: bool _mouseClicked; bool _mouseMoved; bool _escapePressed; - bool _interactive; // Is the game currently interactive + bool _waitingOnBlockingOperation; Common::Array<MystCursorHint> _cursorHints; void loadCursorHints(); diff --git a/engines/mohawk/myst_areas.cpp b/engines/mohawk/myst_areas.cpp index 7cc39e97b2..eff51bf1c9 100644 --- a/engines/mohawk/myst_areas.cpp +++ b/engines/mohawk/myst_areas.cpp @@ -32,9 +32,10 @@ namespace Mohawk { -MystArea::MystArea(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystArea *parent) { - _vm = vm; - _parent = parent; +MystArea::MystArea(MohawkEngine_Myst *vm, ResourceType type, Common::SeekableReadStream *rlstStream, MystArea *parent) : + _vm(vm), + _parent(parent), + _type(type) { if (parent == nullptr) { _flags = rlstStream->readUint16LE(); @@ -77,7 +78,7 @@ void MystArea::handleMouseUp() { uint16 opcode; - switch (type) { + switch (_type) { case kMystAreaForward: opcode = 6; break; @@ -118,7 +119,7 @@ void MystArea::setEnabled(bool enabled) { const Common::String MystArea::describe() { Common::String desc = Common::String::format("type: %2d rect: (%3d %3d %3d %3d)", - type, _rect.left, _rect.top, _rect.width(), _rect.height()); + _type, _rect.left, _rect.top, _rect.width(), _rect.height()); if (_dest != 0) desc += Common::String::format(" dest: %4d", _dest); @@ -137,8 +138,8 @@ void MystArea::drawBoundingRect() { } } -MystAreaAction::MystAreaAction(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystArea *parent) : - MystArea(vm, rlstStream, parent) { +MystAreaAction::MystAreaAction(MohawkEngine_Myst *vm, ResourceType type, Common::SeekableReadStream *rlstStream, MystArea *parent) : + MystArea(vm, type, rlstStream, parent) { debugC(kDebugResource, "\tResource Type 5 Script:"); _script = vm->_scriptParser->readScript(rlstStream, kMystScriptNormal); @@ -175,8 +176,8 @@ Common::String MystAreaVideo::convertMystVideoName(const Common::String &name) { return temp + ".mov"; } -MystAreaVideo::MystAreaVideo(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystArea *parent) : - MystAreaAction(vm, rlstStream, parent) { +MystAreaVideo::MystAreaVideo(MohawkEngine_Myst *vm, ResourceType type, Common::SeekableReadStream *rlstStream, MystArea *parent) : + MystAreaAction(vm, type, rlstStream, parent) { char c = 0; do { @@ -195,12 +196,21 @@ MystAreaVideo::MystAreaVideo(MohawkEngine_Myst *vm, Common::SeekableReadStream * // Position values require modulus 10000 to keep in sane range. _left = rlstStream->readSint16LE() % 10000; _top = rlstStream->readSint16LE() % 10000; - _playOnCardChange = rlstStream->readUint16LE(); + _playOnCardChange = rlstStream->readUint16LE() != 0; _direction = rlstStream->readSint16LE(); _playBlocking = rlstStream->readUint16LE(); _loop = rlstStream->readUint16LE(); _playRate = rlstStream->readUint16LE(); + // WORKAROUND: Myst v1.0 has playOnCardChange set to true + // for the Myst flyby video shown during the intro. + // This causes the flyby to play over the closed Myst book picture. + // Later releases of the game have that flag set to false. + // Here we apply a resource patch to match the newer releases. + if (_videoFile == "qtw/intro/intro2.mov") { + _playOnCardChange = false; + } + debugC(kDebugResource, "\tvideoFile: \"%s\"", _videoFile.c_str()); debugC(kDebugResource, "\tleft: %d", _left); debugC(kDebugResource, "\ttop: %d", _top); @@ -281,8 +291,8 @@ void MystAreaVideo::pauseMovie(bool pause) { handle->pause(pause); } -MystAreaActionSwitch::MystAreaActionSwitch(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystArea *parent) : - MystArea(vm, rlstStream, parent) { +MystAreaActionSwitch::MystAreaActionSwitch(MohawkEngine_Myst *vm, ResourceType type, Common::SeekableReadStream *rlstStream, MystArea *parent) : + MystArea(vm, type, rlstStream, parent) { _actionSwitchVar = rlstStream->readUint16LE(); uint16 numSubResources = rlstStream->readUint16LE(); debugC(kDebugResource, "\tactionSwitchVar: %d", _actionSwitchVar); @@ -335,8 +345,8 @@ void MystAreaActionSwitch::handleMouseDown() { doSwitch(&MystArea::handleMouseDown); } -MystAreaImageSwitch::MystAreaImageSwitch(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystArea *parent) : - MystAreaActionSwitch(vm, rlstStream, parent) { +MystAreaImageSwitch::MystAreaImageSwitch(MohawkEngine_Myst *vm, ResourceType type, Common::SeekableReadStream *rlstStream, MystArea *parent) : + MystAreaActionSwitch(vm, type, rlstStream, parent) { _imageSwitchVar = rlstStream->readUint16LE(); uint16 numSubImages = rlstStream->readUint16LE(); debugC(kDebugResource, "\tvar8: %d", _imageSwitchVar); @@ -477,8 +487,8 @@ const Common::String MystAreaImageSwitch::describe() { // No MystResourceType9! -MystAreaSlider::MystAreaSlider(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystArea *parent) : - MystAreaDrag(vm, rlstStream, parent) { +MystAreaSlider::MystAreaSlider(MohawkEngine_Myst *vm, ResourceType type, Common::SeekableReadStream *rlstStream, MystArea *parent) : + MystAreaDrag(vm, type, rlstStream, parent) { _dragSound = rlstStream->readUint16LE(); debugC(kDebugResource, "\tdrag sound : %d", _dragSound); @@ -643,8 +653,8 @@ void MystAreaSlider::updatePosition(const Common::Point &mouse) { _vm->_sound->playEffect(_dragSound); } -MystAreaDrag::MystAreaDrag(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystArea *parent) : - MystAreaImageSwitch(vm, rlstStream, parent) { +MystAreaDrag::MystAreaDrag(MohawkEngine_Myst *vm, ResourceType type, Common::SeekableReadStream *rlstStream, MystArea *parent) : + MystAreaImageSwitch(vm, type, rlstStream, parent) { _flagHV = rlstStream->readUint16LE(); _minH = rlstStream->readUint16LE(); _maxH = rlstStream->readUint16LE(); @@ -748,8 +758,8 @@ uint16 MystAreaDrag::getList3(uint16 index) { return (index < _lists[2].size()) ? _lists[2][index] : 0; } -MystVideoInfo::MystVideoInfo(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystArea *parent) : - MystAreaDrag(vm, rlstStream, parent) { +MystVideoInfo::MystVideoInfo(MohawkEngine_Myst *vm, ResourceType type, Common::SeekableReadStream *rlstStream, MystArea *parent) : + MystAreaDrag(vm, type, rlstStream, parent) { _numFrames = rlstStream->readUint16LE(); _firstFrame = rlstStream->readUint16LE(); uint16 frameWidth = rlstStream->readUint16LE(); @@ -774,8 +784,8 @@ MystVideoInfo::~MystVideoInfo() { } void MystVideoInfo::drawFrame(uint16 frame) { - _currentFrame = _firstFrame + frame; - _vm->_gfx->copyImageToScreen(_currentFrame, _frameRect); + uint16 currentFrame = _firstFrame + frame; + _vm->_gfx->copyImageToScreen(currentFrame, _frameRect); } bool MystVideoInfo::pullLeverV() { @@ -810,8 +820,8 @@ void MystVideoInfo::releaseLeverV() { } } -MystAreaHover::MystAreaHover(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystArea *parent) : - MystArea(vm, rlstStream, parent) { +MystAreaHover::MystAreaHover(MohawkEngine_Myst *vm, ResourceType type, Common::SeekableReadStream *rlstStream, MystArea *parent) : + MystArea(vm, type, rlstStream, parent) { _enterOpcode = rlstStream->readUint16LE(); _leaveOpcode = rlstStream->readUint16LE(); diff --git a/engines/mohawk/myst_areas.h b/engines/mohawk/myst_areas.h index 32b6ca4f2f..b389f32ea1 100644 --- a/engines/mohawk/myst_areas.h +++ b/engines/mohawk/myst_areas.h @@ -58,12 +58,13 @@ enum { class MystArea { public: - MystArea(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystArea *parent); + MystArea(MohawkEngine_Myst *vm, ResourceType type, Common::SeekableReadStream *rlstStream, MystArea *parent); virtual ~MystArea(); virtual const Common::String describe(); void drawBoundingRect(); + bool hasType(ResourceType type) const { return _type == type; } bool contains(Common::Point point) { return _rect.contains(point); } virtual void drawDataToScreen() {} virtual void handleCardChange() {} @@ -83,10 +84,10 @@ public: virtual void handleMouseDrag() {} MystArea *_parent; - ResourceType type; protected: MohawkEngine_Myst *_vm; + ResourceType _type; uint16 _flags; Common::Rect _rect; uint16 _dest; @@ -94,7 +95,7 @@ protected: class MystAreaAction : public MystArea { public: - MystAreaAction(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystArea *parent); + MystAreaAction(MohawkEngine_Myst *vm, ResourceType type, Common::SeekableReadStream *rlstStream, MystArea *parent); void handleMouseUp() override; const Common::String describe() override; @@ -105,7 +106,7 @@ protected: class MystAreaVideo : public MystAreaAction { public: - MystAreaVideo(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystArea *parent); + MystAreaVideo(MohawkEngine_Myst *vm, ResourceType type, Common::SeekableReadStream *rlstStream, MystArea *parent); VideoEntryPtr playMovie(); VideoEntryPtr getVideo(); @@ -125,20 +126,20 @@ protected: uint16 _loop; int16 _direction; // 1 => forward, -1 => backwards uint16 _playBlocking; - uint16 _playOnCardChange; + bool _playOnCardChange; uint16 _playRate; // percents }; class MystAreaActionSwitch : public MystArea { public: - MystAreaActionSwitch(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystArea *parent); - virtual ~MystAreaActionSwitch(); + MystAreaActionSwitch(MohawkEngine_Myst *vm, ResourceType type, Common::SeekableReadStream *rlstStream, MystArea *parent); + ~MystAreaActionSwitch() override; - virtual void drawDataToScreen() override; - virtual void handleCardChange() override; + void drawDataToScreen() override; + void handleCardChange() override; - virtual void handleMouseUp() override; - virtual void handleMouseDown() override; + void handleMouseUp() override; + void handleMouseDown() override; MystArea *getSubResource(uint16 index) { return _subResources[index]; } protected: @@ -152,16 +153,16 @@ protected: class MystAreaImageSwitch : public MystAreaActionSwitch { public: - MystAreaImageSwitch(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystArea *parent); - virtual ~MystAreaImageSwitch(); + MystAreaImageSwitch(MohawkEngine_Myst *vm, ResourceType type, Common::SeekableReadStream *rlstStream, MystArea *parent); + ~MystAreaImageSwitch() override; struct SubImage { uint16 wdib; Common::Rect rect; }; - virtual const Common::String describe() override; - virtual void drawDataToScreen() override; + const Common::String describe() override; + void drawDataToScreen() override; void drawConditionalDataToScreen(uint16 state, bool update = true); uint16 getImageSwitchVar() override; @@ -175,14 +176,14 @@ protected: class MystAreaDrag : public MystAreaImageSwitch { public: - MystAreaDrag(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystArea *parent); - virtual ~MystAreaDrag(); + MystAreaDrag(MohawkEngine_Myst *vm, ResourceType type, Common::SeekableReadStream *rlstStream, MystArea *parent); + ~MystAreaDrag() override; const Common::String describe() override; - virtual void handleMouseDown() override; - virtual void handleMouseUp() override; - virtual void handleMouseDrag() override; + void handleMouseDown() override; + void handleMouseUp() override; + void handleMouseDrag() override; uint16 getList1(uint16 index); uint16 getList2(uint16 index); @@ -214,8 +215,8 @@ protected: class MystAreaSlider : public MystAreaDrag { public: - MystAreaSlider(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystArea *parent); - virtual ~MystAreaSlider(); + MystAreaSlider(MohawkEngine_Myst *vm, ResourceType type, Common::SeekableReadStream *rlstStream, MystArea *parent); + ~MystAreaSlider() override; void handleMouseDown() override; void handleMouseUp() override; @@ -235,8 +236,8 @@ protected: class MystVideoInfo : public MystAreaDrag { public: - MystVideoInfo(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystArea *parent); - virtual ~MystVideoInfo(); + MystVideoInfo(MohawkEngine_Myst *vm, ResourceType type, Common::SeekableReadStream *rlstStream, MystArea *parent); + ~MystVideoInfo() override; void drawFrame(uint16 frame); bool pullLeverV(); @@ -247,14 +248,11 @@ protected: uint16 _numFrames; uint16 _firstFrame; Common::Rect _frameRect; - -private: - uint16 _currentFrame; }; class MystAreaHover : public MystArea { public: - MystAreaHover(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystArea *parent); + MystAreaHover(MohawkEngine_Myst *vm, ResourceType type, Common::SeekableReadStream *rlstStream, MystArea *parent); const Common::String describe() override; diff --git a/engines/mohawk/myst_graphics.cpp b/engines/mohawk/myst_graphics.cpp index 3f8d15cea5..6b43ea02bc 100644 --- a/engines/mohawk/myst_graphics.cpp +++ b/engines/mohawk/myst_graphics.cpp @@ -231,6 +231,15 @@ void MystGraphics::copyImageSectionToBackBuffer(uint16 image, Common::Rect src, MohawkSurface *mhkSurface = findImage(image); Graphics::Surface *surface = mhkSurface->getSurface(); + if (image == 2258 && _vm->getFeatures() & GF_ME) { + // In Myst ME, the image for the open red page brother door + // when the special lights are on does not have the correct width. + // We work around this issue by tweaking the destination rectangle + // so it renders at the correct position. + // The original executable does the same hack. + dest.left += 49; + } + // Make sure the image is bottom aligned in the dest rect dest.top = dest.bottom - MIN<int>(surface->h, dest.height()); diff --git a/engines/mohawk/myst_graphics.h b/engines/mohawk/myst_graphics.h index 44669bd6dc..b8217f6cfc 100644 --- a/engines/mohawk/myst_graphics.h +++ b/engines/mohawk/myst_graphics.h @@ -40,8 +40,8 @@ enum RectState { class MystGraphics : public GraphicsManager { public: - MystGraphics(MohawkEngine_Myst *vm); - ~MystGraphics(); + explicit MystGraphics(MohawkEngine_Myst *vm); + ~MystGraphics() override; void copyImageSectionToScreen(uint16 image, Common::Rect src, Common::Rect dest); void copyImageSectionToBackBuffer(uint16 image, Common::Rect src, Common::Rect dest); @@ -51,7 +51,6 @@ public: void runTransition(TransitionType type, Common::Rect rect, uint16 steps, uint16 delay); void drawRect(Common::Rect rect, RectState state); void drawLine(const Common::Point &p1, const Common::Point &p2, uint32 color); - void enableDrawingTimeSimulation(bool enable); void fadeToBlack(); void fadeFromBlack(); diff --git a/engines/mohawk/myst_scripts.cpp b/engines/mohawk/myst_scripts.cpp index ba426505e1..c1a593b430 100644 --- a/engines/mohawk/myst_scripts.cpp +++ b/engines/mohawk/myst_scripts.cpp @@ -40,6 +40,7 @@ MystScriptEntry::MystScriptEntry() { var = 0; resourceId = 0; u1 = 0; + opcode = 0; } const uint8 MystScriptParser::_stackMap[11] = { @@ -79,8 +80,10 @@ MystScriptParser::MystScriptParser(MohawkEngine_Myst *vm) : _invokingResource = nullptr; _savedCardId = 0; _savedCursorId = 0; + _savedMapCardId = 0; _tempVar = 0; _scriptNestingLevel = 0; + _startTime = 0; } MystScriptParser::~MystScriptParser() { @@ -375,7 +378,7 @@ void MystScriptParser::o_takePage(uint16 var, const ArgumentsArray &args) { cursorId = kDefaultMystCursor; } - uint16 oldPage = _globals.heldPage; + HeldPage oldPage = _globals.heldPage; // Take / drop page toggleVar(var); @@ -385,7 +388,7 @@ void MystScriptParser::o_takePage(uint16 var, const ArgumentsArray &args) { _vm->redrawArea(var); // Set new cursor - if (_globals.heldPage) + if (_globals.heldPage != kNoPage) _vm->setMainCursor(cursorId); else _vm->setMainCursor(kDefaultMystCursor); @@ -600,9 +603,11 @@ void MystScriptParser::o_copyImageToBackBuffer(uint16 var, const ArgumentsArray Common::Rect dstRect = Common::Rect(args[5], args[6], 544, 333); - if (dstRect.left == -1 || dstRect.top == -1) { - // Interpreted as full screen + if (dstRect.left == -1) { dstRect.left = 0; + } + + if (dstRect.top == -1) { dstRect.top = 0; } @@ -622,6 +627,8 @@ void MystScriptParser::o_copyImageToBackBuffer(uint16 var, const ArgumentsArray } void MystScriptParser::o_changeBackgroundSound(uint16 var, const ArgumentsArray &args) { + soundWaitStop(); + // Used on Stoneship Card 2080 // Used on Channelwood Card 3225 with argc = 8 i.e. Conditional Sound List Common::MemoryWriteStreamDynamic writeStream = Common::MemoryWriteStreamDynamic(DisposeAfterUse::YES); @@ -749,11 +756,11 @@ void MystScriptParser::o_changeCardPlaySoundDirectional(uint16 var, const Argume debugC(kDebugScript, "\tdelay between steps: %d", delayBetweenSteps); debugC(kDebugScript, "\tanimated update data size: %d", dataSize); + _vm->changeToCard(cardId, kNoTransition); + if (soundId) _vm->_sound->playEffect(soundId); - _vm->changeToCard(cardId, kNoTransition); - animatedUpdate(ArgumentsArray(args.begin() + 4, dataSize), delayBetweenSteps); } @@ -784,12 +791,16 @@ void MystScriptParser::o_soundWaitStop(uint16 var, const ArgumentsArray &args) { // Used on Selenitic Card 1191 (Maze Runner) // Used on Mechanical Card 6267 (Code Lock) // Used when Button is pushed... - while (_vm->_sound->isEffectPlaying()) + soundWaitStop(); +} + +void MystScriptParser::soundWaitStop() const { + while (_vm->_sound->isEffectPlaying() && !Engine::shouldQuit()) _vm->doFrame(); } void MystScriptParser::o_quit(uint16 var, const ArgumentsArray &args) { - _vm->quitGame(); + Engine::quitGame(); } void MystScriptParser::showMap() { @@ -800,6 +811,8 @@ void MystScriptParser::showMap() { } void MystScriptParser::o_exitMap(uint16 var, const ArgumentsArray &args) { + assert(_savedMapCardId); + _vm->changeToCard(_savedMapCardId, kTransitionCopy); } diff --git a/engines/mohawk/myst_scripts.h b/engines/mohawk/myst_scripts.h index a336f0239a..a7a840b9d3 100644 --- a/engines/mohawk/myst_scripts.h +++ b/engines/mohawk/myst_scripts.h @@ -60,7 +60,7 @@ typedef Common::SharedPtr<Common::Array<MystScriptEntry> > MystScript; class MystScriptParser { public: - MystScriptParser(MohawkEngine_Myst *vm); + explicit MystScriptParser(MohawkEngine_Myst *vm); virtual ~MystScriptParser(); void runScript(MystScript script, MystArea *invokingResource = nullptr); @@ -87,6 +87,7 @@ public: void showMap(); void animatedUpdate(const ArgumentsArray &args, uint16 delay); + void soundWaitStop() const; // Common opcodes DECLARE_OPCODE(o_toggleVar); diff --git a/engines/mohawk/myst_sound.h b/engines/mohawk/myst_sound.h index 71df23df39..953017097d 100644 --- a/engines/mohawk/myst_sound.h +++ b/engines/mohawk/myst_sound.h @@ -39,7 +39,7 @@ class MohawkEngine_Myst; class MystSound { public: - MystSound(MohawkEngine_Myst *vm); + explicit MystSound(MohawkEngine_Myst *vm); ~MystSound(); // Effect channel diff --git a/engines/mohawk/myst_stacks/channelwood.cpp b/engines/mohawk/myst_stacks/channelwood.cpp index 17df749c74..726b3d6526 100644 --- a/engines/mohawk/myst_stacks/channelwood.cpp +++ b/engines/mohawk/myst_stacks/channelwood.cpp @@ -37,7 +37,13 @@ namespace Mohawk { namespace MystStacks { Channelwood::Channelwood(MohawkEngine_Myst *vm) : - MystScriptParser(vm), _state(vm->_gameState->_channelwood) { + MystScriptParser(vm), + _state(vm->_gameState->_channelwood), + _valveVar(0), + _siriusDrawerState(0), + _doorOpened(0), + _leverPulled(false), + _leverAction(nullptr) { setupOpcodes(); } @@ -181,7 +187,7 @@ uint16 Channelwood::getVar(uint16 var) { } case 102: // Sirrus's Desk Drawer / Red Page State if (_siriusDrawerState) { - if(!(_globals.redPagesInBook & 16) && (_globals.heldPage != 11)) + if(!(_globals.redPagesInBook & 16) && (_globals.heldPage != kRedChannelwoodPage)) return 2; // Drawer Open, Red Page Present else return 1; // Drawer Open, Red Page Taken @@ -189,7 +195,7 @@ uint16 Channelwood::getVar(uint16 var) { return 0; // Drawer Closed } case 103: // Blue Page Present - return !(_globals.bluePagesInBook & 16) && (_globals.heldPage != 5); + return !(_globals.bluePagesInBook & 16) && (_globals.heldPage != kBlueChannelwoodPage); default: return MystScriptParser::getVar(var); } @@ -208,18 +214,18 @@ void Channelwood::toggleVar(uint16 var) { break; case 102: // Red page if (!(_globals.redPagesInBook & 16)) { - if (_globals.heldPage == 11) - _globals.heldPage = 0; + if (_globals.heldPage == kRedChannelwoodPage) + _globals.heldPage = kNoPage; else - _globals.heldPage = 11; + _globals.heldPage = kRedChannelwoodPage; } break; case 103: // Blue page if (!(_globals.bluePagesInBook & 16)) { - if (_globals.heldPage == 5) - _globals.heldPage = 0; + if (_globals.heldPage == kBlueChannelwoodPage) + _globals.heldPage = kNoPage; else - _globals.heldPage = 5; + _globals.heldPage = kBlueChannelwoodPage; } break; default: @@ -329,6 +335,7 @@ void Channelwood::o_pipeExtend(uint16 var, const ArgumentsArray &args) { void Channelwood::o_drawImageChangeCardAndVolume(uint16 var, const ArgumentsArray &args) { uint16 imageId = args[0]; uint16 cardId = args[1]; + uint16 volume = args.size() == 3 ? args[2] : 0; debugC(kDebugScript, "\timageId: %d", imageId); debugC(kDebugScript, "\tcardId: %d", cardId); @@ -338,8 +345,7 @@ void Channelwood::o_drawImageChangeCardAndVolume(uint16 var, const ArgumentsArra _vm->changeToCard(cardId, kTransitionPartToLeft); - if (args.size() == 3) { - uint16 volume = args[2]; + if (volume) { _vm->_sound->changeBackgroundVolume(volume); } } diff --git a/engines/mohawk/myst_stacks/channelwood.h b/engines/mohawk/myst_stacks/channelwood.h index 6b8ba9dff7..3302a8e17b 100644 --- a/engines/mohawk/myst_stacks/channelwood.h +++ b/engines/mohawk/myst_stacks/channelwood.h @@ -37,8 +37,8 @@ namespace MystStacks { class Channelwood : public MystScriptParser { public: - Channelwood(MohawkEngine_Myst *vm); - ~Channelwood(); + explicit Channelwood(MohawkEngine_Myst *vm); + ~Channelwood() override; void disablePersistentScripts() override; void runPersistentScripts() override; @@ -49,7 +49,7 @@ private: void toggleVar(uint16 var) override; bool setVarValue(uint16 var, uint16 value) override; - virtual uint16 getMap() override { return 9932; } + uint16 getMap() override { return 9932; } DECLARE_OPCODE(o_bridgeToggle); DECLARE_OPCODE(o_pipeExtend); diff --git a/engines/mohawk/myst_stacks/credits.cpp b/engines/mohawk/myst_stacks/credits.cpp index ba49ac2201..80ccf7fe9a 100644 --- a/engines/mohawk/myst_stacks/credits.cpp +++ b/engines/mohawk/myst_stacks/credits.cpp @@ -23,6 +23,7 @@ #include "mohawk/myst.h" #include "mohawk/myst_areas.h" #include "mohawk/myst_graphics.h" +#include "mohawk/cursors.h" #include "mohawk/sound.h" #include "mohawk/video.h" #include "mohawk/myst_stacks/credits.h" @@ -34,9 +35,11 @@ namespace MystStacks { // NOTE: Credits Start Card is 10000 -Credits::Credits(MohawkEngine_Myst *vm) : MystScriptParser(vm) { +Credits::Credits(MohawkEngine_Myst *vm) : + MystScriptParser(vm), + _creditsRunning(false), + _curImage(0) { setupOpcodes(); - _curImage = 0; } Credits::~Credits() { @@ -80,13 +83,17 @@ uint16 Credits::getVar(uint16 var) { case 0: // Credits Image Control return _curImage; case 1: // Credits Music Control (Good / bad ending) - return _globals.ending != 4; + return _globals.ending != kBooksDestroyed; default: return MystScriptParser::getVar(var); } } void Credits::o_runCredits(uint16 var, const ArgumentsArray &args) { + // The credits stack does not have all the cursors, reset to the default cursor. + _globals.heldPage = kNoPage; + _vm->setMainCursor(kDefaultMystCursor); + // Activate the credits _creditsRunning = true; _curImage = 0; diff --git a/engines/mohawk/myst_stacks/credits.h b/engines/mohawk/myst_stacks/credits.h index bea5381534..966d93068a 100644 --- a/engines/mohawk/myst_stacks/credits.h +++ b/engines/mohawk/myst_stacks/credits.h @@ -37,8 +37,8 @@ namespace MystStacks { class Credits : public MystScriptParser { public: - Credits(MohawkEngine_Myst *vm); - ~Credits(); + explicit Credits(MohawkEngine_Myst *vm); + ~Credits() override; void disablePersistentScripts() override; void runPersistentScripts() override; diff --git a/engines/mohawk/myst_stacks/demo.cpp b/engines/mohawk/myst_stacks/demo.cpp index 7ae55686c5..d2ba70c198 100644 --- a/engines/mohawk/myst_stacks/demo.cpp +++ b/engines/mohawk/myst_stacks/demo.cpp @@ -30,10 +30,12 @@ namespace Mohawk { namespace MystStacks { -Demo::Demo(MohawkEngine_Myst *vm) : Intro(vm) { +Demo::Demo(MohawkEngine_Myst *vm) : + Intro(vm), + _returnToMenuRunning(false), + _returnToMenuStep(0), + _returnToMenuNextTime(0) { setupOpcodes(); - - _returnToMenuStep = 0; } Demo::~Demo() { diff --git a/engines/mohawk/myst_stacks/demo.h b/engines/mohawk/myst_stacks/demo.h index 337ddaae98..acb224b6cf 100644 --- a/engines/mohawk/myst_stacks/demo.h +++ b/engines/mohawk/myst_stacks/demo.h @@ -37,8 +37,8 @@ namespace MystStacks { class Demo : public Intro { public: - Demo(MohawkEngine_Myst *vm); - ~Demo(); + explicit Demo(MohawkEngine_Myst *vm); + ~Demo() override; void disablePersistentScripts() override; void runPersistentScripts() override; @@ -52,8 +52,6 @@ private: DECLARE_OPCODE(o_returnToMenu_init); - DECLARE_OPCODE(opcode_300); - bool _returnToMenuRunning; uint16 _returnToMenuStep; // 42 uint32 _returnToMenuNextTime; // 6 diff --git a/engines/mohawk/myst_stacks/dni.cpp b/engines/mohawk/myst_stacks/dni.cpp index ba53c70037..a38b5d3857 100644 --- a/engines/mohawk/myst_stacks/dni.cpp +++ b/engines/mohawk/myst_stacks/dni.cpp @@ -34,9 +34,15 @@ namespace Mohawk { namespace MystStacks { Dni::Dni(MohawkEngine_Myst *vm) : - MystScriptParser(vm) { + MystScriptParser(vm), + _notSeenAtrus(true), + _atrusRunning(false), + _waitForLoop(false), + _atrusLeft(false), + _atrusLeftTime(0), + _loopStart(0), + _loopEnd(0) { setupOpcodes(); - _notSeenAtrus = true; } Dni::~Dni() { @@ -74,16 +80,16 @@ void Dni::runPersistentScripts() { uint16 Dni::getVar(uint16 var) { switch(var) { case 0: // Atrus Gone (from across room) - return _globals.ending == 2; + return _globals.ending == kAtrusLeaves; case 1: // Myst Book Status - if (_globals.ending != 4) - return _globals.ending == 3; + if (_globals.ending != kBooksDestroyed) + return _globals.ending == kForgotPage; else return 2; // Linkable case 2: // Music Type if (_notSeenAtrus) { _notSeenAtrus = false; - return _globals.ending != 4 && _globals.heldPage != 13; + return _globals.ending != kBooksDestroyed && _globals.heldPage != kWhitePage; } else return 2; default: @@ -98,9 +104,9 @@ void Dni::o_handPage(uint16 var, const ArgumentsArray &args) { VideoEntryPtr atrus = _vm->findVideo(_video, kDniStack); // Good ending and Atrus asked to give page - if (_globals.ending == 1 && atrus && atrus->getTime() > (uint)Audio::Timestamp(0, 6801, 600).msecs()) { - _globals.ending = 2; - _globals.heldPage = 0; + if (_globals.ending == kAtrusWantsPage && atrus && atrus->getTime() > (uint)Audio::Timestamp(0, 6801, 600).msecs()) { + _globals.ending = kAtrusLeaves; + _globals.heldPage = kNoPage; _vm->setMainCursor(kDefaultMystCursor); // Play movie end (atrus leaving) @@ -121,12 +127,13 @@ void Dni::atrusLeft_run() { atrus->moveTo(_videoPos.x, _videoPos.y); atrus->setBounds(Audio::Timestamp(0, 0, 600), Audio::Timestamp(0, 98000, 600)); + _atrusRunning = false; _waitForLoop = true; _loopStart = 73095; _loopEnd = 98000; // Good ending - _globals.ending = 4; + _globals.ending = kBooksDestroyed; _globals.bluePagesInBook = 63; _globals.redPagesInBook = 63; @@ -146,10 +153,10 @@ void Dni::loopVideo_run() { } void Dni::atrus_run() { - if (_globals.ending == 2) { + if (_globals.ending == kAtrusLeaves) { // Wait for atrus to come back _atrusLeft = true; - } else if (_globals.ending == 1) { + } else if (_globals.ending == kAtrusWantsPage) { // Atrus asking for page if (!_vm->_video->isVideoPlaying()) { _video = "atr1page"; @@ -159,8 +166,8 @@ void Dni::atrus_run() { atrus->setLooping(true); atrus->setBounds(Audio::Timestamp(0, 7388, 600), Audio::Timestamp(0, 14700, 600)); } - } else if (_globals.ending != 3 && _globals.ending != 4) { - if (_globals.heldPage == 13) { + } else if (_globals.ending != kForgotPage && _globals.ending != kBooksDestroyed) { + if (_globals.heldPage == kWhitePage) { _video = "atr1page"; _videoPos = Common::Point(215, 76); VideoEntryPtr atrus = _vm->playMovie(_video, kDniStack); @@ -172,7 +179,7 @@ void Dni::atrus_run() { _loopEnd = 14700; // Wait for page - _globals.ending = 1; + _globals.ending = kAtrusWantsPage; } else { _video = "atr1nopg"; @@ -181,12 +188,13 @@ void Dni::atrus_run() { atrus->moveTo(_videoPos.x, _videoPos.y); atrus->setBounds(Audio::Timestamp(0, 0, 600), Audio::Timestamp(0, 46175, 600)); + _atrusRunning = false; _waitForLoop = true; _loopStart = 30656; _loopEnd = 46175; // Bad ending - _globals.ending = 3; + _globals.ending = kForgotPage; } } else if (!_vm->_video->isVideoPlaying()) { VideoEntryPtr atrus = _vm->playMovie("atrwrite", kDniStack); diff --git a/engines/mohawk/myst_stacks/dni.h b/engines/mohawk/myst_stacks/dni.h index 7ba49b1936..30c9329fe6 100644 --- a/engines/mohawk/myst_stacks/dni.h +++ b/engines/mohawk/myst_stacks/dni.h @@ -37,8 +37,8 @@ namespace MystStacks { class Dni : public MystScriptParser { public: - Dni(MohawkEngine_Myst *vm); - ~Dni(); + explicit Dni(MohawkEngine_Myst *vm); + ~Dni() override; void disablePersistentScripts() override; void runPersistentScripts() override; diff --git a/engines/mohawk/myst_stacks/intro.cpp b/engines/mohawk/myst_stacks/intro.cpp index 2b431e834a..9e03713b0f 100644 --- a/engines/mohawk/myst_stacks/intro.cpp +++ b/engines/mohawk/myst_stacks/intro.cpp @@ -31,7 +31,12 @@ namespace Mohawk { namespace MystStacks { -Intro::Intro(MohawkEngine_Myst *vm) : MystScriptParser(vm) { +Intro::Intro(MohawkEngine_Myst *vm) : + MystScriptParser(vm), + _introMoviesRunning(false), + _introStep(0), + _linkBookRunning(false), + _linkBookMovie(nullptr) { setupOpcodes(); } @@ -66,7 +71,7 @@ void Intro::runPersistentScripts() { uint16 Intro::getVar(uint16 var) { switch(var) { case 0: - if (_globals.currentAge == 9 || _globals.currentAge == 10) + if (_globals.currentAge == kSirrusEnding || _globals.currentAge == kAchenarEnding) return 2; else return _globals.currentAge; diff --git a/engines/mohawk/myst_stacks/intro.h b/engines/mohawk/myst_stacks/intro.h index 938a30e691..ec78199802 100644 --- a/engines/mohawk/myst_stacks/intro.h +++ b/engines/mohawk/myst_stacks/intro.h @@ -38,8 +38,8 @@ namespace MystStacks { class Intro : public MystScriptParser { public: - Intro(MohawkEngine_Myst *vm); - ~Intro(); + explicit Intro(MohawkEngine_Myst *vm); + ~Intro() override; void disablePersistentScripts() override; void runPersistentScripts() override; diff --git a/engines/mohawk/myst_stacks/makingof.cpp b/engines/mohawk/myst_stacks/makingof.cpp index 2907c53231..4e0ce516b8 100644 --- a/engines/mohawk/myst_stacks/makingof.cpp +++ b/engines/mohawk/myst_stacks/makingof.cpp @@ -30,7 +30,8 @@ namespace Mohawk { namespace MystStacks { -MakingOf::MakingOf(MohawkEngine_Myst *vm) : MystScriptParser(vm) { +MakingOf::MakingOf(MohawkEngine_Myst *vm) : + MystScriptParser(vm) { setupOpcodes(); } diff --git a/engines/mohawk/myst_stacks/makingof.h b/engines/mohawk/myst_stacks/makingof.h index cdc64d2991..f65c9696c3 100644 --- a/engines/mohawk/myst_stacks/makingof.h +++ b/engines/mohawk/myst_stacks/makingof.h @@ -37,8 +37,8 @@ namespace MystStacks { class MakingOf : public MystScriptParser { public: - MakingOf(MohawkEngine_Myst *vm); - ~MakingOf(); + explicit MakingOf(MohawkEngine_Myst *vm); + ~MakingOf() override; void disablePersistentScripts() override; void runPersistentScripts() override; diff --git a/engines/mohawk/myst_stacks/mechanical.cpp b/engines/mohawk/myst_stacks/mechanical.cpp index bd5406a4f1..a58e278590 100644 --- a/engines/mohawk/myst_stacks/mechanical.cpp +++ b/engines/mohawk/myst_stacks/mechanical.cpp @@ -36,23 +36,53 @@ namespace Mohawk { namespace MystStacks { Mechanical::Mechanical(MohawkEngine_Myst *vm) : - MystScriptParser(vm), _state(vm->_gameState->_mechanical) { + MystScriptParser(vm), + _state(vm->_gameState->_mechanical) { setupOpcodes(); _elevatorGoingMiddle = false; _elevatorPosition = 0; + _elevatorGoingDown = 0; + _elevatorRotationSpeed = 0; + _elevatorRotationGearPosition = 0; + _elevatorRotationSoundId = 0; + _elevatorRotationLeverMoving = false; + _elevatorTooLate = false; + _elevatorInCabin = false; + _elevatorTopCounter = 0; + _elevatorNextTime = 0; _crystalLit = 0; _mystStaircaseState = false; _fortressPosition = 0; - _fortressRotationSpeed = 0; - _fortressSimulationSpeed = 0; _gearsWereRunning = false; _fortressRotationShortMovieWorkaround = false; _fortressRotationShortMovieCount = 0; _fortressRotationShortMovieLast = 0; + + _fortressRotationRunning = false; + _fortressRotationSpeed = 0; + _fortressRotationBrake = 0; + _fortressRotationGears = nullptr; + + _fortressSimulationRunning = false; + _fortressSimulationInit = false; + _fortressSimulationSpeed = 0; + _fortressSimulationBrake = 0; + _fortressSimulationStartSound1 = 0; + _fortressSimulationStartSound2 = 0; + _fortressSimulationHolo = nullptr; + _fortressSimulationStartup = nullptr; + _fortressSimulationHoloRate = 0; + + _birdSinging = false; + _birdCrankStartTime = 0; + _birdSingEndTime = 0; + _bird = nullptr; + + _snakeBox = nullptr; } Mechanical::~Mechanical() { @@ -140,12 +170,12 @@ uint16 Mechanical::getVar(uint16 var) { return _state.sirrusPanelState; case 2: // Achenar's Secret Room Crate Lid Open and Blue Page Present if (_state.achenarCrateOpened) { - if (_globals.bluePagesInBook & 4 || _globals.heldPage == 3) + if (_globals.bluePagesInBook & 4 || _globals.heldPage == kBlueMechanicalPage) return 2; else return 3; } else { - return _globals.bluePagesInBook & 4 || _globals.heldPage == 3; + return _globals.bluePagesInBook & 4 || _globals.heldPage == kBlueMechanicalPage; } case 3: // Achenar's Secret Room Crate State return _state.achenarCrateOpened; @@ -193,9 +223,9 @@ uint16 Mechanical::getVar(uint16 var) { case 22: // Crystal Lit Flag - Red return _crystalLit == 2; case 102: // Red page - return !(_globals.redPagesInBook & 4) && (_globals.heldPage != 9); + return !(_globals.redPagesInBook & 4) && (_globals.heldPage != kRedMechanicalPage); case 103: // Blue page - return !(_globals.bluePagesInBook & 4) && (_globals.heldPage != 3); + return !(_globals.bluePagesInBook & 4) && (_globals.heldPage != kBlueMechanicalPage); default: return MystScriptParser::getVar(var); } @@ -229,18 +259,18 @@ void Mechanical::toggleVar(uint16 var) { break; case 102: // Red page if (!(_globals.redPagesInBook & 4)) { - if (_globals.heldPage == 9) - _globals.heldPage = 0; + if (_globals.heldPage == kRedMechanicalPage) + _globals.heldPage = kNoPage; else - _globals.heldPage = 9; + _globals.heldPage = kRedMechanicalPage; } break; case 103: // Blue page if (!(_globals.bluePagesInBook & 4)) { - if (_globals.heldPage == 3) - _globals.heldPage = 0; + if (_globals.heldPage == kBlueMechanicalPage) + _globals.heldPage = kNoPage; else - _globals.heldPage = 3; + _globals.heldPage = kBlueMechanicalPage; } break; default: diff --git a/engines/mohawk/myst_stacks/mechanical.h b/engines/mohawk/myst_stacks/mechanical.h index 46cfe687a0..52cd7e7732 100644 --- a/engines/mohawk/myst_stacks/mechanical.h +++ b/engines/mohawk/myst_stacks/mechanical.h @@ -37,8 +37,8 @@ namespace MystStacks { class Mechanical : public MystScriptParser { public: - Mechanical(MohawkEngine_Myst *vm); - ~Mechanical(); + explicit Mechanical(MohawkEngine_Myst *vm); + ~Mechanical() override; void disablePersistentScripts() override; void runPersistentScripts() override; @@ -49,7 +49,7 @@ private: void toggleVar(uint16 var) override; bool setVarValue(uint16 var, uint16 value) override; - virtual uint16 getMap() override { return 9931; } + uint16 getMap() override { return 9931; } void birdSing_run(); void elevatorRotation_run(); diff --git a/engines/mohawk/myst_stacks/myst.cpp b/engines/mohawk/myst_stacks/myst.cpp index dbc4ff55b8..4adee5fc20 100644 --- a/engines/mohawk/myst_stacks/myst.cpp +++ b/engines/mohawk/myst_stacks/myst.cpp @@ -37,7 +37,9 @@ namespace Mohawk { namespace MystStacks { Myst::Myst(MohawkEngine_Myst *vm) : - MystScriptParser(vm), _state(_vm->_gameState->_myst) { + MystScriptParser(vm), + _state(_vm->_gameState->_myst), + _towerRotationCenter(Common::Point(383, 124)) { setupOpcodes(); // Card ID preinitialized by the engine for use by opcode 18 @@ -45,22 +47,112 @@ Myst::Myst(MohawkEngine_Myst *vm) : _savedCardId = 4329; _towerRotationBlinkLabel = false; + _towerRotationBlinkLabelCount = 0; + _towerRotationSpeed = 0; + _towerRotationMapInitialized = 0; + _towerRotationMapRunning = false; + _towerRotationMapClicked = false; + _towerRotationMapTower = nullptr; + _towerRotationMapLabel = nullptr; + _towerRotationOverSpot = false; + _libraryBookcaseChanged = false; + _libraryBookcaseMoving = false; + _libraryBookcaseMovie = nullptr; + _libraryBookcaseSoundId = 0; + + _libraryBookPagesTurning = false; + _libraryBookNumPages = 0; + _libraryBookBaseImage = 0; + _libraryBookSound1 = 0; + _libraryBookSound2 = 0; + + _libraryCombinationBookPagesTurning = false; + + for (uint i = 0; i < ARRAYSIZE(_fireplaceLines); i++) { + _fireplaceLines[i] = 0; + } + _dockVaultState = 0; + _cabinDoorOpened = 0; _cabinHandleDown = 0; _cabinMatchState = 2; _cabinGaugeMovieEnabled = false; + + _boilerPressureIncreasing = false; + _boilerPressureDecreasing = false; + _basementPressureIncreasing = false; + _basementPressureDecreasing = false; + _matchBurning = false; + _matchGoOutCnt = 0; + _matchGoOutTime = 0; + _tree = nullptr; _treeAlcove = nullptr; _treeStopped = false; _treeMinPosition = 0; + _treeMinAccessiblePosition = 0; + _treeMaxAccessiblePosition = 0; + + _imagerRunning = false; + _imagerRedButton = nullptr; + _imagerMovie = nullptr; + _imagerValidationRunning = false; + _imagerValidationCard = 0; _imagerValidationStep = 0; - _observatoryCurrentSlider = nullptr; + for (uint i = 0; i < ARRAYSIZE(_imagerSound); i++) { + _imagerSound[i] = 0; + } + _butterfliesMoviePlayed = false; _state.treeLastMoveTime = _vm->_system->getMillis(); + _rocketPianoSound = 0; + _rocketSlider1 = nullptr; + _rocketSlider2 = nullptr; + _rocketSlider3 = nullptr; + _rocketSlider4 = nullptr; + _rocketSlider5 = nullptr; + _rocketSliderSound = 0; + _rocketLeverPosition = 0; + + _generatorControlRoomRunning = false; + _generatorVoltage = _state.generatorVoltage; + + _observatoryRunning = false; + _observatoryMonthChanging = false; + _observatoryDayChanging = false; + _observatoryYearChanging = false; + _observatoryTimeChanging = false; + _observatoryVisualizer = nullptr; + _observatoryGoButton = nullptr; + _observatoryCurrentSlider = nullptr; + _observatoryDaySlider = nullptr; + _observatoryMonthSlider = nullptr; + _observatoryYearSlider = nullptr; + _observatoryTimeSlider = nullptr; + _observatoryLastTime = 0; + _observatoryNotInitialized = true; + _observatoryIncrement = 0; + + _greenBookRunning = false; + + _gullsFlying1 = false; + _gullsFlying2 = false; + _gullsFlying3 = false; + _gullsNextTime = 0; + + _courtyardBoxSound = 0; + + _clockTurningWheel = 0; + _clockWeightPosition = 0; + _clockMiddleGearMovedAlone = false; + _clockLeverPulled = false; + for (uint i = 0; i < ARRAYSIZE(_clockGearsPositions); i++) { + _clockGearsPositions[i] = 0; + } } Myst::~Myst() { @@ -149,6 +241,8 @@ void Myst::setupOpcodes() { REGISTER_OPCODE(175, Myst, o_observatoryYearSliderEndMove); REGISTER_OPCODE(176, Myst, o_observatoryTimeSliderStartMove); REGISTER_OPCODE(177, Myst, o_observatoryTimeSliderEndMove); + REGISTER_OPCODE(178, Myst, o_libraryBookPageTurnStartLeft); + REGISTER_OPCODE(179, Myst, o_libraryBookPageTurnStartRight); REGISTER_OPCODE(180, Myst, o_libraryCombinationBookStop); REGISTER_OPCODE(181, Myst, NOP); REGISTER_OPCODE(182, Myst, o_cabinMatchLight); @@ -211,6 +305,7 @@ void Myst::setupOpcodes() { void Myst::disablePersistentScripts() { _libraryBookcaseMoving = false; _generatorControlRoomRunning = false; + _libraryBookPagesTurning = false; _libraryCombinationBookPagesTurning = false; _clockTurningWheel = 0; _towerRotationMapRunning = false; @@ -242,6 +337,9 @@ void Myst::runPersistentScripts() { if (_libraryCombinationBookPagesTurning) libraryCombinationBook_run(); + if (_libraryBookPagesTurning) + libraryBook_run(); + if (_libraryBookcaseMoving) libraryBookcaseTransform_run(); @@ -308,7 +406,7 @@ uint16 Myst::getVar(uint16 var) { case 0: // Myst Library Bookcase Closed return _state.libraryBookcaseDoor; case 1: - if (_globals.ending != 4) + if (_globals.ending != kBooksDestroyed) return _state.libraryBookcaseDoor != 1; else if (_state.libraryBookcaseDoor == 1) return 2; @@ -389,13 +487,13 @@ uint16 Myst::getVar(uint16 var) { && _fireplaceLines[4] == 204 && _fireplaceLines[5] == 250; case 24: // Fireplace Blue Page Present - if (_globals.ending != 4) - return !(_globals.bluePagesInBook & 32) && (_globals.heldPage != 6); + if (_globals.ending != kBooksDestroyed) + return !(_globals.bluePagesInBook & 32) && (_globals.heldPage != kBlueFirePlacePage); else return 0; case 25: // Fireplace Red Page Present - if (_globals.ending != 4) - return !(_globals.redPagesInBook & 32) && (_globals.heldPage != 12); + if (_globals.ending != kBooksDestroyed) + return !(_globals.redPagesInBook & 32) && (_globals.heldPage != kRedFirePlacePage); else return 0; case 26: // Courtyard Image Box - Cross @@ -536,25 +634,41 @@ uint16 Myst::getVar(uint16 var) { return 10; case 79: // Stellar Observatory Date - Year #4 (Right) return (_state.observatoryYearSetting / 1) % 10; - case 80: // Stellar Observatory Hour #1 - Left ( Number 1 (0) or Blank (10)) + case 80: // Stellar Observatory Hour #1 - Left ( Hour digits can be 10 (Blank), or 0-2) + uint32 observatoryLeftMinutes; if (!observatoryIsDDMMYYYY2400()) { - if (_state.observatoryTimeSetting % (12 * 60) < (10 * 60)) + // 12 Hour Format + observatoryLeftMinutes = _state.observatoryTimeSetting % (12 * 60); + if (observatoryLeftMinutes > 59 && observatoryLeftMinutes < (10 * 60)) return 10; else return 1; } else { - if (_state.observatoryTimeSetting < (10 * 60)) - return 10; - else if (_state.observatoryTimeSetting < (20 * 60)) + // 24 Hour Format + observatoryLeftMinutes = _state.observatoryTimeSetting; + if (observatoryLeftMinutes < (10 * 60)) + return 0; + else if (observatoryLeftMinutes < (20 * 60)) return 1; else return 2; } case 81: // Stellar Observatory Hour #2 - Right - if (!observatoryIsDDMMYYYY2400()) - return ((_state.observatoryTimeSetting % (12 * 60)) / 60) % 10; - else - return (_state.observatoryTimeSetting / 60) % 10; + uint32 observatoryRightMinutes,observatoryRightHour; + if (!observatoryIsDDMMYYYY2400()) { + // 12 Hour Format + observatoryRightMinutes = _state.observatoryTimeSetting % (12 * 60); + observatoryRightHour = observatoryRightMinutes / 60; + if (observatoryRightHour % 12 == 0) + return 2; + else + return observatoryRightHour % 10; + } else { + // 24 Hour Format + observatoryRightMinutes = _state.observatoryTimeSetting; + observatoryRightHour = observatoryRightMinutes / 60; + return observatoryRightHour % 10; + } case 82: // Stellar Observatory Minutes #1 - Left return (_state.observatoryTimeSetting % 60) / 10; case 83: // Stellar Observatory Minutes #2 - Right @@ -592,13 +706,13 @@ uint16 Myst::getVar(uint16 var) { case 99: // Cabin Boiler Gas Valve Position return _state.cabinValvePosition % 6; case 102: // Red page - if (_globals.ending != 4) - return !(_globals.redPagesInBook & 1) && (_globals.heldPage != 7); + if (_globals.ending != kBooksDestroyed) + return !(_globals.redPagesInBook & 1) && (_globals.heldPage != kRedLibraryPage); else return 0; case 103: // Blue page - if (_globals.ending != 4) - return !(_globals.bluePagesInBook & 1) && (_globals.heldPage != 1); + if (_globals.ending != kBooksDestroyed) + return !(_globals.bluePagesInBook & 1) && (_globals.heldPage != kBlueLibraryPage); else return 0; case 300: // Rocket Ship Music Puzzle Slider State @@ -656,19 +770,19 @@ void Myst::toggleVar(uint16 var) { _state.rocketshipMarkerSwitch = (_state.rocketshipMarkerSwitch + 1) % 2; break; case 24: // Fireplace Blue Page - if (_globals.ending != 4 && !(_globals.bluePagesInBook & 32)) { - if (_globals.heldPage == 6) - _globals.heldPage = 0; + if (_globals.ending != kBooksDestroyed && !(_globals.bluePagesInBook & 32)) { + if (_globals.heldPage == kBlueFirePlacePage) + _globals.heldPage = kNoPage; else - _globals.heldPage = 6; + _globals.heldPage = kBlueFirePlacePage; } break; case 25: // Fireplace Red page - if (_globals.ending != 4 && !(_globals.redPagesInBook & 32)) { - if (_globals.heldPage == 12) - _globals.heldPage = 0; + if (_globals.ending != kBooksDestroyed && !(_globals.redPagesInBook & 32)) { + if (_globals.heldPage == kRedFirePlacePage) + _globals.heldPage = kNoPage; else - _globals.heldPage = 12; + _globals.heldPage = kRedFirePlacePage; } break; case 26: // Courtyard Image Box - Cross @@ -688,30 +802,30 @@ void Myst::toggleVar(uint16 var) { } break; case 41: // Vault white page - if (_globals.ending != 4) { + if (_globals.ending != kBooksDestroyed) { if (_dockVaultState == 1) { _dockVaultState = 2; - _globals.heldPage = 0; + _globals.heldPage = kNoPage; } else if (_dockVaultState == 2) { _dockVaultState = 1; - _globals.heldPage = 13; + _globals.heldPage = kWhitePage; } } break; case 102: // Red page - if (_globals.ending != 4 && !(_globals.redPagesInBook & 1)) { - if (_globals.heldPage == 7) - _globals.heldPage = 0; + if (_globals.ending != kBooksDestroyed && !(_globals.redPagesInBook & 1)) { + if (_globals.heldPage == kRedLibraryPage) + _globals.heldPage = kNoPage; else - _globals.heldPage = 7; + _globals.heldPage = kRedLibraryPage; } break; case 103: // Blue page - if (_globals.ending != 4 && !(_globals.bluePagesInBook & 1)) { - if (_globals.heldPage == 1) - _globals.heldPage = 0; + if (_globals.ending != kBooksDestroyed && !(_globals.bluePagesInBook & 1)) { + if (_globals.heldPage == kBlueLibraryPage) + _globals.heldPage = kNoPage; else - _globals.heldPage = 1; + _globals.heldPage = kBlueLibraryPage; } break; default: @@ -812,6 +926,10 @@ uint16 Myst::bookCountPages(uint16 var) { } void Myst::o_libraryBookPageTurnLeft(uint16 var, const ArgumentsArray &args) { + libraryBookPageTurnLeft(); +} + +void Myst::libraryBookPageTurnLeft() { if (_libraryBookPage - 1 >= 0) { _libraryBookPage--; @@ -826,6 +944,10 @@ void Myst::o_libraryBookPageTurnLeft(uint16 var, const ArgumentsArray &args) { } void Myst::o_libraryBookPageTurnRight(uint16 var, const ArgumentsArray &args) { + libraryBookPageTurnRight(); +} + +void Myst::libraryBookPageTurnRight() { if (_libraryBookPage + 1 < _libraryBookNumPages) { _libraryBookPage++; @@ -900,10 +1022,9 @@ void Myst::o_towerRotationStart(uint16 var, const ArgumentsArray &args) { _vm->_cursor->setCursor(700); - const Common::Point center = Common::Point(383, 124); - Common::Point end = towerRotationMapComputeCoords(center, _state.towerRotationAngle); + Common::Point end = towerRotationMapComputeCoords(_state.towerRotationAngle); towerRotationMapComputeAngle(); - towerRotationMapDrawLine(center, end); + towerRotationMapDrawLine(end, true); _vm->_sound->playEffect(5378, true); } @@ -974,7 +1095,7 @@ void Myst::o_dockVaultOpen(uint16 var, const ArgumentsArray &args) { (_state.observatoryMarkerSwitch == 1) && (_state.poolMarkerSwitch == 1) && (_state.rocketshipMarkerSwitch == 1)) { - if (_globals.heldPage != 13 && _globals.ending != 4) + if (_globals.heldPage != kWhitePage && _globals.ending != kBooksDestroyed) _dockVaultState = 2; else _dockVaultState = 1; @@ -1017,50 +1138,48 @@ void Myst::o_bookGivePage(uint16 var, const ArgumentsArray &args) { debugC(kDebugScript, "Card Id (Book Cover): %d", cardIdBookCover); debugC(kDebugScript, "SoundId (Add Page): %d", soundIdAddPage); - // No page or white page - if (!_globals.heldPage || _globals.heldPage == 13) { - _vm->changeToCard(cardIdBookCover, kTransitionDissolve); - return; - } - uint16 bookVar = 101; uint16 mask = 0; switch (_globals.heldPage) { - case 7: + case kNoPage: + case kWhitePage: + _vm->changeToCard(cardIdBookCover, kTransitionDissolve); + return; + case kRedLibraryPage: bookVar = 100; // fallthrough - case 1: + case kBlueLibraryPage: mask = 1; break; - case 8: + case kRedSeleniticPage: bookVar = 100; // fallthrough - case 2: + case kBlueSeleniticPage: mask = 2; break; - case 9: + case kRedMechanicalPage: bookVar = 100; // fallthrough - case 3: + case kBlueMechanicalPage: mask = 4; break; - case 10: + case kRedStoneshipPage: bookVar = 100; // fallthrough - case 4: + case kBlueStoneshipPage: mask = 8; break; - case 11: + case kRedChannelwoodPage: bookVar = 100; // fallthrough - case 5: + case kBlueChannelwoodPage: mask = 16; break; - case 12: + case kRedFirePlacePage: bookVar = 100; // fallthrough - case 6: + case kBlueFirePlacePage: mask = 32; break; } @@ -1082,16 +1201,16 @@ void Myst::o_bookGivePage(uint16 var, const ArgumentsArray &args) { _globals.bluePagesInBook |= mask; // Remove page from hand - _globals.heldPage = 0; + _globals.heldPage = kNoPage; _vm->_cursor->showCursor(); if (mask == 32) { // You lose! if (var == 100) - _globals.currentAge = 9; + _globals.currentAge = kSirrusEnding; else - _globals.currentAge = 10; + _globals.currentAge = kAchenarEnding; _vm->changeToCard(cardIdLose, kTransitionDissolve); } else { @@ -1521,6 +1640,7 @@ void Myst::observatoryIncrementMonth(int16 increment) { } _vm->_sound->playEffect(8500); + _vm->wait(20); } void Myst::observatoryMonthChange_run() { @@ -1587,6 +1707,7 @@ void Myst::observatoryIncrementDay(int16 increment) { } _vm->_sound->playEffect(8500); + _vm->wait(20); } void Myst::observatoryDayChange_run() { @@ -1647,6 +1768,7 @@ void Myst::observatoryIncrementYear(int16 increment) { } _vm->_sound->playEffect(8500); + _vm->wait(20); } void Myst::observatoryYearChange_run() { @@ -1712,6 +1834,7 @@ void Myst::observatoryIncrementTime(int16 increment) { } _vm->_sound->playEffect(8500); + _vm->wait(20); } void Myst::observatoryTimeChange_run() { @@ -2260,7 +2383,7 @@ void Myst::o_rocketPianoMove(uint16 var, const ArgumentsArray &args) { if (piano.contains(mouse)) { MystArea *resource = _vm->forceUpdateClickedResource(); - if (resource && resource->type == kMystAreaDrag) { + if (resource && resource->hasType(kMystAreaDrag)) { // Press new key key = static_cast<MystAreaDrag *>(resource); src = key->getSubImage(1).rect; @@ -2336,7 +2459,7 @@ void Myst::o_rocketLeverMove(uint16 var, const ArgumentsArray &args) { uint16 soundId = lever->getList2(0); if (soundId) - _vm->_sound->playEffect(soundId); + _vm->playSoundBlocking(soundId); // If rocket correctly powered if (_state.generatorVoltage == 59 && !_state.generatorBreakers) @@ -2407,6 +2530,7 @@ void Myst::observatoryUpdateMonth() { _state.observatoryMonthSetting = month; _state.observatoryMonthSlider = _observatoryMonthSlider->_pos.y; _vm->_sound->playEffect(8500); + _vm->wait(20); // Redraw digits _vm->redrawArea(73); @@ -2434,6 +2558,7 @@ void Myst::observatoryUpdateDay() { _state.observatoryDaySetting = day; _state.observatoryDaySlider = _observatoryDaySlider->_pos.y; _vm->_sound->playEffect(8500); + _vm->wait(20); // Redraw digits _vm->redrawArea(75); @@ -2462,6 +2587,7 @@ void Myst::observatoryUpdateYear() { _state.observatoryYearSetting = year; _state.observatoryYearSlider = _observatoryYearSlider->_pos.y; _vm->_sound->playEffect(8500); + _vm->wait(20); // Redraw digits _vm->redrawArea(79); @@ -2492,6 +2618,7 @@ void Myst::observatoryUpdateTime() { _state.observatoryTimeSetting = time; _state.observatoryTimeSlider = _observatoryTimeSlider->_pos.y; _vm->_sound->playEffect(8500); + _vm->wait(20); // Redraw digits _vm->redrawArea(80); @@ -2505,7 +2632,35 @@ void Myst::observatoryUpdateTime() { } } +void Myst::o_libraryBookPageTurnStartLeft(uint16 var, const ArgumentsArray &args) { + _tempVar = -1; + libraryBookPageTurnLeft(); + _startTime = _vm->_system->getMillis(); + _libraryBookPagesTurning = true; +} + +void Myst::o_libraryBookPageTurnStartRight(uint16 var, const ArgumentsArray &args) { + _tempVar = 1; + libraryBookPageTurnRight(); + _startTime = _vm->_system->getMillis(); + _libraryBookPagesTurning = true; +} + +void Myst::libraryBook_run() { + uint32 time = _vm->_system->getMillis(); + if (time >= _startTime + 500) { + if (_tempVar > 0) { + libraryBookPageTurnRight(); + _startTime = time; + } else if (_tempVar < 0) { + libraryBookPageTurnLeft(); + _startTime = time; + } + } +} + void Myst::o_libraryCombinationBookStop(uint16 var, const ArgumentsArray &args) { + _libraryBookPagesTurning = false; _libraryCombinationBookPagesTurning = false; } @@ -3021,7 +3176,7 @@ void Myst::towerRotationMap_run() { } else { // Stop blinking label _towerRotationBlinkLabel = false; - _towerRotationMapLabel->drawConditionalDataToScreen(0); + towerRotationMapRedraw(); // Blink tower _startTime = time + 500; @@ -3087,18 +3242,18 @@ uint16 Myst::towerRotationMapComputeAngle() { return angle; } -Common::Point Myst::towerRotationMapComputeCoords(const Common::Point ¢er, uint16 angle) { +Common::Point Myst::towerRotationMapComputeCoords(uint16 angle) { Common::Point end; // Polar to rect coords double radians = angle * M_PI / 180.0; - end.x = (int16)(center.x + cos(radians) * 310.0); - end.y = (int16)(center.y + sin(radians) * 310.0); + end.x = (int16)(_towerRotationCenter.x + cos(radians) * 310.0); + end.y = (int16)(_towerRotationCenter.y + sin(radians) * 310.0); return end; } -void Myst::towerRotationMapDrawLine(const Common::Point ¢er, const Common::Point &end) { +void Myst::towerRotationMapDrawLine(const Common::Point &end, bool rotationLabelVisible) { uint32 color; if (_vm->getFeatures() & GF_ME) { @@ -3133,18 +3288,22 @@ void Myst::towerRotationMapDrawLine(const Common::Point ¢er, const Common::P _towerRotationMapTower->drawConditionalDataToScreen(0, false); // Draw label - _towerRotationMapLabel->drawConditionalDataToScreen(1, false); + _towerRotationMapLabel->drawConditionalDataToScreen(rotationLabelVisible ? 1 : 0, false); // Draw line - _vm->_gfx->drawLine(center, end, color); + _vm->_gfx->drawLine(_towerRotationCenter, end, color); _vm->_gfx->copyBackBufferToScreen(rect); } void Myst::towerRotationMapRotate() { - const Common::Point center = Common::Point(383, 124); uint16 angle = towerRotationMapComputeAngle(); - Common::Point end = towerRotationMapComputeCoords(center, angle); - towerRotationMapDrawLine(center, end); + Common::Point end = towerRotationMapComputeCoords(angle); + towerRotationMapDrawLine(end, true); +} + +void Myst::towerRotationMapRedraw() { + Common::Point end = towerRotationMapComputeCoords(_state.towerRotationAngle); + towerRotationMapDrawLine(end, false); } void Myst::o_forechamberDoor_init(uint16 var, const ArgumentsArray &args) { @@ -3559,7 +3718,7 @@ void Myst::greenBook_run() { VideoEntryPtr book = _vm->playMovie(videoName, kMystStack); book->moveTo(314, 76); - if (_globals.ending != 4) { + if (_globals.ending != kBooksDestroyed) { _tempVar = 2; } else { book->setBounds(Audio::Timestamp(0, loopStart, 600), Audio::Timestamp(0, loopEnd, 600)); diff --git a/engines/mohawk/myst_stacks/myst.h b/engines/mohawk/myst_stacks/myst.h index 210012616f..ee89af115d 100644 --- a/engines/mohawk/myst_stacks/myst.h +++ b/engines/mohawk/myst_stacks/myst.h @@ -37,11 +37,11 @@ namespace MystStacks { class Myst : public MystScriptParser { public: - Myst(MohawkEngine_Myst *vm); - ~Myst(); + explicit Myst(MohawkEngine_Myst *vm); + ~Myst() override; - virtual void disablePersistentScripts() override; - virtual void runPersistentScripts() override; + void disablePersistentScripts() override; + void runPersistentScripts() override; protected: void setupOpcodes(); @@ -49,12 +49,13 @@ protected: void toggleVar(uint16 var) override; bool setVarValue(uint16 var, uint16 value) override; - virtual uint16 getMap() override { return 9934; } + uint16 getMap() override { return 9934; } void towerRotationMap_run(); virtual void libraryBookcaseTransform_run(); void generatorControlRoom_run(); void libraryCombinationBook_run(); + void libraryBook_run(); void clockWheel_run(); void matchBurn_run(); void boilerPressureIncrease_run(); @@ -137,6 +138,8 @@ protected: DECLARE_OPCODE(o_observatoryYearSliderEndMove); DECLARE_OPCODE(o_observatoryTimeSliderStartMove); DECLARE_OPCODE(o_observatoryTimeSliderEndMove); + DECLARE_OPCODE(o_libraryBookPageTurnStartLeft); + DECLARE_OPCODE(o_libraryBookPageTurnStartRight); DECLARE_OPCODE(o_libraryCombinationBookStop); DECLARE_OPCODE(o_cabinMatchLight); DECLARE_OPCODE(o_courtyardBoxEnter); @@ -207,6 +210,7 @@ protected: uint16 _rocketLeverPosition; // 296 VideoEntryPtr _rocketLinkBook; // 268 + bool _libraryBookPagesTurning; bool _libraryCombinationBookPagesTurning; int16 _libraryBookPage; // 86 uint16 _libraryBookNumPages; // 88 @@ -259,6 +263,7 @@ protected: uint16 _towerRotationSpeed; // 124 bool _towerRotationMapClicked; // 132 bool _towerRotationOverSpot; // 136 + const Common::Point _towerRotationCenter; bool _matchBurning; uint16 _matchGoOutCnt; @@ -309,6 +314,8 @@ protected: uint16 rocketSliderGetSound(uint16 pos); void rocketCheckSolution(); + void libraryBookPageTurnLeft(); + void libraryBookPageTurnRight(); void libraryCombinationBookTurnRight(); void libraryCombinationBookTurnLeft(); @@ -326,10 +333,11 @@ protected: void clockResetGear(uint16 gear); void towerRotationMapRotate(); + void towerRotationMapRedraw(); void towerRotationDrawBuildings(); uint16 towerRotationMapComputeAngle(); - Common::Point towerRotationMapComputeCoords(const Common::Point ¢er, uint16 angle); - void towerRotationMapDrawLine(const Common::Point ¢er, const Common::Point &end); + Common::Point towerRotationMapComputeCoords(uint16 angle); + void towerRotationMapDrawLine(const Common::Point &end, bool rotationLabelVisible); void boilerFireInit(); void boilerFireUpdate(bool init); diff --git a/engines/mohawk/myst_stacks/preview.cpp b/engines/mohawk/myst_stacks/preview.cpp index bc0dd04360..348a195dea 100644 --- a/engines/mohawk/myst_stacks/preview.cpp +++ b/engines/mohawk/myst_stacks/preview.cpp @@ -34,9 +34,18 @@ namespace Mohawk { namespace MystStacks { -Preview::Preview(MohawkEngine_Myst *vm) : Myst(vm) { +Preview::Preview(MohawkEngine_Myst *vm) : + Myst(vm) { setupOpcodes(); _vm->_cursor->hideCursor(); + + _libraryState = 0; + _library = nullptr; + + _speechRunning = false; + _speechStep = 0; + _currentCue = 0; + _speechNextTime = 0; } Preview::~Preview() { @@ -84,7 +93,7 @@ void Preview::o_stayHere(uint16 var, const ArgumentsArray &args) { void Preview::o_speechStop(uint16 var, const ArgumentsArray &args) { _vm->_sound->stopSpeech(); _speechRunning = false; - _globals.currentAge = 2; + _globals.currentAge = kMystLibrary; } void Preview::speechUpdateCue() { @@ -196,7 +205,7 @@ void Preview::speech_run() { _vm->changeToCard(4329, kTransitionDissolve); _speechRunning = false; - _globals.currentAge = 2; + _globals.currentAge = kMystLibrary; _vm->_cursor->showCursor(); break; diff --git a/engines/mohawk/myst_stacks/preview.h b/engines/mohawk/myst_stacks/preview.h index 6c0fe83b42..a27fec7cd1 100644 --- a/engines/mohawk/myst_stacks/preview.h +++ b/engines/mohawk/myst_stacks/preview.h @@ -39,8 +39,8 @@ namespace MystStacks { class Preview : public Myst { public: - Preview(MohawkEngine_Myst *vm); - ~Preview(); + explicit Preview(MohawkEngine_Myst *vm); + ~Preview() override; void disablePersistentScripts() override; void runPersistentScripts() override; diff --git a/engines/mohawk/myst_stacks/selenitic.cpp b/engines/mohawk/myst_stacks/selenitic.cpp index 0f1949f128..239030645c 100644 --- a/engines/mohawk/myst_stacks/selenitic.cpp +++ b/engines/mohawk/myst_stacks/selenitic.cpp @@ -36,15 +36,48 @@ namespace Mohawk { namespace MystStacks { Selenitic::Selenitic(MohawkEngine_Myst *vm) : - MystScriptParser(vm), _state(vm->_gameState->_selenitic) { + MystScriptParser(vm), + _state(vm->_gameState->_selenitic) { setupOpcodes(); + _mazeRunnerPosition = 288; _mazeRunnerDirection = 8; _mazeRunnerDoorOpened = false; + _mazeRunnerWindow = nullptr; + _mazeRunnerCompass = nullptr; + _mazeRunnerLight = nullptr; + _mazeRunnerRightButton = nullptr; + _mazeRunnerLeftButton = nullptr; + _soundReceiverRunning = false; _soundReceiverDirection = 0; _soundReceiverStartTime = 0; _soundReceiverNearBlinkCounter = 0; + _soundReceiverSigmaPressed = false; + + for (uint i = 0; i < ARRAYSIZE(_soundReceiverSources); i++) { + _soundReceiverSources[i] = nullptr; + } + + _soundReceiverCurrentSource = nullptr; + _soundReceiverPosition = nullptr; + _soundReceiverSpeed = kSoundReceiverSpeedStill; + _soundReceiverViewer = nullptr; + _soundReceiverRightButton = nullptr; + _soundReceiverLeftButton = nullptr; + _soundReceiverAngle1 = nullptr; + _soundReceiverAngle2 = nullptr; + _soundReceiverAngle3 = nullptr; + _soundReceiverAngle4 = nullptr; + _soundReceiverSigmaButton = nullptr; + + _soundLockSoundId = 0; + _soundLockSlider1 = nullptr; + _soundLockSlider2 = nullptr; + _soundLockSlider3 = nullptr; + _soundLockSlider4 = nullptr; + _soundLockSlider5 = nullptr; + _soundLockButton = nullptr; } Selenitic::~Selenitic() { @@ -157,9 +190,9 @@ uint16 Selenitic::getVar(uint16 var) { case 33: // Maze runner at entry return _mazeRunnerPosition != 288; case 102: // Red page - return !(_globals.redPagesInBook & 2) && (_globals.heldPage != 8); + return !(_globals.redPagesInBook & 2) && (_globals.heldPage != kRedSeleniticPage); case 103: // Blue page - return !(_globals.bluePagesInBook & 2) && (_globals.heldPage != 2); + return !(_globals.bluePagesInBook & 2) && (_globals.heldPage != kBlueSeleniticPage); default: return MystScriptParser::getVar(var); } @@ -190,18 +223,18 @@ void Selenitic::toggleVar(uint16 var) { break; case 102: // Red page if (!(_globals.redPagesInBook & 2)) { - if (_globals.heldPage == 8) - _globals.heldPage = 0; + if (_globals.heldPage == kRedSeleniticPage) + _globals.heldPage = kNoPage; else - _globals.heldPage = 8; + _globals.heldPage = kRedSeleniticPage; } break; case 103: // Blue page if (!(_globals.bluePagesInBook & 2)) { - if (_globals.heldPage == 2) - _globals.heldPage = 0; + if (_globals.heldPage == kBlueSeleniticPage) + _globals.heldPage = kNoPage; else - _globals.heldPage = 2; + _globals.heldPage = kBlueSeleniticPage; } break; default: @@ -636,7 +669,7 @@ void Selenitic::soundReceiverLeftRight(uint direction) { _vm->_sound->stopEffect(); _soundReceiverDirection = direction; - _soundReceiverSpeed = 1; + _soundReceiverSpeed = kSoundReceiverSpeedSlow; _soundReceiverStartTime = _vm->_system->getMillis(); soundReceiverUpdate(); @@ -918,7 +951,7 @@ void Selenitic::soundReceiver_run() { if (_soundReceiverDirection) { uint32 currentTime = _vm->_system->getMillis(); - if (_soundReceiverSpeed == 50 && currentTime > _soundReceiverStartTime + 500) { + if (_soundReceiverSpeed == kSoundReceiverSpeedFast && currentTime > _soundReceiverStartTime + 500) { soundReceiverIncreaseSpeed(); _soundReceiverStartTime = currentTime; } else if (currentTime > _soundReceiverStartTime + 1000) { @@ -926,8 +959,9 @@ void Selenitic::soundReceiver_run() { _soundReceiverStartTime = currentTime; } - if (currentTime > _soundReceiverStartTime + 100) + if (_soundReceiverSpeed > kSoundReceiverSpeedSlow || currentTime > _soundReceiverStartTime + 100) { soundReceiverUpdate(); + } } else if (!_soundReceiverSigmaPressed) { soundReceiverUpdateSound(); } @@ -936,14 +970,20 @@ void Selenitic::soundReceiver_run() { void Selenitic::soundReceiverIncreaseSpeed() { switch (_soundReceiverSpeed) { - case 1: - _soundReceiverSpeed = 10; + case kSoundReceiverSpeedStill: + // Should not happen break; - case 10: - _soundReceiverSpeed = 50; + case kSoundReceiverSpeedSlow: + _soundReceiverSpeed = kSoundReceiverSpeedNormal; + break; + case kSoundReceiverSpeedNormal: + _soundReceiverSpeed = kSoundReceiverSpeedFast; + break; + case kSoundReceiverSpeedFast: + _soundReceiverSpeed = kSoundReceiverSpeedFaster; break; - case 50: - _soundReceiverSpeed = 100; + case kSoundReceiverSpeedFaster: + // Can't go faster break; } } @@ -990,7 +1030,7 @@ uint16 Selenitic::soundReceiverCurrentSound(uint16 source, uint16 position) { if (sourceEnabled) { if (position == solution) { soundId = soundIdGood; - } else if (position > solution && position <= solution + 50) { + } else if (position > solution && position < solution + 50) { _soundReceiverNearBlinkCounter++; if (_soundReceiverNearBlinkCounter % 2) { _soundReceiverLeftButton->drawConditionalDataToScreen(2); @@ -998,7 +1038,7 @@ uint16 Selenitic::soundReceiverCurrentSound(uint16 source, uint16 position) { _soundReceiverLeftButton->drawConditionalDataToScreen(0); } soundId = soundIdNear; - } else if (position < solution && position >= solution - 50) { + } else if (position < solution && position > solution - 50) { _soundReceiverNearBlinkCounter++; if (_soundReceiverNearBlinkCounter % 2) { _soundReceiverRightButton->drawConditionalDataToScreen(2); @@ -1072,7 +1112,7 @@ void Selenitic::o_soundReceiver_init(uint16 var, const ArgumentsArray &args) { void Selenitic::o_soundLock_init(uint16 var, const ArgumentsArray &args) { for (uint i = 0; i < _vm->_resources.size(); i++) { - if (_vm->_resources[i]->type == kMystAreaSlider) { + if (_vm->_resources[i]->hasType(kMystAreaSlider)) { switch (_vm->_resources[i]->getImageSwitchVar()) { case 20: _soundLockSlider1 = _vm->getViewResource<MystAreaSlider>(i); @@ -1095,7 +1135,7 @@ void Selenitic::o_soundLock_init(uint16 var, const ArgumentsArray &args) { _soundLockSlider5->setStep(_state.soundLockSliderPositions[4]); break; } - } else if (_vm->_resources[i]->type == kMystAreaImageSwitch && _vm->_resources[i]->getImageSwitchVar() == 28) { + } else if (_vm->_resources[i]->hasType(kMystAreaImageSwitch) && _vm->_resources[i]->getImageSwitchVar() == 28) { _soundLockButton = _vm->getViewResource<MystAreaImageSwitch>(i); } } diff --git a/engines/mohawk/myst_stacks/selenitic.h b/engines/mohawk/myst_stacks/selenitic.h index 341886d20b..fc20006165 100644 --- a/engines/mohawk/myst_stacks/selenitic.h +++ b/engines/mohawk/myst_stacks/selenitic.h @@ -38,8 +38,8 @@ namespace MystStacks { class Selenitic : public MystScriptParser { public: - Selenitic(MohawkEngine_Myst *vm); - ~Selenitic(); + explicit Selenitic(MohawkEngine_Myst *vm); + ~Selenitic() override; void disablePersistentScripts() override; void runPersistentScripts() override; @@ -50,7 +50,7 @@ private: void toggleVar(uint16 var) override; bool setVarValue(uint16 var, uint16 value) override; - virtual uint16 getMap() override { return 9930; } + uint16 getMap() override { return 9930; } DECLARE_OPCODE(o_mazeRunnerMove); DECLARE_OPCODE(o_mazeRunnerSoundRepeat); @@ -74,6 +74,14 @@ private: DECLARE_OPCODE(o_mazeRunnerRight_init); DECLARE_OPCODE(o_mazeRunnerLeft_init); + enum SoundReceiverSpeed { + kSoundReceiverSpeedStill = 0, + kSoundReceiverSpeedSlow = 1, + kSoundReceiverSpeedNormal = 5, // The original has this at 10 + kSoundReceiverSpeedFast = 10, // The original has this at 50 too fast! + kSoundReceiverSpeedFaster = 13 // The original has this at 100, way too fast! + }; + void soundReceiver_run(); MystGameState::Selenitic &_state; @@ -84,7 +92,7 @@ private: MystAreaImageSwitch *_soundReceiverCurrentSource; // 112 uint16 *_soundReceiverPosition; // 116 uint16 _soundReceiverDirection; // 120 - uint16 _soundReceiverSpeed; // 122 + SoundReceiverSpeed _soundReceiverSpeed; // 122 uint32 _soundReceiverStartTime; //124 uint _soundReceiverNearBlinkCounter; MystAreaImageSwitch *_soundReceiverViewer; // 128 diff --git a/engines/mohawk/myst_stacks/slides.cpp b/engines/mohawk/myst_stacks/slides.cpp index f8c388d7bc..25d5cf480d 100644 --- a/engines/mohawk/myst_stacks/slides.cpp +++ b/engines/mohawk/myst_stacks/slides.cpp @@ -33,9 +33,15 @@ namespace Mohawk { namespace MystStacks { -Slides::Slides(MohawkEngine_Myst *vm) : MystScriptParser(vm) { +Slides::Slides(MohawkEngine_Myst *vm) : + MystScriptParser(vm) { setupOpcodes(); + _vm->_cursor->hideCursor(); + + _cardSwapEnabled = false; + _nextCardID = 0; + _nextCardTime = 0; } Slides::~Slides() { diff --git a/engines/mohawk/myst_stacks/slides.h b/engines/mohawk/myst_stacks/slides.h index 63fd9c823e..6970c3b3f0 100644 --- a/engines/mohawk/myst_stacks/slides.h +++ b/engines/mohawk/myst_stacks/slides.h @@ -37,8 +37,8 @@ namespace MystStacks { class Slides : public MystScriptParser { public: - Slides(MohawkEngine_Myst *vm); - ~Slides(); + explicit Slides(MohawkEngine_Myst *vm); + ~Slides() override; void disablePersistentScripts() override; void runPersistentScripts() override; diff --git a/engines/mohawk/myst_stacks/stoneship.cpp b/engines/mohawk/myst_stacks/stoneship.cpp index 534efe7197..cd1db68d44 100644 --- a/engines/mohawk/myst_stacks/stoneship.cpp +++ b/engines/mohawk/myst_stacks/stoneship.cpp @@ -37,10 +37,14 @@ namespace Mohawk { namespace MystStacks { Stoneship::Stoneship(MohawkEngine_Myst *vm) : - MystScriptParser(vm), _state(vm->_gameState->_stoneship) { + MystScriptParser(vm), + _state(vm->_gameState->_stoneship) { setupOpcodes(); _tunnelRunning = false; + _tunnelNextTime = 0; + _tunnelAlarmSound = 0; + _tunnelImagesCount = 0; _state.lightState = 0; _state.generatorDepletionTime = 0; @@ -61,6 +65,31 @@ Stoneship::Stoneship(MohawkEngine_Myst *vm) : _state.generatorPowerAvailable = 2; else _state.generatorPowerAvailable = 0; + + _batteryCharging = false; + _batteryDepleting = false; + _batteryNextTime = 0; + _batteryLastCharge = 0; + _batteryGaugeRunning = false; + _batteryGauge = nullptr; + + _hologramTurnedOn = 0; + _hologramDisplay = nullptr; + _hologramSelection = nullptr; + _hologramDisplayPos = 0; + + _telescopeRunning = false; + _telescopePosition = 0; + _telescopePanorama = 0; + _telescopeOldMouse = 0; + _telescopeLighthouseOff = 0; + _telescopeLighthouseOn = 0; + _telescopeLighthouseState = false; + _telescopeNexTime = 0; + + _cloudOrbMovie = nullptr; + _cloudOrbSound = 0; + _cloudOrbStopSound = 0; } Stoneship::~Stoneship() { @@ -251,9 +280,9 @@ uint16 Stoneship::getVar(uint16 var) { return 0; // Closed } case 102: // Red page - return !(_globals.redPagesInBook & 8) && (_globals.heldPage != 10); + return !(_globals.redPagesInBook & 8) && (_globals.heldPage != kRedStoneshipPage); case 103: // Blue page - return !(_globals.bluePagesInBook & 8) && (_globals.heldPage != 4); + return !(_globals.bluePagesInBook & 8) && (_globals.heldPage != kBlueStoneshipPage); default: return MystScriptParser::getVar(var); } @@ -305,18 +334,18 @@ void Stoneship::toggleVar(uint16 var) { break; case 102: // Red page if (!(_globals.redPagesInBook & 8)) { - if (_globals.heldPage == 10) - _globals.heldPage = 0; + if (_globals.heldPage == kRedStoneshipPage) + _globals.heldPage = kNoPage; else - _globals.heldPage = 10; + _globals.heldPage = kRedStoneshipPage; } break; case 103: // Blue page if (!(_globals.bluePagesInBook & 8)) { - if (_globals.heldPage == 4) - _globals.heldPage = 0; + if (_globals.heldPage == kBlueStoneshipPage) + _globals.heldPage = kNoPage; else - _globals.heldPage = 4; + _globals.heldPage = kBlueStoneshipPage; } break; default: @@ -399,7 +428,7 @@ void Stoneship::o_pumpTurnOff(uint16 var, const ArgumentsArray &args) { for (uint i = 0; i < _vm->_resources.size(); i++) { MystArea *resource = _vm->_resources[i]; - if (resource->type == kMystAreaImageSwitch && resource->getImageSwitchVar() == buttonVar) { + if (resource->hasType(kMystAreaImageSwitch) && resource->getImageSwitchVar() == buttonVar) { static_cast<MystAreaImageSwitch *>(resource)->drawConditionalDataToScreen(0, true); break; } diff --git a/engines/mohawk/myst_stacks/stoneship.h b/engines/mohawk/myst_stacks/stoneship.h index dca7ce8fd3..64f58ec427 100644 --- a/engines/mohawk/myst_stacks/stoneship.h +++ b/engines/mohawk/myst_stacks/stoneship.h @@ -37,8 +37,8 @@ namespace MystStacks { class Stoneship : public MystScriptParser { public: - Stoneship(MohawkEngine_Myst *vm); - ~Stoneship(); + explicit Stoneship(MohawkEngine_Myst *vm); + ~Stoneship() override; void disablePersistentScripts() override; void runPersistentScripts() override; @@ -49,7 +49,7 @@ private: void toggleVar(uint16 var) override; bool setVarValue(uint16 var, uint16 value) override; - virtual uint16 getMap() override { return 9933; } + uint16 getMap() override { return 9933; } DECLARE_OPCODE(o_pumpTurnOff); DECLARE_OPCODE(o_brotherDoorOpen); diff --git a/engines/mohawk/myst_state.cpp b/engines/mohawk/myst_state.cpp index 213a976413..c65ece55d5 100644 --- a/engines/mohawk/myst_state.cpp +++ b/engines/mohawk/myst_state.cpp @@ -41,10 +41,11 @@ MystSaveMetadata::MystSaveMetadata() { saveHour = 0; saveMinute = 0; totalPlayTime = 0; + autoSave = false; } bool MystSaveMetadata::sync(Common::Serializer &s) { - static const Common::Serializer::Version kCurrentVersion = 1; + static const Common::Serializer::Version kCurrentVersion = 2; if (!s.syncVersion(kCurrentVersion)) { return false; @@ -57,10 +58,13 @@ bool MystSaveMetadata::sync(Common::Serializer &s) { s.syncAsByte(saveMinute); s.syncString(saveDescription); s.syncAsUint32LE(totalPlayTime); + s.syncAsByte(autoSave, 2); return true; } +const int MystGameState::kAutoSaveSlot = 0; + MystGameState::MystGameState(MohawkEngine_Myst *vm, Common::SaveFileManager *saveFileMan) : _vm(vm), _saveFileMan(saveFileMan) { // Most of the variables are zero at game start. memset(&_globals, 0, sizeof(_globals)); @@ -78,8 +82,10 @@ MystGameState::MystGameState(MohawkEngine_Myst *vm, Common::SaveFileManager *sav // Unknown _globals.u0 = 2; // Current Age / Stack - Start in Myst - _globals.currentAge = 7; + _globals.currentAge = kMystStart; + _globals.heldPage = kNoPage; _globals.u1 = 1; + _globals.ending = kDniNotVisited; // Library Bookcase Door - Default to Up _myst.libraryBookcaseDoor = 1; @@ -121,13 +127,13 @@ bool MystGameState::load(int slot) { // Set our default cursor _vm->_cursor->showCursor(); - if (_globals.heldPage == 0 || _globals.heldPage > 13) + if (_globals.heldPage == kNoPage) _vm->setMainCursor(kDefaultMystCursor); - else if (_globals.heldPage < 7) + else if (_globals.heldPage < kRedLibraryPage) //A blue page is held _vm->setMainCursor(kBluePageCursor); - else if (_globals.heldPage < 13) + else if (_globals.heldPage < kWhitePage) //A red page is held _vm->setMainCursor(kRedPageCursor); - else // if (globals.heldPage == 13) + else _vm->setMainCursor(kWhitePageCursor); return true; @@ -178,12 +184,12 @@ void MystGameState::loadMetadata(int slot) { delete metadataFile; } -bool MystGameState::save(int slot, const Common::String &desc) { +bool MystGameState::save(int slot, const Common::String &desc, bool autoSave) { if (!saveState(slot)) { return false; } - updateMetadateForSaving(desc); + updateMetadateForSaving(desc, autoSave); return saveMetadata(slot); } @@ -214,7 +220,7 @@ Common::String MystGameState::buildMetadataFilename(int slot) { return Common::String::format("myst-%03d.mym", slot); } -void MystGameState::updateMetadateForSaving(const Common::String &desc) { +void MystGameState::updateMetadateForSaving(const Common::String &desc, bool autoSave) { // Update save creation info TimeDate t; g_system->getTimeAndDate(t); @@ -225,6 +231,7 @@ void MystGameState::updateMetadateForSaving(const Common::String &desc) { _metadata.saveMinute = t.tm_min; _metadata.saveDescription = desc; _metadata.totalPlayTime = _vm->getTotalPlayTime(); + _metadata.autoSave = autoSave; } bool MystGameState::saveMetadata(int slot) { @@ -249,12 +256,43 @@ bool MystGameState::saveMetadata(int slot) { return true; } +bool MystGameState::isAutoSaveAllowed() { + // Open autosave slot and see if it an autosave + // Autosaving will be enabled if it is an autosave or if there is no save in that slot + + Common::String dataFilename = buildSaveFilename(kAutoSaveSlot); + Common::ScopedPtr<Common::InSaveFile> dataFile(g_system->getSavefileManager()->openForLoading(dataFilename)); + if (!dataFile) { // Cannot load non-meta file, enable autosave + return true; + } + + Common::String metaFilename = buildMetadataFilename(kAutoSaveSlot); + Common::ScopedPtr<Common::InSaveFile> metadataFile(g_system->getSavefileManager()->openForLoading(metaFilename)); + if (!metadataFile) { // Can load non-meta file, but not metafile, could be a save from the original, disable autosave + return false; + } + + Common::Serializer m(metadataFile.get(), nullptr); + + // Read the metadata file + Mohawk::MystSaveMetadata metadata; + if (!metadata.sync(m)) { // the save in the autosave slot is corrupted, enable autosave + return true; + } + + return metadata.autoSave; +} + SaveStateDescriptor MystGameState::querySaveMetaInfos(int slot) { // Open the metadata file Common::String filename = buildMetadataFilename(slot); Common::InSaveFile *metadataFile = g_system->getSavefileManager()->openForLoading(filename); + + SaveStateDescriptor desc; + desc.setWriteProtectedFlag(slot == kAutoSaveSlot); + if (!metadataFile) { - return SaveStateDescriptor(); + return desc; } Common::Serializer m(metadataFile, nullptr); @@ -263,16 +301,23 @@ SaveStateDescriptor MystGameState::querySaveMetaInfos(int slot) { Mohawk::MystSaveMetadata metadata; if (!metadata.sync(m)) { delete metadataFile; - return SaveStateDescriptor(); + return desc; } // Set the save description - SaveStateDescriptor desc; desc.setDescription(metadata.saveDescription); desc.setSaveDate(metadata.saveYear, metadata.saveMonth, metadata.saveDay); desc.setSaveTime(metadata.saveHour, metadata.saveMinute); desc.setPlayTime(metadata.totalPlayTime); - desc.setThumbnail(Graphics::loadThumbnail(*metadataFile)); + if (metadata.autoSave) // Allow non-saves to be deleted, but not autosaves + desc.setDeletableFlag(slot != kAutoSaveSlot); + + Graphics::Surface *thumbnail; + if (!Graphics::loadThumbnail(*metadataFile, thumbnail)) { + delete metadataFile; + return desc; + } + desc.setThumbnail(thumbnail); delete metadataFile; diff --git a/engines/mohawk/myst_state.h b/engines/mohawk/myst_state.h index 7d5f3f7102..28bbf60d63 100644 --- a/engines/mohawk/myst_state.h +++ b/engines/mohawk/myst_state.h @@ -47,14 +47,61 @@ struct MystSaveMetadata { uint32 totalPlayTime; + bool autoSave; + Common::String saveDescription; MystSaveMetadata(); bool sync(Common::Serializer &s); }; +// Page being held +enum HeldPage { + kNoPage = 0, + kBlueLibraryPage = 1, + kBlueSeleniticPage = 2, + kBlueMechanicalPage = 3, + kBlueStoneshipPage = 4, + kBlueChannelwoodPage = 5, + kBlueFirePlacePage = 6, + kRedLibraryPage = 7, + kRedSeleniticPage = 8, + kRedMechanicalPage = 9, + kRedStoneshipPage = 10, + kRedChannelwoodPage = 11, + kRedFirePlacePage = 12, + kWhitePage = 13 +}; + +// Age the player is in +enum ActiveAge { + kSelenitic = 0, + kStoneship = 1, + kMystLibrary = 2, + kMechanical = 3, + kChannelwood = 4, + kIntro = 5, + kDni = 6, + kMystStart = 7, + kCredits = 8, + kSirrusEnding = 9, + kAchenarEnding = 10 +}; + +// Various states that Atrus can be in when in Dni +enum DniEnding { + kDniNotVisited = 0, // Player hasn't been to Dni/K'veer yet + kAtrusWantsPage = 1, // Player is in Dni with the white page + kAtrusLeaves = 2, // Atrus leaves Dni after receiving the white page + kForgotPage = 3, // Player has entered Dni without bringing the white page + kBooksDestroyed = 4 // Atrus returns to Dni after previously leaving + // and destroying the books of his sons +}; + class MystGameState { public: + static const int kAutoSaveSlot; + MystGameState(MohawkEngine_Myst*, Common::SaveFileManager*); ~MystGameState(); @@ -62,7 +109,8 @@ public: static Common::String querySaveDescription(int slot); bool load(int slot); - bool save(int slot, const Common::String &desc); + bool save(int slot, const Common::String &desc, bool autoSave); + bool isAutoSaveAllowed(); static void deleteSave(int slot); void addZipDest(uint16 stack, uint16 view); @@ -80,14 +128,14 @@ public: */ struct Globals { uint16 u0; - uint16 currentAge; - uint16 heldPage; + ActiveAge currentAge; + HeldPage heldPage; uint16 u1; uint16 transitions; uint16 zipMode; uint16 redPagesInBook; uint16 bluePagesInBook; - uint16 ending; + DniEnding ending; } _globals; /* 50 Myst Specific Variables : @@ -297,7 +345,7 @@ private: bool loadState(int slot); void loadMetadata(int slot); bool saveState(int slot); - void updateMetadateForSaving(const Common::String &desc); + void updateMetadateForSaving(const Common::String &desc, bool autoSave); bool saveMetadata(int slot); // The values in these regions are lists of VIEW resources diff --git a/engines/mohawk/resource.cpp b/engines/mohawk/resource.cpp index b0ae8dd6ea..ea44ca7879 100644 --- a/engines/mohawk/resource.cpp +++ b/engines/mohawk/resource.cpp @@ -32,7 +32,7 @@ namespace Mohawk { // Base Archive code Archive::Archive() { - _stream = 0; + _stream = nullptr; } Archive::~Archive() { @@ -57,7 +57,7 @@ bool Archive::openFile(const Common::String &fileName) { void Archive::close() { _types.clear(); - delete _stream; _stream = 0; + delete _stream; _stream = nullptr; } bool Archive::hasResource(uint32 tag, uint16 id) const { diff --git a/engines/mohawk/resource.h b/engines/mohawk/resource.h index 12c5a139e4..809c55da1d 100644 --- a/engines/mohawk/resource.h +++ b/engines/mohawk/resource.h @@ -93,10 +93,6 @@ namespace Mohawk { #define ID_BMAP MKTAG('B','M','A','P') // Old Mohawk Bitmap #define ID_BCOD MKTAG('B','C','O','D') // Book Code -// JamesMath Resource FourCC's -#define ID_TANM MKTAG('t','A','N','M') // Animation? -#define ID_TMFO MKTAG('t','M','F','O') // ??? - // CSTime Resource FourCC's #define ID_CINF MKTAG('C','I','N','F') // Case Info #define ID_CONV MKTAG('C','O','N','V') // Conversation @@ -112,10 +108,6 @@ namespace Mohawk { #define ID_DATA MKTAG('D','a','t','a') // Game Sound Chunk #define ID_CUE MKTAG('C','u','e','#') // Game Sound Chunk -// Mohawk MIDI Tags -#define ID_MIDI MKTAG('M','I','D','I') // Game Sound (Third Tag), instead of WAVE -#define ID_PRG MKTAG('P','r','g','#') // MIDI Patch - // Common Resource FourCC's #define ID_TBMP MKTAG('t','B','M','P') // Standard Mohawk Bitmap #define ID_TWAV MKTAG('t','W','A','V') // Standard Mohawk Sound @@ -170,25 +162,25 @@ protected: class MohawkArchive : public Archive { public: MohawkArchive() : Archive() {} - ~MohawkArchive() {} + ~MohawkArchive() override {} - bool openStream(Common::SeekableReadStream *stream); + bool openStream(Common::SeekableReadStream *stream) override; }; class LivingBooksArchive_v1 : public Archive { public: LivingBooksArchive_v1() : Archive() {} - ~LivingBooksArchive_v1() {} + ~LivingBooksArchive_v1() override {} - bool openStream(Common::SeekableReadStream *stream); + bool openStream(Common::SeekableReadStream *stream) override; }; class DOSArchive_v2 : public Archive { public: DOSArchive_v2() : Archive() {} - ~DOSArchive_v2() {} + ~DOSArchive_v2() override {} - bool openStream(Common::SeekableReadStream *stream); + bool openStream(Common::SeekableReadStream *stream) override; }; } // End of namespace Mohawk diff --git a/engines/mohawk/resource_cache.cpp b/engines/mohawk/resource_cache.cpp index 0c19934278..ab1758b673 100644 --- a/engines/mohawk/resource_cache.cpp +++ b/engines/mohawk/resource_cache.cpp @@ -64,7 +64,7 @@ void ResourceCache::add(uint32 tag, uint16 id, Common::SeekableReadStream *data) // Returns NULL if not found Common::SeekableReadStream *ResourceCache::search(uint32 tag, uint16 id) { if (!enabled) - return NULL; + return nullptr; debugC(kDebugCache, "Searching for tag 0x%04X id %d", tag, id); @@ -79,7 +79,7 @@ Common::SeekableReadStream *ResourceCache::search(uint32 tag, uint16 id) { } debugC(kDebugCache, "tag 0x%04X id %d not found", tag, id); - return NULL; + return nullptr; } } // End of namespace Mohawk diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp index 0bf4024057..8139c1a92f 100644 --- a/engines/mohawk/riven.cpp +++ b/engines/mohawk/riven.cpp @@ -71,6 +71,7 @@ MohawkEngine_Riven::MohawkEngine_Riven(OSystem *syst, const MohawkGameDescriptio _optionsDialog = nullptr; _card = nullptr; _inventory = nullptr; + _lastSaveTime = 0; DebugMan.addDebugChannel(kRivenDebugScript, "Script", "Track Script Execution"); DebugMan.addDebugChannel(kRivenDebugPatches, "Patches", "Track Script Patching"); @@ -240,6 +241,12 @@ void MohawkEngine_Riven::doFrame() { loadGameStateAndDisplayError(_optionsDialog->getLoadSlot()); if (_optionsDialog->getSaveSlot() >= 0) saveGameStateAndDisplayError(_optionsDialog->getSaveSlot(), _optionsDialog->getSaveDescription()); + + if (hasGameEnded()) { + // Attempt to autosave before exiting + tryAutoSaving(); + } + _gfx->setTransitionMode((RivenTransitionMode) _vars["transitionmode"]); _card->initializeZipMode(); break; @@ -281,6 +288,11 @@ void MohawkEngine_Riven::doFrame() { break; } break; + case Common::EVENT_QUIT: + case Common::EVENT_RTL: + // Attempt to autosave before exiting + tryAutoSaving(); + break; default: break; } @@ -294,6 +306,10 @@ void MohawkEngine_Riven::doFrame() { _scriptMan->runQueuedScripts(); } + if (shouldPerformAutoSave(_lastSaveTime)) { + tryAutoSaving(); + } + _inventory->onFrame(); // Update the screen once per frame @@ -575,7 +591,7 @@ void MohawkEngine_Riven::loadGameStateAndDisplayError(int slot) { } Common::Error MohawkEngine_Riven::saveGameState(int slot, const Common::String &desc) { - return _saveLoad->saveGame(slot, desc); + return _saveLoad->saveGame(slot, desc, false); } void MohawkEngine_Riven::saveGameStateAndDisplayError(int slot, const Common::String &desc) { @@ -589,6 +605,23 @@ void MohawkEngine_Riven::saveGameStateAndDisplayError(int slot, const Common::St } } +void MohawkEngine_Riven::tryAutoSaving() { + if (!canSaveGameStateCurrently()) { + return; // Can't save right now, try again on the next frame + } + + _lastSaveTime = _system->getMillis(); + + if (!_saveLoad->isAutoSaveAllowed()) { + return; // Can't autosave ever, try again after the next autosave delay + } + + Common::Error saveError = _saveLoad->saveGame(RivenSaveLoad::kAutoSaveSlot, "Autosave", true); + if (saveError.getCode() != Common::kNoError) + warning("Attempt to autosave has failed."); +} + + void MohawkEngine_Riven::addZipVisitedCard(uint16 cardId, uint16 cardNameId) { Common::String cardName = getStack()->getName(kCardNames, cardNameId); if (cardName.empty()) diff --git a/engines/mohawk/riven.h b/engines/mohawk/riven.h index 86a431170f..3dc19c7ee4 100644 --- a/engines/mohawk/riven.h +++ b/engines/mohawk/riven.h @@ -79,11 +79,11 @@ typedef Common::HashMap<Common::String, uint32, Common::IgnoreCase_Hash, Common: class MohawkEngine_Riven : public MohawkEngine { protected: - Common::Error run(); + Common::Error run() override; public: MohawkEngine_Riven(OSystem *syst, const MohawkGameDescription *gamedesc); - virtual ~MohawkEngine_Riven(); + ~MohawkEngine_Riven() override; RivenVideoManager *_video; RivenSoundManager *_sound; @@ -95,13 +95,13 @@ public: // Display debug rectangles around the hotspots bool _showHotspots; - GUI::Debugger *getDebugger(); + GUI::Debugger *getDebugger() override; - bool canLoadGameStateCurrently(); - bool canSaveGameStateCurrently(); - Common::Error loadGameState(int slot); - Common::Error saveGameState(int slot, const Common::String &desc); - bool hasFeature(EngineFeature f) const; + bool canLoadGameStateCurrently() override; + bool canSaveGameStateCurrently() override; + Common::Error loadGameState(int slot) override; + Common::Error saveGameState(int slot, const Common::String &desc) override; + bool hasFeature(EngineFeature f) const override; void doFrame(); @@ -121,6 +121,7 @@ private: RivenStack *_stack; bool _gameEnded; + uint32 _lastSaveTime; // Variables void initVars(); @@ -152,6 +153,7 @@ public: // Save / Load void runLoadDialog(); void runSaveDialog(); + void tryAutoSaving(); void loadGameStateAndDisplayError(int slot); void saveGameStateAndDisplayError(int slot, const Common::String &desc); diff --git a/engines/mohawk/riven_card.cpp b/engines/mohawk/riven_card.cpp index 7a5cd617a6..031a88163b 100644 --- a/engines/mohawk/riven_card.cpp +++ b/engines/mohawk/riven_card.cpp @@ -72,8 +72,18 @@ void RivenCard::loadCardResource(uint16 id) { void RivenCard::applyPatches(uint16 id) { uint32 globalId = _vm->getStack()->getCardGlobalId(id); - // Apply properties patches + applyPropertiesPatch8EB7(globalId); + applyPropertiesPatch2E76(globalId); + // Apply script patches + for (uint i = 0; i < _scripts.size(); i++) { + _scripts[i].script->applyCardPatches(_vm, globalId, _scripts[i].type, 0xFFFF); + } + + applyPropertiesPatch22118(globalId); +} + +void RivenCard::applyPropertiesPatch8EB7(uint32 globalId) { // On Jungle Island on the back side of the "beetle" gate, the forward hotspot // is always enabled, preventing keyboard navigation from automatically opening // the gate. @@ -138,7 +148,9 @@ void RivenCard::applyPatches(uint16 id) { debugC(kRivenDebugPatches, "Applied fix always enabled forward hotspot in card %x", globalId); } +} +void RivenCard::applyPropertiesPatch2E76(uint32 globalId) { // In Gehn's office, after having encountered him once before and coming back // with the trap book, the draw update script of card 1 tries to switch to // card 2 while still loading card 1. Switching cards is not allowed during @@ -179,7 +191,7 @@ void RivenCard::applyPatches(uint16 id) { // activatePLST(6); // break; // } - // break; + // break; // case 2: // activatePLST(5); // break; @@ -243,10 +255,138 @@ void RivenCard::applyPatches(uint16 id) { debugC(kRivenDebugPatches, "Applied invalid card change during screen update (1/2) to card %x", globalId); // The second part of this patch is in the script patches } +} - // Apply script patches - for (uint i = 0; i < _scripts.size(); i++) { - _scripts[i].script->applyCardPatches(_vm, globalId, _scripts[i].type, 0xFFFF); +void RivenCard::applyPropertiesPatch22118(uint32 globalId) { + // On Temple Island, near the steam valve closest to the bridge to Boiler island, + // the background sound on the view offering a view to the bridge does + // not properly reflect the valve's position. + // + // The sound is always that of steam going through the pipe when the bridge is + // down. When the valve points up, the sound should be that of steam escaping + // through the top of the pipe. + // + // Script before patch: + // == Script 0 == + // type: CardLoad + // switch (bbigbridge) { + // case 0: + // switch (tbookvalve) { + // case 0: + // activatePLST(2); + // activateSLST(1); + // break; + // } + // break; + // } + // switch (bbigbridge) { + // case 0: + // switch (tbookvalve) { + // case 1: + // activatePLST(2); + // activateSLST(2); + // break; + // } + // break; + // } + // switch (bbigbridge) { + // case 1: + // switch (tbookvalve) { + // case 0: + // activatePLST(1); + // activateSLST(2); + // break; + // } + // break; + // } + // switch (bbigbridge) { + // case 1: + // switch (tbookvalve) { + // case 1: + // activatePLST(1); + // activateSLST(2); + // break; + // } + // break; + // } + // + // + // Script after patch: + // == Script 0 == + // type: CardLoad + // switch (bbigbridge) { + // case 0: + // switch (tbookvalve) { + // case 0: + // activatePLST(2); + // break; + // } + // break; + // } + // switch (bbigbridge) { + // case 0: + // switch (tbookvalve) { + // case 1: + // activatePLST(2); + // break; + // } + // break; + // } + // switch (bbigbridge) { + // case 1: + // switch (tbookvalve) { + // case 0: + // activatePLST(1); + // break; + // } + // break; + // } + // switch (bbigbridge) { + // case 1: + // switch (tbookvalve) { + // case 1: + // activatePLST(1); + // break; + // } + // break; + // } + // switch (tbookvalve) { + // case 0: + // activateSLST(1); + // break; + // case 1: + // activateSLST(2); + // break; + // } + if (globalId == 0x22118) { + uint16 tBookValveVariable = _vm->getStack()->getIdFromName(kVariableNames, "tbookvalve"); + uint16 patchData[] = { + 1, // Command count in script + kRivenCommandSwitch, + 2, // Unused + tBookValveVariable, + 2, // Branches count + + 0, // tbookvalve == 0 branch (steam escaping at the top of the pipe) + 1, // Command count in sub-script + kRivenCommandActivateSLST, + 1, // Argument count + 1, // Steam leaking sound id + + 1, // tbookvalve == 1 branch (steam going to the left pipe) + 1, // Command count in sub-script + kRivenCommandActivateSLST, + 1, // Argument count + 2, // Steam in pipe sound id + }; + + RivenScriptPtr patchScript = _vm->_scriptMan->readScriptFromData(patchData, ARRAYSIZE(patchData)); + + // Append the patch to the existing script + RivenScriptPtr loadScript = getScript(kCardLoadScript); + loadScript += patchScript; + + debugC(kRivenDebugPatches, "Applied incorrect steam sounds (2/2) to card %x", globalId); } } diff --git a/engines/mohawk/riven_card.h b/engines/mohawk/riven_card.h index b11363c916..2b6a8d41bf 100644 --- a/engines/mohawk/riven_card.h +++ b/engines/mohawk/riven_card.h @@ -152,6 +152,9 @@ private: void loadCardHotspotEnableList(uint16 id); void loadCardWaterEffectList(uint16 id); void applyPatches(uint16 id); + void applyPropertiesPatch8EB7(uint32 globalId); + void applyPropertiesPatch2E76(uint32 globalId); + void applyPropertiesPatch22118(uint32 globalId); void setCurrentCardVariable(); RivenScriptPtr getScript(uint16 scriptType) const; diff --git a/engines/mohawk/riven_graphics.cpp b/engines/mohawk/riven_graphics.cpp index d0a5c05636..d4ea8fc703 100644 --- a/engines/mohawk/riven_graphics.cpp +++ b/engines/mohawk/riven_graphics.cpp @@ -101,7 +101,7 @@ public: _lastCopyArea = makeDirectionalInitalArea(); } - virtual bool drawFrame(uint32 elapsed) override { + bool drawFrame(uint32 elapsed) override { Common::Rect copyArea; switch (_type) { case kRivenTransitionWipeLeft: @@ -162,7 +162,7 @@ public: complete = false; } - virtual bool drawFrame(uint32 elapsed) override { + bool drawFrame(uint32 elapsed) override { Common::Rect newArea; switch (_type) { case kRivenTransitionPanLeft: @@ -264,7 +264,7 @@ public: _timeBased = false; } - virtual bool drawFrame(uint32 elapsed) override { + bool drawFrame(uint32 elapsed) override { assert(_effectScreen->format == _mainScreen->format); assert(_effectScreen->format == _system->getScreenFormat()); @@ -303,7 +303,22 @@ public: } }; -RivenGraphics::RivenGraphics(MohawkEngine_Riven* vm) : GraphicsManager(), _vm(vm) { +RivenGraphics::RivenGraphics(MohawkEngine_Riven* vm) : + GraphicsManager(), + _vm(vm), + _screenUpdateNesting(0), + _screenUpdateRunning(false), + _enableCardUpdateScript(true), + _scheduledTransition(kRivenTransitionNone), + _dirtyScreen(false), + _creditsImage(302), + _creditsPos(0), + _transitionMode(kRivenTransitionModeFastest), + _transitionOffset(-1), + _waterEffect(nullptr), + _fliesEffect(nullptr), + _transitionFrames(0), + _transitionDuration(0) { _bitmapDecoder = new MohawkBitmap(); // Restrict ourselves to a single pixel format to simplify the effects implementation @@ -317,20 +332,6 @@ RivenGraphics::RivenGraphics(MohawkEngine_Riven* vm) : GraphicsManager(), _vm(vm _effectScreen = new Graphics::Surface(); _effectScreen->create(608, 392, _pixelFormat); - - _screenUpdateNesting = 0; - _screenUpdateRunning = false; - _enableCardUpdateScript = true; - _scheduledTransition = kRivenTransitionNone; - _dirtyScreen = false; - - _creditsImage = 302; - _creditsPos = 0; - - _transitionMode = kRivenTransitionModeFastest; - _transitionOffset = -1; - _waterEffect = nullptr; - _fliesEffect = nullptr; } RivenGraphics::~RivenGraphics() { @@ -339,7 +340,8 @@ RivenGraphics::~RivenGraphics() { _mainScreen->free(); delete _mainScreen; delete _bitmapDecoder; - delete _fliesEffect; + clearFliesEffect(); + clearWaterEffect(); } MohawkSurface *RivenGraphics::decodeImage(uint16 id) { diff --git a/engines/mohawk/riven_graphics.h b/engines/mohawk/riven_graphics.h index 31ab84038c..7b831c5609 100644 --- a/engines/mohawk/riven_graphics.h +++ b/engines/mohawk/riven_graphics.h @@ -54,8 +54,8 @@ enum RivenTransitionMode { class RivenGraphics : public GraphicsManager { public: - RivenGraphics(MohawkEngine_Riven *vm); - ~RivenGraphics(); + explicit RivenGraphics(MohawkEngine_Riven *vm); + ~RivenGraphics() override; // Screen updates void beginScreenUpdate(); @@ -94,11 +94,11 @@ public: // Credits void beginCredits(); void updateCredits(); - uint getCurCreditsImage() { return _creditsImage; } + uint getCurCreditsImage() const { return _creditsImage; } protected: - MohawkSurface *decodeImage(uint16 id); - MohawkEngine *getVM() { return (MohawkEngine *)_vm; } + MohawkSurface *decodeImage(uint16 id) override; + MohawkEngine *getVM() override { return (MohawkEngine *)_vm; } private: MohawkEngine_Riven *_vm; diff --git a/engines/mohawk/riven_inventory.h b/engines/mohawk/riven_inventory.h index 7c75b48374..8ce5f7ebd2 100644 --- a/engines/mohawk/riven_inventory.h +++ b/engines/mohawk/riven_inventory.h @@ -36,7 +36,7 @@ class MohawkEngine_Riven; */ class RivenInventory { public: - RivenInventory(MohawkEngine_Riven *vm); + explicit RivenInventory(MohawkEngine_Riven *vm); virtual ~RivenInventory(); /** Handle a click event in the inventory area */ diff --git a/engines/mohawk/riven_saveload.cpp b/engines/mohawk/riven_saveload.cpp index 7165166d8f..e8d29a0c24 100644 --- a/engines/mohawk/riven_saveload.cpp +++ b/engines/mohawk/riven_saveload.cpp @@ -38,10 +38,11 @@ RivenSaveMetadata::RivenSaveMetadata() { saveHour = 0; saveMinute = 0; totalPlayTime = 0; + autoSave = false; } bool RivenSaveMetadata::sync(Common::Serializer &s) { - static const Common::Serializer::Version kCurrentVersion = 1; + static const Common::Serializer::Version kCurrentVersion = 2; if (!s.syncVersion(kCurrentVersion)) { return false; @@ -54,10 +55,13 @@ bool RivenSaveMetadata::sync(Common::Serializer &s) { s.syncAsByte(saveMinute); s.syncString(saveDescription); s.syncAsUint32BE(totalPlayTime); + s.syncAsByte(autoSave, 2); return true; } +const int RivenSaveLoad::kAutoSaveSlot = 0; + RivenSaveLoad::RivenSaveLoad(MohawkEngine_Riven *vm, Common::SaveFileManager *saveFileMan) : _vm(vm), _saveFileMan(saveFileMan) { } @@ -105,22 +109,25 @@ Common::String RivenSaveLoad::querySaveDescription(const int slot) { SaveStateDescriptor RivenSaveLoad::querySaveMetaInfos(const int slot) { Common::String filename = buildSaveFilename(slot); Common::InSaveFile *loadFile = g_system->getSavefileManager()->openForLoading(filename); + SaveStateDescriptor descriptor; + descriptor.setWriteProtectedFlag(slot == kAutoSaveSlot); + if (!loadFile) { - return SaveStateDescriptor(); + return descriptor; } MohawkArchive mhk; if (!mhk.openStream(loadFile)) { - return SaveStateDescriptor(); + return descriptor; } if (!mhk.hasResource(ID_META, 1)) { - return SaveStateDescriptor(); + return descriptor; } Common::SeekableReadStream *metaStream = mhk.getResource(ID_META, 1); if (!metaStream) { - return SaveStateDescriptor(); + return descriptor; } Common::Serializer serializer = Common::Serializer(metaStream, nullptr); @@ -128,14 +135,15 @@ SaveStateDescriptor RivenSaveLoad::querySaveMetaInfos(const int slot) { RivenSaveMetadata metadata; if (!metadata.sync(serializer)) { delete metaStream; - return SaveStateDescriptor(); + return descriptor; } - SaveStateDescriptor descriptor; descriptor.setDescription(metadata.saveDescription); descriptor.setPlayTime(metadata.totalPlayTime); descriptor.setSaveDate(metadata.saveYear, metadata.saveMonth, metadata.saveDay); descriptor.setSaveTime(metadata.saveHour, metadata.saveMinute); + if (metadata.autoSave) // Allow non-saves to be deleted, but not autosaves + descriptor.setDeletableFlag(slot != kAutoSaveSlot); delete metaStream; @@ -148,13 +156,51 @@ SaveStateDescriptor RivenSaveLoad::querySaveMetaInfos(const int slot) { return descriptor; } - descriptor.setThumbnail(Graphics::loadThumbnail(*thmbStream)); + Graphics::Surface *thumbnail; + if (!Graphics::loadThumbnail(*thmbStream, thumbnail)) { + return descriptor; + } + descriptor.setThumbnail(thumbnail); delete thmbStream; return descriptor; } +bool RivenSaveLoad::isAutoSaveAllowed() { + // Open autosave slot and see if it an autosave + // Autosaving will be enabled if it is an autosave or if there is no save in that slot + + Common::String filename = buildSaveFilename(kAutoSaveSlot); + Common::InSaveFile *loadFile = g_system->getSavefileManager()->openForLoading(filename); + if (!loadFile) { + return true; // There is no save in the autosave slot, enable autosave + } + + MohawkArchive mhk; + if (!mhk.openStream(loadFile)) { + return true; // Corrupt save, enable autosave + } + + if (!mhk.hasResource(ID_META, 1)) { + return false; // don't autosave over saves that don't have a meta section (like saves from the original) + } + + Common::ScopedPtr<Common::SeekableReadStream> metaStream(mhk.getResource(ID_META, 1)); + if (!metaStream) { + return true; // corrupt save, enable autosave + } + + Common::Serializer serializer = Common::Serializer(metaStream.get(), nullptr); + + RivenSaveMetadata metadata; + if (!metadata.sync(serializer)) { + return true; // corrupt save, enable autosave + } + + return metadata.autoSave; +} + Common::Error RivenSaveLoad::loadGame(const int slot) { if (_vm->getFeatures() & GF_DEMO) // Don't load games in the demo return Common::kNoError; @@ -375,7 +421,7 @@ Common::MemoryWriteStreamDynamic *RivenSaveLoad::genTHMBSection() const { return stream; } -Common::MemoryWriteStreamDynamic *RivenSaveLoad::genMETASection(const Common::String &desc) const { +Common::MemoryWriteStreamDynamic *RivenSaveLoad::genMETASection(const Common::String &desc, bool autoSave) const { Common::MemoryWriteStreamDynamic *stream = new Common::MemoryWriteStreamDynamic(DisposeAfterUse::YES); Common::Serializer serializer = Common::Serializer(nullptr, stream); @@ -390,12 +436,13 @@ Common::MemoryWriteStreamDynamic *RivenSaveLoad::genMETASection(const Common::St metadata.saveMinute = t.tm_min; metadata.saveDescription = desc; metadata.totalPlayTime = _vm->getTotalPlayTime(); + metadata.autoSave = autoSave; metadata.sync(serializer); return stream; } -Common::Error RivenSaveLoad::saveGame(const int slot, const Common::String &description) { +Common::Error RivenSaveLoad::saveGame(const int slot, const Common::String &description, bool autoSave) { // NOTE: This code is designed to only output a Mohawk archive // for a Riven saved game. It's hardcoded to do this because // (as of right now) this is the only place in the engine @@ -411,7 +458,7 @@ Common::Error RivenSaveLoad::saveGame(const int slot, const Common::String &desc debug (0, "Saving game to \'%s\'", filename.c_str()); - Common::MemoryWriteStreamDynamic *metaSection = genMETASection(description); + Common::MemoryWriteStreamDynamic *metaSection = genMETASection(description, autoSave); Common::MemoryWriteStreamDynamic *nameSection = genNAMESection(); Common::MemoryWriteStreamDynamic *thmbSection = genTHMBSection(); Common::MemoryWriteStreamDynamic *varsSection = genVARSSection(); diff --git a/engines/mohawk/riven_saveload.h b/engines/mohawk/riven_saveload.h index 34bfbdc434..1432505b02 100644 --- a/engines/mohawk/riven_saveload.h +++ b/engines/mohawk/riven_saveload.h @@ -49,6 +49,8 @@ struct RivenSaveMetadata { uint32 totalPlayTime; + bool autoSave; + Common::String saveDescription; RivenSaveMetadata(); @@ -57,11 +59,14 @@ struct RivenSaveMetadata { class RivenSaveLoad { public: + static const int kAutoSaveSlot; + RivenSaveLoad(MohawkEngine_Riven*, Common::SaveFileManager*); ~RivenSaveLoad(); Common::Error loadGame(const int slot); - Common::Error saveGame(const int slot, const Common::String &description); + Common::Error saveGame(const int slot, const Common::String &description, bool autoSave); + bool isAutoSaveAllowed(); static void deleteSave(const int slot); static SaveStateDescriptor querySaveMetaInfos(const int slot); @@ -74,7 +79,7 @@ private: static Common::String buildSaveFilename(const int slot); Common::MemoryWriteStreamDynamic *genNAMESection(); - Common::MemoryWriteStreamDynamic *genMETASection(const Common::String &desc) const; + Common::MemoryWriteStreamDynamic *genMETASection(const Common::String &desc, bool autoSave) const; Common::MemoryWriteStreamDynamic *genTHMBSection() const; Common::MemoryWriteStreamDynamic *genVARSSection(); Common::MemoryWriteStreamDynamic *genVERSSection(); diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp index 18a3597086..c12989bca6 100644 --- a/engines/mohawk/riven_scripts.cpp +++ b/engines/mohawk/riven_scripts.cpp @@ -345,6 +345,24 @@ void RivenScript::applyCardPatches(MohawkEngine_Riven *vm, uint32 cardGlobalId, debugC(kRivenDebugPatches, "Applied invalid card change during screen update (2/2) to card %x", cardGlobalId); } + // First part of the patch to fix the invalid steam sounds + // when looking at the Boiler island bridge from Temple island. + // The second part is in the card patches. + if (cardGlobalId == 0x22118 && scriptType == kCardLoadScript) { + shouldApplyPatches = true; + + // Remove all the activateSLST calls. + // Fixed calls will be added back in the second part of the patch. + for (uint i = 0; i < _commands.size(); i++) { + if (_commands[i]->getType() == kRivenCommandActivateSLST) { + _commands.remove_at(i); + break; + } + } + + debugC(kRivenDebugPatches, "Applied incorrect steam sounds (1/2) to card %x", cardGlobalId); + } + if (shouldApplyPatches) { for (uint i = 0; i < _commands.size(); i++) { _commands[i]->applyCardPatches(cardGlobalId, scriptType, hotspotId); diff --git a/engines/mohawk/riven_sound.h b/engines/mohawk/riven_sound.h index ce2ddbcbe8..fd14dee151 100644 --- a/engines/mohawk/riven_sound.h +++ b/engines/mohawk/riven_sound.h @@ -65,7 +65,7 @@ struct SLSTRecord { */ class RivenSoundManager { public: - RivenSoundManager(MohawkEngine_Riven *vm); + explicit RivenSoundManager(MohawkEngine_Riven *vm); ~RivenSoundManager(); /** diff --git a/engines/mohawk/riven_stacks/jspit.cpp b/engines/mohawk/riven_stacks/jspit.cpp index eeff81005b..b1f15a10c0 100644 --- a/engines/mohawk/riven_stacks/jspit.cpp +++ b/engines/mohawk/riven_stacks/jspit.cpp @@ -369,7 +369,7 @@ int JSpit::jspitElevatorLoop() { return 1; } } - + return 0; } diff --git a/engines/mohawk/riven_stacks/pspit.cpp b/engines/mohawk/riven_stacks/pspit.cpp index a3134754b4..3c8b4306e4 100644 --- a/engines/mohawk/riven_stacks/pspit.cpp +++ b/engines/mohawk/riven_stacks/pspit.cpp @@ -83,7 +83,22 @@ void PSpit::catherineIdleTimer() { void PSpit::xpisland990_elevcombo(const ArgumentArray &args) { // Play button sound based on args[0] _vm->_sound->playSound(args[0] + 5); + _vm->_cursor->hideCursor(); _vm->delay(500); + _vm->_cursor->showCursor(); + + // If the user released the mouse button during the wait time, the mouse up event + // is not forwarded to the game script handler. The button appears to be down + // until the user moves the mouse outside of the button hotspot. + // This happens with the original engine as well. + // To work around this issue we run the mouse up script if the mouse is not + // pressed anymore at this point. + if (!mouseIsDown()) { + Common::String buttonName = Common::String::format("combo%d", args[0]); + RivenHotspot *button = _vm->getCard()->getHotspotByName(buttonName); + RivenScriptPtr mouseUpScript = button->getScript(kMouseUpScript); + _vm->_scriptMan->runScript(mouseUpScript, false); + } // It is impossible to get here if Gehn is not trapped. However, // the original also disallows brute forcing the ending if you have diff --git a/engines/mohawk/riven_video.h b/engines/mohawk/riven_video.h index 5b5bf30fd2..ae5a266e04 100644 --- a/engines/mohawk/riven_video.h +++ b/engines/mohawk/riven_video.h @@ -130,7 +130,7 @@ private: class RivenVideoManager { public: - RivenVideoManager(MohawkEngine_Riven *vm); + explicit RivenVideoManager(MohawkEngine_Riven *vm); ~RivenVideoManager(); void updateMovies(); diff --git a/engines/mohawk/sound.cpp b/engines/mohawk/sound.cpp index 81d83fc7b9..7bd6c63539 100644 --- a/engines/mohawk/sound.cpp +++ b/engines/mohawk/sound.cpp @@ -158,11 +158,16 @@ Audio::RewindableAudioStream *makeMohawkWaveStream(Common::SeekableReadStream *s // The sound in the CD version of Riven is encoded in Intel DVI ADPCM // The sound in the DVD version of Riven is encoded in MPEG-2 Layer II or Intel DVI ADPCM if (dataChunk.encoding == kCodecRaw) { - byte flags = Audio::FLAG_UNSIGNED; + byte flags = 0; if (dataChunk.channels == 2) flags |= Audio::FLAG_STEREO; + if (dataChunk.bitsPerSample == 16) + flags |= Audio::FLAG_16BITS; + else + flags |= Audio::FLAG_UNSIGNED; + return Audio::makeRawStream(dataChunk.audioData, dataChunk.sampleRate, flags); } else if (dataChunk.encoding == kCodecADPCM) { uint32 blockAlign = dataChunk.channels * dataChunk.bitsPerSample / 8; @@ -180,51 +185,18 @@ Audio::RewindableAudioStream *makeMohawkWaveStream(Common::SeekableReadStream *s return nullptr; } -Sound::Sound(MohawkEngine* vm) : _vm(vm) { - _midiDriver = NULL; - _midiParser = NULL; - _midiData = NULL; - initMidi(); +Sound::Sound(MohawkEngine* vm) : + _vm(vm) { } Sound::~Sound() { stopSound(); - - if (_midiParser) { - _midiParser->unloadMusic(); - delete _midiParser; - } - - if (_midiDriver) { - _midiDriver->close(); - delete _midiDriver; - } - - if (_midiData) - delete[] _midiData; -} - -void Sound::initMidi() { - if (!(_vm->getFeatures() & GF_HASMIDI)) - return; - - // Let's get our MIDI parser/driver - _midiParser = MidiParser::createParser_SMF(); - _midiDriver = MidiDriver::createMidi(MidiDriver::detectDevice(MDT_ADLIB|MDT_MIDI)); - - // Set up everything! - _midiDriver->open(); - _midiParser->setMidiDriver(_midiDriver); - _midiParser->setTimerRate(_midiDriver->getBaseTempo()); } Audio::RewindableAudioStream *Sound::makeAudioStream(uint16 id, CueList *cueList) { - Audio::RewindableAudioStream *audStream = NULL; + Audio::RewindableAudioStream *audStream = nullptr; switch (_vm->getGameType()) { - case GType_ZOOMBINI: - audStream = makeMohawkWaveStream(_vm->getResource(ID_SND, id)); - break; case GType_LIVINGBOOKSV1: audStream = makeLivingBooksWaveStream_v1(_vm->getResource(ID_WAV, id)); break; @@ -261,55 +233,7 @@ Audio::SoundHandle *Sound::playSound(uint16 id, byte volume, bool loop, CueList return &handle->handle; } - return NULL; -} - -void Sound::playMidi(uint16 id) { - uint32 idTag; - if (!(_vm->getFeatures() & GF_HASMIDI)) { - warning ("Attempting to play MIDI in a game without MIDI"); - return; - } - - assert(_midiDriver && _midiParser); - - _midiParser->unloadMusic(); - if (_midiData) - delete[] _midiData; - - Common::SeekableReadStream *midi = _vm->getResource(ID_TMID, id); - - idTag = midi->readUint32BE(); - assert(idTag == ID_MHWK); - midi->readUint32BE(); // Skip size - idTag = midi->readUint32BE(); - assert(idTag == ID_MIDI); - - _midiData = new byte[midi->size() - 12]; // Enough to cover MThd/Prg#/MTrk - - // Read the MThd Data - midi->read(_midiData, 14); - - // TODO: Load patches from the Prg# section... skip it for now. - idTag = midi->readUint32BE(); - assert(idTag == ID_PRG); - midi->skip(midi->readUint32BE()); - - // Read the MTrk Data - uint32 mtrkSize = midi->size() - midi->pos(); - midi->read(_midiData + 14, mtrkSize); - - delete midi; - - // Now, play it :) - if (!_midiParser->loadMusic(_midiData, 14 + mtrkSize)) - error ("Could not play MIDI music from tMID %04x\n", id); - - _midiDriver->setTimerCallback(_midiParser, MidiParser::timerCallback); -} - -void Sound::stopMidi() { - _midiParser->unloadMusic(); + return nullptr; } Audio::RewindableAudioStream *Sound::makeLivingBooksWaveStream_v1(Common::SeekableReadStream *stream) { diff --git a/engines/mohawk/sound.h b/engines/mohawk/sound.h index 11fd004513..15ee249177 100644 --- a/engines/mohawk/sound.h +++ b/engines/mohawk/sound.h @@ -102,13 +102,11 @@ class MohawkEngine; class Sound { public: - Sound(MohawkEngine *vm); + explicit Sound(MohawkEngine *vm); ~Sound(); // Generic sound functions Audio::SoundHandle *playSound(uint16 id, byte volume = Audio::Mixer::kMaxChannelVolume, bool loop = false, CueList *cueList = NULL); - void playMidi(uint16 id); - void stopMidi(); void stopSound(); void stopSound(uint16 id); bool isPlaying(uint16 id); @@ -117,17 +115,12 @@ public: private: MohawkEngine *_vm; - MidiDriver *_midiDriver; - MidiParser *_midiParser; - byte *_midiData; static Audio::RewindableAudioStream *makeLivingBooksWaveStream_v1(Common::SeekableReadStream *stream); - void initMidi(); Common::Array<SndHandle> _handles; SndHandle *getHandle(); - Audio::RewindableAudioStream *makeAudioStream(uint16 id, CueList *cueList = NULL); - uint16 convertMystID(uint16 id); + Audio::RewindableAudioStream *makeAudioStream(uint16 id, CueList *cueList = nullptr); }; } // End of namespace Mohawk diff --git a/engines/mohawk/video.cpp b/engines/mohawk/video.cpp index 994e219b03..ae20a59370 100644 --- a/engines/mohawk/video.cpp +++ b/engines/mohawk/video.cpp @@ -38,7 +38,7 @@ namespace Mohawk { -VideoEntry::VideoEntry() : _video(0), _id(-1), _x(0), _y(0), _loop(false), _enabled(true) { +VideoEntry::VideoEntry() : _video(nullptr), _id(-1), _x(0), _y(0), _loop(false), _enabled(true) { } VideoEntry::VideoEntry(Video::VideoDecoder *video, const Common::String &fileName) : _video(video), _fileName(fileName), _id(-1), _x(0), _y(0), _loop(false), _enabled(true) { @@ -53,7 +53,7 @@ VideoEntry::~VideoEntry() { void VideoEntry::close() { delete _video; - _video = 0; + _video = nullptr; } bool VideoEntry::endOfVideo() const { @@ -230,7 +230,7 @@ bool VideoManager::drawNextFrame(VideoEntryPtr videoEntry) { return false; } - Graphics::Surface *convertedFrame = 0; + Graphics::Surface *convertedFrame = nullptr; Graphics::PixelFormat pixelFormat = _vm->_system->getScreenFormat(); if (frame->format != pixelFormat) { diff --git a/engines/mohawk/video.h b/engines/mohawk/video.h index 1f8b93d467..fdc55a51ab 100644 --- a/engines/mohawk/video.h +++ b/engines/mohawk/video.h @@ -237,7 +237,7 @@ typedef Common::SharedPtr<VideoEntry> VideoEntryPtr; class VideoManager { public: - VideoManager(MohawkEngine *vm); + explicit VideoManager(MohawkEngine *vm); virtual ~VideoManager(); // Generic movie functions diff --git a/engines/mohawk/view.cpp b/engines/mohawk/view.cpp index 70d20270a5..ec73723971 100644 --- a/engines/mohawk/view.cpp +++ b/engines/mohawk/view.cpp @@ -63,10 +63,10 @@ void Feature::setNodeDefaults(Feature *prev, Feature *next) { _prev = prev; _next = next; - _moveProc = NULL; - _drawProc = NULL; - _doneProc = NULL; - _frameProc = NULL; + _moveProc = nullptr; + _drawProc = nullptr; + _doneProc = nullptr; + _frameProc = nullptr; _data.bounds = Common::Rect(); _data.clipRect = Common::Rect(); @@ -333,7 +333,7 @@ void NewFeature::finishResetFeatureScript() { } View::View(MohawkEngine *vm) : _vm(vm) { - _currentModule = NULL; + _currentModule = nullptr; _backgroundId = 0xffff; @@ -393,7 +393,7 @@ void View::setModule(Module *module) { delete _currentModule; } - _currentModule = NULL; + _currentModule = nullptr; if (module) { _currentModule = module; @@ -572,7 +572,7 @@ Feature *View::getFeaturePtr(uint16 id) { return node; } - return NULL; + return nullptr; } uint16 View::getNewFeatureId() { @@ -591,8 +591,8 @@ void View::removeFeature(Feature *feature, bool free) { feature->_prev->_next = feature->_next; feature->_next->_prev = feature->_prev; - feature->_next = NULL; - feature->_prev = NULL; + feature->_next = nullptr; + feature->_prev = nullptr; if (free) delete feature; @@ -619,21 +619,21 @@ Feature *View::pointOnFeature(bool topdown, uint32 flags, Common::Point pos) { else curr = curr->_next; } - return NULL; + return nullptr; } void View::sortView() { Feature *base = _rootNode; Feature *next = base->_next; - Feature *otherRoot = NULL; - Feature *otherBase = NULL; - Feature *objectRoot = NULL; - Feature *objectBase = NULL; - Feature *staticRoot = NULL; - Feature *staticBase = NULL; + Feature *otherRoot = nullptr; + Feature *otherBase = nullptr; + Feature *objectRoot = nullptr; + Feature *objectBase = nullptr; + Feature *staticRoot = nullptr; + Feature *staticBase = nullptr; // Remove all features. - base->_next = NULL; + base->_next = nullptr; // Iterate through all the previous features, placing them in the appropriate list. while (next) { @@ -645,33 +645,33 @@ void View::sortView() { // so we insert this node directly after the current base. base->_next = curr; curr->_prev = base; - curr->_next = NULL; + curr->_next = nullptr; base = base->_next; } else if (curr->_flags & kFeatureSortStatic) { // Insert this node into the list of static objects. if (staticBase) { staticBase->_next = curr; curr->_prev = staticBase; - curr->_next = NULL; + curr->_next = nullptr; staticBase = curr; } else { staticBase = curr; staticRoot = curr; - curr->_prev = NULL; - curr->_next = NULL; + curr->_prev = nullptr; + curr->_next = nullptr; } } else if (curr->_flags & kFeatureObjectMask) { // This is == 1 or == 2 in old code. // Insert this node into the list of objects. if (objectRoot) { objectBase->_next = curr; curr->_prev = objectBase; - curr->_next = NULL; + curr->_next = nullptr; objectBase = curr; } else { objectBase = curr; objectRoot = curr; - curr->_prev = NULL; - curr->_next = NULL; + curr->_prev = nullptr; + curr->_next = nullptr; } } else { if (!(curr->_flags & kFeatureOldSortForeground)) @@ -681,13 +681,13 @@ void View::sortView() { if (otherRoot) { otherBase->_next = curr; curr->_prev = otherBase; - curr->_next = NULL; + curr->_next = nullptr; otherBase = curr; } else { otherBase = curr; otherRoot = curr; - curr->_prev = NULL; - curr->_next = NULL; + curr->_prev = nullptr; + curr->_next = nullptr; } } } @@ -700,7 +700,7 @@ void View::sortView() { base->_next = prev; prev->_prev = base; base = base->_next; - base->_next = NULL; + base->_next = nullptr; } // Add the other features on top.. @@ -711,12 +711,12 @@ void View::sortView() { Feature *View::sortOneList(Feature *root) { if (!root) - return NULL; + return nullptr; // Save the next feature and then clear the list. Feature *curr = root->_next; - root->_next = NULL; - root->_prev = NULL; + root->_next = nullptr; + root->_prev = nullptr; // Iterate over all the features. while (curr) { @@ -735,7 +735,7 @@ Feature *View::sortOneList(Feature *root) { // This is the end of the list: add ourselves there. check->_next = prev; prev->_prev = check; - prev->_next = NULL; + prev->_next = nullptr; break; } } else { @@ -779,7 +779,7 @@ Feature *View::mergeLists(Feature *root, Feature *mergeRoot) { check = check->_next; check->_next = prev; prev->_prev = check; - prev->_next = NULL; + prev->_next = nullptr; continue; } @@ -802,7 +802,7 @@ Feature *View::mergeLists(Feature *root, Feature *mergeRoot) { // We're at the end of the list, so we have to go here. check->_next = prev; prev->_prev = check; - prev->_next = NULL; + prev->_next = nullptr; base = prev; break; } diff --git a/engines/mortevielle/mortevielle.h b/engines/mortevielle/mortevielle.h index 42d70fcb37..5d00547f4e 100644 --- a/engines/mortevielle/mortevielle.h +++ b/engines/mortevielle/mortevielle.h @@ -33,7 +33,6 @@ #include "common/random.h" #include "common/rect.h" #include "common/stack.h" -#include "engines/advancedDetector.h" #include "engines/engine.h" #include "common/error.h" #include "graphics/surface.h" diff --git a/engines/mortevielle/saveload.cpp b/engines/mortevielle/saveload.cpp index 3065d6c551..078338a0b5 100644 --- a/engines/mortevielle/saveload.cpp +++ b/engines/mortevielle/saveload.cpp @@ -92,8 +92,10 @@ bool SavegameManager::loadSavegame(const Common::String &filename) { if (!strncmp(&buffer[0], &SAVEGAME_ID[0], 4)) { // Yes, it is, so skip over the savegame header SavegameHeader header; - readSavegameHeader(stream, header); - delete header.thumbnail; + if (!readSavegameHeader(stream, header)) { + delete stream; + return false; + } } else { stream->seek(0); } @@ -208,9 +210,7 @@ void SavegameManager::writeSavegameHeader(Common::OutSaveFile *out, const Common out->writeSint16LE(td.tm_min); } -bool SavegameManager::readSavegameHeader(Common::InSaveFile *in, SavegameHeader &header) { - header.thumbnail = NULL; - +WARN_UNUSED_RESULT bool SavegameManager::readSavegameHeader(Common::InSaveFile *in, SavegameHeader &header, bool skipThumbnail) { // Get the savegame version header.version = in->readByte(); @@ -221,9 +221,9 @@ bool SavegameManager::readSavegameHeader(Common::InSaveFile *in, SavegameHeader header.saveName += ch; // Get the thumbnail - header.thumbnail = Graphics::loadThumbnail(*in); - if (!header.thumbnail) + if (!Graphics::loadThumbnail(*in, header.thumbnail, skipThumbnail)) { return false; + } // Read in save date/time header.saveYear = in->readSint16LE(); @@ -240,7 +240,6 @@ SaveStateList SavegameManager::listSaves(const Common::String &target) { pattern += ".###"; Common::StringArray files = g_system->getSavefileManager()->listSavefiles(pattern); - sort(files.begin(), files.end()); // Sort (hopefully ensuring we are sorted numerically..) SaveStateList saveList; for (Common::StringArray::const_iterator file = files.begin(); file != files.end(); ++file) { @@ -264,7 +263,6 @@ SaveStateList SavegameManager::listSaves(const Common::String &target) { validFlag = readSavegameHeader(in, header); if (validFlag) { - delete header.thumbnail; saveDescription = header.saveName; } } else if (file->size() == 497) { @@ -282,6 +280,7 @@ SaveStateList SavegameManager::listSaves(const Common::String &target) { } } + Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator()); return saveList; } @@ -311,7 +310,10 @@ SaveStateDescriptor SavegameManager::querySaveMetaInfos(const Common::String &fi } else { // Get the savegame header information SavegameHeader header; - readSavegameHeader(f, header); + if (!readSavegameHeader(f, header, false)) { + delete f; + return SaveStateDescriptor(); + } delete f; // Create the return descriptor diff --git a/engines/mortevielle/saveload.h b/engines/mortevielle/saveload.h index a0de05b920..18042d9fb4 100644 --- a/engines/mortevielle/saveload.h +++ b/engines/mortevielle/saveload.h @@ -33,6 +33,7 @@ #include "graphics/palette.h" #include "graphics/scaler.h" #include "graphics/thumbnail.h" +#include "engines/savestate.h" #define SAVEGAME_VERSION 1 @@ -65,7 +66,7 @@ public: Common::Error saveGame(int slot); void writeSavegameHeader(Common::OutSaveFile *out, const Common::String &saveName); - static bool readSavegameHeader(Common::InSaveFile *in, SavegameHeader &header); + WARN_UNUSED_RESULT static bool readSavegameHeader(Common::InSaveFile *in, SavegameHeader &header, bool skipThumbnail = true); static SaveStateList listSaves(const Common::String &target); static SaveStateDescriptor querySaveMetaInfos(const Common::String &fileName); }; diff --git a/engines/neverhood/detection.cpp b/engines/neverhood/detection.cpp index 46605bb2f7..920d149659 100644 --- a/engines/neverhood/detection.cpp +++ b/engines/neverhood/detection.cpp @@ -271,7 +271,7 @@ SaveStateList NeverhoodMetaEngine::listSaves(const char *target) const { if (slotNum >= 0 && slotNum <= 999) { Common::InSaveFile *in = saveFileMan->openForLoading(file->c_str()); if (in) { - if (Neverhood::NeverhoodEngine::readSaveHeader(in, false, header) == Neverhood::NeverhoodEngine::kRSHENoError) { + if (Neverhood::NeverhoodEngine::readSaveHeader(in, header) == Neverhood::NeverhoodEngine::kRSHENoError) { saveList.push_back(SaveStateDescriptor(slotNum, header.description)); } delete in; @@ -302,7 +302,7 @@ SaveStateDescriptor NeverhoodMetaEngine::querySaveMetaInfos(const char *target, Neverhood::NeverhoodEngine::SaveHeader header; Neverhood::NeverhoodEngine::kReadSaveHeaderError error; - error = Neverhood::NeverhoodEngine::readSaveHeader(in, true, header); + error = Neverhood::NeverhoodEngine::readSaveHeader(in, header, false); delete in; if (error == Neverhood::NeverhoodEngine::kRSHENoError) { diff --git a/engines/neverhood/menumodule.cpp b/engines/neverhood/menumodule.cpp index e3996a2507..b64f4dcdd2 100644 --- a/engines/neverhood/menumodule.cpp +++ b/engines/neverhood/menumodule.cpp @@ -291,7 +291,7 @@ void MenuModule::loadSavegameList() { if (slotNum >= 0 && slotNum <= 999) { Common::InSaveFile *in = saveFileMan->openForLoading(file->c_str()); if (in) { - if (Neverhood::NeverhoodEngine::readSaveHeader(in, false, header) == Neverhood::NeverhoodEngine::kRSHENoError) { + if (Neverhood::NeverhoodEngine::readSaveHeader(in, header) == Neverhood::NeverhoodEngine::kRSHENoError) { SavegameItem savegameItem; savegameItem.slotNum = slotNum; savegameItem.description = header.description; diff --git a/engines/neverhood/neverhood.h b/engines/neverhood/neverhood.h index 4c5f9c3303..90055eeb9d 100644 --- a/engines/neverhood/neverhood.h +++ b/engines/neverhood/neverhood.h @@ -129,7 +129,7 @@ public: bool loadgame(const char *filename); const char *getSavegameFilename(int num); static Common::String getSavegameFilename(const Common::String &target, int num); - static kReadSaveHeaderError readSaveHeader(Common::SeekableReadStream *in, bool loadThumbnail, SaveHeader &header); + WARN_UNUSED_RESULT static kReadSaveHeaderError readSaveHeader(Common::SeekableReadStream *in, SaveHeader &header, bool skipThumbnail = true); GameState& gameState() { return _gameState; } GameModule *gameModule() { return _gameModule; } diff --git a/engines/neverhood/saveload.cpp b/engines/neverhood/saveload.cpp index 8fe6c9a155..d7e6f1ebfe 100644 --- a/engines/neverhood/saveload.cpp +++ b/engines/neverhood/saveload.cpp @@ -32,7 +32,7 @@ namespace Neverhood { #define NEVERHOOD_SAVEGAME_VERSION 0 -NeverhoodEngine::kReadSaveHeaderError NeverhoodEngine::readSaveHeader(Common::SeekableReadStream *in, bool loadThumbnail, SaveHeader &header) { +WARN_UNUSED_RESULT NeverhoodEngine::kReadSaveHeaderError NeverhoodEngine::readSaveHeader(Common::SeekableReadStream *in, SaveHeader &header, bool skipThumbnail) { header.version = in->readUint32LE(); if (header.version > NEVERHOOD_SAVEGAME_VERSION) @@ -43,10 +43,8 @@ NeverhoodEngine::kReadSaveHeaderError NeverhoodEngine::readSaveHeader(Common::Se while (descriptionLen--) header.description += (char)in->readByte(); - if (loadThumbnail) { - header.thumbnail = Graphics::loadThumbnail(*in); - } else { - Graphics::skipThumbnail(*in); + if (!Graphics::loadThumbnail(*in, header.thumbnail, skipThumbnail)) { + return kRSHEIoError; } // Not used yet, reserved for future usage @@ -110,7 +108,7 @@ bool NeverhoodEngine::loadgame(const char *filename) { SaveHeader header; - kReadSaveHeaderError errorCode = readSaveHeader(in, false, header); + kReadSaveHeaderError errorCode = readSaveHeader(in, header); if (errorCode != kRSHENoError) { warning("Error loading savegame '%s'", filename); diff --git a/engines/obsolete.cpp b/engines/obsolete.cpp index d65fb13ec1..ea96cff42e 100644 --- a/engines/obsolete.cpp +++ b/engines/obsolete.cpp @@ -55,7 +55,7 @@ void upgradeTargetIfNecessary(const ObsoleteGameID *obsoleteList) { } } -GameDescriptor findGameID( +PlainGameDescriptor findGameID( const char *gameid, const PlainGameDescriptor *gameids, const ObsoleteGameID *obsoleteList @@ -63,7 +63,7 @@ GameDescriptor findGameID( // First search the list of supported gameids for a match. const PlainGameDescriptor *g = findPlainGameDescriptor(gameid, gameids); if (g) - return GameDescriptor(*g); + return *g; // If we didn't find the gameid in the main list, check if it // is an obsolete game id. @@ -73,16 +73,16 @@ GameDescriptor findGameID( if (0 == scumm_stricmp(gameid, o->from)) { g = findPlainGameDescriptor(o->to, gameids); if (g && g->description) - return GameDescriptor(gameid, "Obsolete game ID (" + Common::String(g->description) + ")"); + return PlainGameDescriptor::of(gameid, g->description); else - return GameDescriptor(gameid, "Obsolete game ID"); + return PlainGameDescriptor::of(gameid, "Obsolete game ID"); } o++; } } // No match found - return GameDescriptor(); + return PlainGameDescriptor::empty(); } } // End of namespace Engines diff --git a/engines/obsolete.h b/engines/obsolete.h index be0963a7dc..7c7249e52b 100644 --- a/engines/obsolete.h +++ b/engines/obsolete.h @@ -66,7 +66,7 @@ void upgradeTargetIfNecessary(const ObsoleteGameID *obsoleteList); * Optionally can take a list of obsolete game ids into account in order * to support obsolete gameids. */ -GameDescriptor findGameID( +PlainGameDescriptor findGameID( const char *gameid, const PlainGameDescriptor *gameids, const ObsoleteGameID *obsoleteList = 0 diff --git a/engines/pegasus/items/biochips/pegasuschip.h b/engines/pegasus/items/biochips/pegasuschip.h index b81df94b39..31cd778d98 100644 --- a/engines/pegasus/items/biochips/pegasuschip.h +++ b/engines/pegasus/items/biochips/pegasuschip.h @@ -36,7 +36,7 @@ public: PegasusChip(const ItemID, const NeighborhoodID, const RoomID, const DirectionConstant); virtual ~PegasusChip(); - void select(); + void select() override; void takeSharedArea() override; diff --git a/engines/plumbers/console.cpp b/engines/plumbers/console.cpp index f005b60769..730a74e2e7 100644 --- a/engines/plumbers/console.cpp +++ b/engines/plumbers/console.cpp @@ -27,7 +27,7 @@ namespace Plumbers { -Console::Console(Plumbers::PlumbersGame *vm) : _vm(vm) { +Console::Console() { _allowSkip = false; registerCmd("allowSkip", WRAP_METHOD(Console, Cmd_allowSkip)); } diff --git a/engines/plumbers/console.h b/engines/plumbers/console.h index c849678419..cb50834f36 100644 --- a/engines/plumbers/console.h +++ b/engines/plumbers/console.h @@ -30,13 +30,10 @@ namespace Plumbers { class PlumbersGame; class Console : public GUI::Debugger { -private: - PlumbersGame *_vm; - public: bool _allowSkip; - explicit Console(Plumbers::PlumbersGame *vm); + explicit Console(); virtual ~Console(void) {} bool Cmd_allowSkip(int argc, const char** argv); diff --git a/engines/plumbers/plumbers.cpp b/engines/plumbers/plumbers.cpp index f0445e51eb..5491f81c65 100644 --- a/engines/plumbers/plumbers.cpp +++ b/engines/plumbers/plumbers.cpp @@ -89,7 +89,8 @@ static const byte cursorPalette[] = { Common::Error PlumbersGame::run() { initGraphics(640, 480); - _console = new Console(this); + _console = new Console(); + _image = new Image::BitmapDecoder(); CursorMan.replaceCursor(MOUSECURSOR_SCI, 11, 16, 0, 0, 0); CursorMan.replaceCursorPalette(cursorPalette, 0, 3); @@ -178,17 +179,11 @@ Common::Error PlumbersGame::run() { void PlumbersGame::loadImage(const Common::String &dirname, const Common::String &filename) { Common::String name = dirname + "/" + filename; debugC(1, kDebugGeneral, "%s : %s", __FUNCTION__, name.c_str()); - Common::File *file = new Common::File(); - if (!file->open(name)) + Common::File file; + if (!file.open(name)) error("unable to load image %s", name.c_str()); - if (_image) - delete _image; - - _image = new Image::BitmapDecoder(); - _image->loadStream(*file); - file->close(); - delete file; + _image->loadStream(file); } void PlumbersGame::drawScreen() { @@ -362,7 +357,6 @@ void PlumbersGame::readTables(const Common::String &fileName) { file.read(buf, kMaxName); _bitmaps[i]._filename = Common::String(buf); } - file.close(); } int PlumbersGame::getSceneNumb(int sNo) { diff --git a/engines/plumbers/plumbers.h b/engines/plumbers/plumbers.h index 56b27fb745..430c696c6d 100644 --- a/engines/plumbers/plumbers.h +++ b/engines/plumbers/plumbers.h @@ -25,7 +25,6 @@ #include "common/scummsys.h" #include "common/config-manager.h" -#include "engines/advancedDetector.h" #include "common/error.h" #include "engines/engine.h" @@ -44,6 +43,8 @@ #include "plumbers/console.h" +struct ADGameDescription; + namespace Plumbers { enum PlumbersDebugChannels { kDebugGeneral = 1 << 0 @@ -110,7 +111,7 @@ private: }; Common::Queue<Action> _actions; - + void loadImage(const Common::String &dirname, const Common::String &filename); void drawScreen(); diff --git a/engines/prince/archive.cpp b/engines/prince/archive.cpp index 984c078bfb..0ed6e8c753 100644 --- a/engines/prince/archive.cpp +++ b/engines/prince/archive.cpp @@ -55,8 +55,8 @@ bool PtcArchive::open(const Common::String &filename) { uint32 fileTableOffset = _stream->readUint32LE() ^ 0x4D4F4B2D; // MOK- uint32 fileTableSize = _stream->readUint32LE() ^ 0x534F4654; // SOFT - //debug("fileTableOffset : %08X", fileTableOffset); - //debug("fileTableSize: %08X", fileTableSize); + debug(8, "fileTableOffset : %08X", fileTableOffset); + debug(8, "fileTableSize: %08X", fileTableSize); _stream->seek(fileTableOffset); @@ -70,7 +70,7 @@ bool PtcArchive::open(const Common::String &filename) { Common::String name = (const char*)fileItem; item._offset = READ_LE_UINT32(fileItem + 24); item._size = READ_LE_UINT32(fileItem + 28); - //debug("%12s %8X %d", name.c_str(), item._offset, item._size); + debug(8, "%12s %8X %d", name.c_str(), item._offset, item._size); _items[name] = item; } @@ -135,6 +135,8 @@ Common::SeekableReadStream *PtcArchive::createReadStreamForMember(const Common:: return 0; } + debug(8, "PtcArchive::createReadStreamForMember(%s)", name.c_str()); + const FileEntry &entryHeader = _items[name]; if (entryHeader._size < 4) @@ -156,9 +158,9 @@ Common::SeekableReadStream *PtcArchive::createReadStreamForMember(const Common:: free(buffer); size = decompLen; buffer = decompData; - } - //debug("PtcArchive::createReadStreamForMember name %s", name.c_str()); + debug(8, "PtcArchive::createReadStreamForMember: decompressed %d to %d bytes", entryHeader._size, decompLen); + } return new Common::MemoryReadStream(buffer, size, DisposeAfterUse::YES); } diff --git a/engines/prince/cursor.cpp b/engines/prince/cursor.cpp index ab3a52eaa2..a4e58dc4a2 100644 --- a/engines/prince/cursor.cpp +++ b/engines/prince/cursor.cpp @@ -20,7 +20,12 @@ * */ +#include "graphics/cursorman.h" + +#include "prince/prince.h" #include "prince/cursor.h" +#include "prince/debugger.h" +#include "prince/script.h" #include "common/debug.h" @@ -51,4 +56,98 @@ bool Cursor::loadStream(Common::SeekableReadStream &stream) { return true; } +void PrinceEngine::changeCursor(uint16 curId) { + _debugger->_cursorNr = curId; + _mouseFlag = curId; + _flags->setFlagValue(Flags::MOUSEENABLED, curId); + + const Graphics::Surface *curSurface = nullptr; + + switch (curId) { + default: + error("Unknown cursor Id: %d", curId); + case 0: + CursorMan.showMouse(false); + _optionsFlag = 0; + _selectedMob = -1; + return; + case 1: + curSurface = _cursor1->getSurface(); + break; + case 2: + curSurface = _cursor2; + break; + case 3: + curSurface = _cursor3->getSurface(); + Common::Point mousePos = _system->getEventManager()->getMousePos(); + mousePos.x = CLIP(mousePos.x, (int16) 315, (int16) 639); + mousePos.y = CLIP(mousePos.y, (int16) 0, (int16) 170); + _system->warpMouse(mousePos.x, mousePos.y); + break; + } + + CursorMan.replaceCursorPalette(_roomBmp->getPalette(), 0, 255); + CursorMan.replaceCursor( + curSurface->getBasePtr(0, 0), + curSurface->w, curSurface->h, + 0, 0, + 255, false, + &curSurface->format + ); + CursorMan.showMouse(true); +} + +void PrinceEngine::makeInvCursor(int itemNr) { + const Graphics::Surface *cur1Surface = _cursor1->getSurface(); + int cur1W = cur1Surface->w; + int cur1H = cur1Surface->h; + const Common::Rect cur1Rect(0, 0, cur1W, cur1H); + + const Graphics::Surface *itemSurface = _allInvList[itemNr].getSurface(); + int itemW = itemSurface->w; + int itemH = itemSurface->h; + + int cur2W = cur1W + itemW / 2; + int cur2H = cur1H + itemH / 2; + + if (_cursor2 != nullptr) { + _cursor2->free(); + delete _cursor2; + } + _cursor2 = new Graphics::Surface(); + _cursor2->create(cur2W, cur2H, Graphics::PixelFormat::createFormatCLUT8()); + Common::Rect cur2Rect(0, 0, cur2W, cur2H); + _cursor2->fillRect(cur2Rect, 255); + _cursor2->copyRectToSurface(*cur1Surface, 0, 0, cur1Rect); + + const byte *src1 = (const byte *)itemSurface->getBasePtr(0, 0); + byte *dst1 = (byte *)_cursor2->getBasePtr(cur1W, cur1H); + + if (itemH % 2) { + itemH--; + } + if (itemW % 2) { + itemW--; + } + + for (int y = 0; y < itemH; y++) { + const byte *src2 = src1; + byte *dst2 = dst1; + if (y % 2 == 0) { + for (int x = 0; x < itemW; x++, src2++) { + if (x % 2 == 0) { + if (*src2) { + *dst2 = *src2; + } else { + *dst2 = 255; + } + dst2++; + } + } + dst1 += _cursor2->pitch; + } + src1 += itemSurface->pitch; + } +} + } // End of namespace Prince diff --git a/engines/prince/decompress.cpp b/engines/prince/decompress.cpp index 2b563917f7..eda992c093 100644 --- a/engines/prince/decompress.cpp +++ b/engines/prince/decompress.cpp @@ -27,145 +27,145 @@ namespace Prince { static const uint16 table1[] = { - 0x8000, 0x0002, - 0x4000, 0x0004, - 0x2000, 0x0008, - 0x1000, 0x0010, - 0x0800, 0x0020, - 0x0400, 0x0040, - 0x0200, 0x0080, - 0x0100, 0x0100, - 0x0080, 0x0200, - 0x0040, 0x0400 + 0x8000, 0x0002, + 0x4000, 0x0004, + 0x2000, 0x0008, + 0x1000, 0x0010, + 0x0800, 0x0020, + 0x0400, 0x0040, + 0x0200, 0x0080, + 0x0100, 0x0100, + 0x0080, 0x0200, + 0x0040, 0x0400 }; static const uint32 table2[] = { - 0x0000F000, - 0x0020FC00, - 0x00A0FF00, - 0x02A0FF80, - 0x06A0FFC0, - 0x0EA0FFE0, - 0x1EA0FFF0, - 0x3EA0FFF8 + 0x0000F000, + 0x0020FC00, + 0x00A0FF00, + 0x02A0FF80, + 0x06A0FFC0, + 0x0EA0FFE0, + 0x1EA0FFF0, + 0x3EA0FFF8 }; static const uint16 table3[] = { - 0x8000, 0x0000, - 0x4000, 0x0002, - 0x2000, 0x0006, - 0x1000, 0x000E, - 0x0800, 0x001E, - 0x0400, 0x003E, - 0x0200, 0x007E, - 0x0100, 0x00FE, - 0x0080, 0x01FE, - 0x0040, 0x03FE, - 0x0020, 0x07FE, - 0x0010, 0x0FFE, - 0x0008, 0x1FFE, - 0x0004, 0x3FFE, - 0x0002, 0x7FFE, - 0x0001, 0xFFFE + 0x8000, 0x0000, + 0x4000, 0x0002, + 0x2000, 0x0006, + 0x1000, 0x000E, + 0x0800, 0x001E, + 0x0400, 0x003E, + 0x0200, 0x007E, + 0x0100, 0x00FE, + 0x0080, 0x01FE, + 0x0040, 0x03FE, + 0x0020, 0x07FE, + 0x0010, 0x0FFE, + 0x0008, 0x1FFE, + 0x0004, 0x3FFE, + 0x0002, 0x7FFE, + 0x0001, 0xFFFE }; void Decompressor::decompress(byte *source, byte *dest, uint32 destSize) { - byte *destEnd = dest + destSize; - int more; - _src = source; - _dst = dest; - _bitBuffer = 0x80; - while (_dst < destEnd) { - uint32 ebp; - uint16 offset, length; - if (getBit()) { - if (getBit()) { - if (getBit()) { - if (getBit()) { - if (getBit()) { - if (getBit()) { - uint32 tableIndex = 0; - while (getBit()) - tableIndex++; - length = table3[tableIndex * 2 + 0]; - do { - more = !(length & 0x8000); - length = (length << 1) | getBit(); - } while (more); - length += table3[tableIndex * 2 + 1]; - length++; - memcpy(_dst, _src, length); - _src += length; - _dst += length; - } - *_dst++ = *_src++; - } - *_dst++ = *_src++; - } - *_dst++ = *_src++; - } - *_dst++ = *_src++; - } - *_dst++ = *_src++; - } - if (!getBit()) { - if (getBit()) { - uint32 tableIndex = getBit(); - tableIndex = (tableIndex << 1) | getBit(); - tableIndex = (tableIndex << 1) | getBit(); - ebp = table2[tableIndex]; - length = 1; - } else { - ebp = 0x0000FF00; - length = 0; - } - } else { - uint32 tableIndex = 0; - while (getBit()) - tableIndex++; - length = table1[tableIndex * 2 + 0]; - do { - more = !(length & 0x8000); - length = (length << 1) | getBit(); - } while (more); - length += table1[tableIndex * 2 + 1]; - tableIndex = getBit(); - tableIndex = (tableIndex << 1) | getBit(); - tableIndex = (tableIndex << 1) | getBit(); - ebp = table2[tableIndex]; - } - offset = ebp & 0xFFFF; - do { - if (_bitBuffer == 0x80) { - if (offset >= 0xFF00) { - offset = (offset << 8) | *_src++; - } - } - more = offset & 0x8000; - offset = (offset << 1) | getBit(); - } while (more); - offset += (ebp >> 16); - length += 2; - while (length--) { - if (_dst >= destEnd) { - return; - } - *_dst = *(_dst - offset); - _dst++; - } - } + byte *destEnd = dest + destSize; + int more; + _src = source; + _dst = dest; + _bitBuffer = 0x80; + while (_dst < destEnd) { + uint32 ebp; + uint16 offset, length; + if (getBit()) { + if (getBit()) { + if (getBit()) { + if (getBit()) { + if (getBit()) { + if (getBit()) { + uint32 tableIndex = 0; + while (getBit()) + tableIndex++; + length = table3[tableIndex * 2 + 0]; + do { + more = !(length & 0x8000); + length = (length << 1) | getBit(); + } while (more); + length += table3[tableIndex * 2 + 1]; + length++; + memcpy(_dst, _src, length); + _src += length; + _dst += length; + } + *_dst++ = *_src++; + } + *_dst++ = *_src++; + } + *_dst++ = *_src++; + } + *_dst++ = *_src++; + } + *_dst++ = *_src++; + } + if (!getBit()) { + if (getBit()) { + uint32 tableIndex = getBit(); + tableIndex = (tableIndex << 1) | getBit(); + tableIndex = (tableIndex << 1) | getBit(); + ebp = table2[tableIndex]; + length = 1; + } else { + ebp = 0x0000FF00; + length = 0; + } + } else { + uint32 tableIndex = 0; + while (getBit()) + tableIndex++; + length = table1[tableIndex * 2 + 0]; + do { + more = !(length & 0x8000); + length = (length << 1) | getBit(); + } while (more); + length += table1[tableIndex * 2 + 1]; + tableIndex = getBit(); + tableIndex = (tableIndex << 1) | getBit(); + tableIndex = (tableIndex << 1) | getBit(); + ebp = table2[tableIndex]; + } + offset = ebp & 0xFFFF; + do { + if (_bitBuffer == 0x80) { + if (offset >= 0xFF00) { + offset = (offset << 8) | *_src++; + } + } + more = offset & 0x8000; + offset = (offset << 1) | getBit(); + } while (more); + offset += (ebp >> 16); + length += 2; + while (length--) { + if (_dst >= destEnd) { + return; + } + *_dst = *(_dst - offset); + _dst++; + } + } } int Decompressor::getBit() { - int bit = (_bitBuffer & 0x80) >> 7; - _bitBuffer <<= 1; - if (_bitBuffer == 0) { - _bitBuffer = *_src++; - bit = (_bitBuffer & 0x80) >> 7; - _bitBuffer <<= 1; - _bitBuffer |= 1; - } - return bit; + int bit = (_bitBuffer & 0x80) >> 7; + _bitBuffer <<= 1; + if (_bitBuffer == 0) { + _bitBuffer = *_src++; + bit = (_bitBuffer & 0x80) >> 7; + _bitBuffer <<= 1; + _bitBuffer |= 1; + } + return bit; } } // End of namespace Prince diff --git a/engines/prince/detection.cpp b/engines/prince/detection.cpp index a8fa305332..2147c23116 100644 --- a/engines/prince/detection.cpp +++ b/engines/prince/detection.cpp @@ -21,9 +21,15 @@ */ #include "prince/prince.h" +#include "engines/advancedDetector.h" namespace Prince { +struct PrinceGameDescription { + ADGameDescription desc; + PrinceGameType gameType; +}; + int PrinceEngine::getGameType() const { return _gameDescription->gameType; } @@ -43,7 +49,7 @@ Common::Language PrinceEngine::getLanguage() const { } // End of namespace Prince static const PlainGameDescriptor princeGames[] = { - {"prince", "Prince Game"}, + {"prince", "The Prince and the Coward"}, {0, 0} }; @@ -52,11 +58,11 @@ static const PrinceGameDescription gameDescriptions[] = { { { "prince", - "Galador", + "Galador: Der Fluch des Prinzen", AD_ENTRY1s("databank.ptc", "5fa03833177331214ec1354761b1d2ee", 3565031), Common::DE_DEU, Common::kPlatformWindows, - ADGF_TESTING, + ADGF_USEEXTRAASTITLE | ADGF_TESTING, GUIO1(GUIO_NONE) }, kPrinceDataDE @@ -68,7 +74,7 @@ static const PrinceGameDescription gameDescriptions[] = { AD_ENTRY1s("databank.ptc", "48ec9806bda9d152acbea8ce31c93c49", 3435298), Common::PL_POL, Common::kPlatformWindows, - ADGF_TESTING, + ADGF_USEEXTRAASTITLE | ADGF_TESTING, GUIO1(GUIO_NONE) }, kPrinceDataPL @@ -76,11 +82,11 @@ static const PrinceGameDescription gameDescriptions[] = { { { "prince", - "Galador", + "", AD_ENTRY1s("talktxt.dat", "02bb2372f19aca3c65896ed81b2cefb3", 125702), Common::RU_RUS, Common::kPlatformWindows, - ADGF_TESTING, + ADGF_TESTING | GF_EXTRACTED, GUIO1(GUIO_NONE) }, kPrinceDataDE @@ -88,19 +94,19 @@ static const PrinceGameDescription gameDescriptions[] = { { { "prince", - "Galador", + "", AD_ENTRY1s("databank.ptc", "a67b55730f3d7064921bd2a59e1063a3", 3892982), Common::RU_RUS, Common::kPlatformWindows, - ADGF_TESTING, + ADGF_TESTING | GF_NOVOICES, GUIO1(GUIO_NONE) }, - kPrinceDataPL + kPrinceDataDE }, { { "prince", - "The Prince and the Coward", + "", { {"databank.ptc", 0, "5fa03833177331214ec1354761b1d2ee", 3565031}, {"prince_translation.dat", 0, 0, -1}, @@ -116,7 +122,7 @@ static const PrinceGameDescription gameDescriptions[] = { { { "prince", - "The Prince and the Coward", + "", { {"databank.ptc", 0, "48ec9806bda9d152acbea8ce31c93c49", 3435298}, {"prince_translation.dat", 0, 0, -1}, @@ -169,6 +175,7 @@ bool PrinceMetaEngine::hasFeature(MetaEngineFeature f) const { (f == kSavesSupportMetaInfo) || (f == kSavesSupportThumbnail) || (f == kSavesSupportCreationDate) || + (f == kSavesSupportPlayTime) || (f == kSupportsListSaves) || (f == kSupportsLoadingDuringStartup) || (f == kSimpleSavesNames); @@ -188,7 +195,6 @@ SaveStateList PrinceMetaEngine::listSaves(const char *target) const { pattern += ".###"; filenames = saveFileMan->listSavefiles(pattern); - sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..) SaveStateList saveList; for (Common::StringArray::const_iterator filename = filenames.begin(); filename != filenames.end(); filename++) { @@ -209,10 +215,6 @@ SaveStateList PrinceMetaEngine::listSaves(const char *target) const { // Valid savegame if (Prince::PrinceEngine::readSavegameHeader(file, header)) { saveList.push_back(SaveStateDescriptor(slotNum, header.saveName)); - if (header.thumbnail) { - header.thumbnail->free(); - delete header.thumbnail; - } } } else { // Must be an original format savegame @@ -224,6 +226,7 @@ SaveStateList PrinceMetaEngine::listSaves(const char *target) const { } } + Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator()); return saveList; } @@ -239,7 +242,7 @@ SaveStateDescriptor PrinceMetaEngine::querySaveMetaInfos(const char *target, int f->read(buffer, kSavegameStrSize + 1); bool hasHeader = !strncmp(buffer, kSavegameStr, kSavegameStrSize + 1) && - Prince::PrinceEngine::readSavegameHeader(f, header); + Prince::PrinceEngine::readSavegameHeader(f, header, false); delete f; if (!hasHeader) { @@ -252,6 +255,7 @@ SaveStateDescriptor PrinceMetaEngine::querySaveMetaInfos(const char *target, int desc.setThumbnail(header.thumbnail); desc.setSaveDate(header.saveYear, header.saveMonth, header.saveDay); desc.setSaveTime(header.saveHour, header.saveMinutes); + desc.setPlayTime(header.playTime * 1000); return desc; } diff --git a/engines/prince/draw.cpp b/engines/prince/draw.cpp new file mode 100644 index 0000000000..a924f99def --- /dev/null +++ b/engines/prince/draw.cpp @@ -0,0 +1,765 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "graphics/palette.h" + +#include "prince/prince.h" + +#include "prince/animation.h" +#include "prince/graphics.h" +#include "prince/hero.h" +#include "prince/script.h" + +namespace Prince { + +bool PrinceEngine::spriteCheck(int sprWidth, int sprHeight, int destX, int destY) { + destX -= _picWindowX; + destY -= _picWindowY; + + // if x1 is on visible part of screen + if (destX < 0) { + if (destX + sprWidth < 1) { + //x2 is negative - out of window + return false; + } + } + // if x1 is outside of screen on right side + if (destX >= kNormalWidth) { + return false; + } + + if (destY < 0) { + if (destY + sprHeight < 1) { + //y2 is negative - out of window + return false; + } + } + if (destY >= kNormalHeight) { + return false; + } + + return true; +} + +// CheckNak +void PrinceEngine::checkMasks(int x1, int y1, int sprWidth, int sprHeight, int z) { + int x2 = x1 + sprWidth - 1; + int y2 = y1 + sprHeight - 1; + if (x1 < 0) { + x1 = 0; + } + for (uint i = 0; i < _maskList.size(); i++) { + if (!_maskList[i]._state && !_maskList[i]._flags) { + if (_maskList[i]._z > z) { + if (_maskList[i]._x1 <= x2 && _maskList[i]._x2 >= x1) { + if (_maskList[i]._y1 <= y2 && _maskList[i]._y2 >= y1) { + _maskList[i]._state = 1; + } + } + } + } + } +} + +// ClsNak +void PrinceEngine::clsMasks() { + for (uint i = 0; i < _maskList.size(); i++) { + if (_maskList[i]._state) { + _maskList[i]._state = 0; + } + } +} + +// InsertNakladki +void PrinceEngine::insertMasks(Graphics::Surface *originalRoomSurface) { + for (uint i = 0; i < _maskList.size(); i++) { + if (_maskList[i]._state) { + if (_maskList[i]._data != nullptr) { + showMask(i, originalRoomSurface); + } else { + error("insertMasks() - Wrong mask data- nr %d", i); + } + } + } +} + +// ShowNak +void PrinceEngine::showMask(int maskNr, Graphics::Surface *originalRoomSurface) { + if (!_maskList[maskNr]._flags) { + if (spriteCheck(_maskList[maskNr]._width, _maskList[maskNr]._height, _maskList[maskNr]._x1, _maskList[maskNr]._y1)) { + int destX = _maskList[maskNr]._x1 - _picWindowX; + int destY = _maskList[maskNr]._y1 - _picWindowY; + DrawNode newDrawNode; + newDrawNode.posX = destX; + newDrawNode.posY = destY; + newDrawNode.posZ = _maskList[maskNr]._z; + newDrawNode.width = _maskList[maskNr]._width; + newDrawNode.height = _maskList[maskNr]._height; + newDrawNode.s = nullptr; + newDrawNode.originalRoomSurface = originalRoomSurface; + newDrawNode.data = _maskList[maskNr].getMask(); + newDrawNode.drawFunction = &_graph->drawMaskDrawNode; + _drawNodeList.push_back(newDrawNode); + } + } +} + +void PrinceEngine::showSprite(Graphics::Surface *spriteSurface, int destX, int destY, int destZ) { + if (spriteCheck(spriteSurface->w, spriteSurface->h, destX, destY)) { + destX -= _picWindowX; + destY -= _picWindowY; + DrawNode newDrawNode; + newDrawNode.posX = destX; + newDrawNode.posY = destY; + newDrawNode.posZ = destZ; + newDrawNode.width = 0; + newDrawNode.height = 0; + newDrawNode.s = spriteSurface; + newDrawNode.originalRoomSurface = nullptr; + newDrawNode.data = _transTable; + newDrawNode.drawFunction = &_graph->drawTransparentWithTransDrawNode; + _drawNodeList.push_back(newDrawNode); + } +} + +void PrinceEngine::showSpriteShadow(Graphics::Surface *shadowSurface, int destX, int destY, int destZ) { + if (spriteCheck(shadowSurface->w, shadowSurface->h, destX, destY)) { + destX -= _picWindowX; + destY -= _picWindowY; + DrawNode newDrawNode; + newDrawNode.posX = destX; + newDrawNode.posY = destY; + newDrawNode.posZ = destZ; + newDrawNode.width = 0; + newDrawNode.height = 0; + newDrawNode.s = shadowSurface; + newDrawNode.originalRoomSurface = nullptr; + newDrawNode.data = _graph->_shadowTable70; + newDrawNode.drawFunction = &_graph->drawAsShadowDrawNode; + _drawNodeList.push_back(newDrawNode); + } +} + +void PrinceEngine::showAnim(Anim &anim) { + //ShowFrameCode + //ShowAnimFrame + int phase = anim._showFrame; + int phaseFrameIndex = anim._animData->getPhaseFrameIndex(phase); + int x = anim._x + anim._animData->getPhaseOffsetX(phase); + int y = anim._y + anim._animData->getPhaseOffsetY(phase); + int animFlag = anim._flags; + int checkMaskFlag = (animFlag & 1); + int maxFrontFlag = (animFlag & 2); + int specialZFlag = anim._nextAnim; + int z = anim._nextAnim; + Graphics::Surface *animSurface = anim._animData->getFrame(phaseFrameIndex); + int frameWidth = animSurface->w; + int frameHeight = animSurface->h; + int shadowZ = 0; + + if (checkMaskFlag) { + if (!anim._nextAnim) { + z = y + frameHeight - 1; + } + checkMasks(x, y, frameWidth, frameHeight, z); + } + + if (specialZFlag) { + z = specialZFlag; + } else if (maxFrontFlag) { + z = kMaxPicHeight + 1; + } else { + z = y + frameHeight - 1; + } + shadowZ = z; + + anim._currX = x; + anim._currY = y; + anim._currW = frameWidth; + anim._currH = frameHeight; + showSprite(animSurface, x, y, z); + + // make_special_shadow + if ((anim._flags & 0x80)) { + DrawNode newDrawNode; + newDrawNode.posX = x; + newDrawNode.posY = y + animSurface->h - anim._shadowBack; + newDrawNode.posZ = Hero::kHeroShadowZ; + newDrawNode.width = 0; + newDrawNode.height = 0; + newDrawNode.scaleValue = _scaleValue; + newDrawNode.originalRoomSurface = nullptr; + newDrawNode.data = this; + newDrawNode.drawFunction = &Hero::showHeroShadow; + newDrawNode.s = animSurface; + _drawNodeList.push_back(newDrawNode); + } + + //ShowFrameCodeShadow + //ShowAnimFrameShadow + if (anim._shadowData != nullptr) { + int shadowPhaseFrameIndex = anim._shadowData->getPhaseFrameIndex(phase); + int shadowX = anim._shadowData->getBaseX() + anim._shadowData->getPhaseOffsetX(phase); + int shadowY = anim._shadowData->getBaseY() + anim._shadowData->getPhaseOffsetY(phase); + Graphics::Surface *shadowSurface = anim._shadowData->getFrame(shadowPhaseFrameIndex); + int shadowFrameWidth = shadowSurface->w; + int shadowFrameHeight = shadowSurface->h; + + if (checkMaskFlag) { + checkMasks(shadowX, shadowY, shadowFrameWidth, shadowFrameHeight, shadowY + shadowFrameWidth - 1); + } + + if (!shadowZ) { + if (maxFrontFlag) { + shadowZ = kMaxPicHeight + 1; + } else { + shadowZ = shadowY + shadowFrameWidth - 1; + } + } + showSpriteShadow(shadowSurface, shadowX, shadowY, shadowZ); + } +} + +void PrinceEngine::showNormAnims() { + for (int i = 0; i < kMaxNormAnims; i++) { + Anim &anim = _normAnimList[i]; + if (anim._animData != nullptr) { + int phaseCount = anim._animData->getPhaseCount(); + if (!anim._state) { + if (anim._frame == anim._lastFrame - 1) { + if (anim._loopType) { + if (anim._loopType == 1) { + anim._frame = anim._loopFrame; + } else { + continue; + } + } + } else { + anim._frame++; + } + anim._showFrame = anim._frame; + if (anim._showFrame >= phaseCount) { + anim._showFrame = phaseCount - 1; + } + showAnim(anim); + } + } + } +} + +void PrinceEngine::setBackAnim(Anim &backAnim) { + int start = backAnim._basaData._start; + if (start != -1) { + backAnim._frame = start; + backAnim._showFrame = start; + backAnim._loopFrame = start; + } + int end = backAnim._basaData._end; + if (end != -1) { + backAnim._lastFrame = end; + } + backAnim._state = 0; +} + +void PrinceEngine::showBackAnims() { + for (int i = 0; i < kMaxBackAnims; i++) { + BAS &seq = _backAnimList[i]._seq; + int activeSubAnim = seq._currRelative; + if (!_backAnimList[i].backAnims.empty()) { + if (_backAnimList[i].backAnims[activeSubAnim]._animData != nullptr) { + if (!_backAnimList[i].backAnims[activeSubAnim]._state) { + seq._counter++; + if (seq._type == 2) { + if (!seq._currRelative) { + if (seq._counter >= seq._data) { + if (seq._anims > 2) { + seq._currRelative = _randomSource.getRandomNumber(seq._anims - 2) + 1; + activeSubAnim = seq._currRelative; + seq._current = _backAnimList[i].backAnims[activeSubAnim]._basaData._num; + } + setBackAnim(_backAnimList[i].backAnims[activeSubAnim]); + seq._counter = 0; + } + } + } + + if (seq._type == 3) { + if (!seq._currRelative) { + if (seq._counter < seq._data2) { + continue; + } else { + setBackAnim(_backAnimList[i].backAnims[activeSubAnim]); + } + } + } + + if (_backAnimList[i].backAnims[activeSubAnim]._frame == _backAnimList[i].backAnims[activeSubAnim]._lastFrame - 1) { + _backAnimList[i].backAnims[activeSubAnim]._frame = _backAnimList[i].backAnims[activeSubAnim]._loopFrame; + switch (seq._type) { + case 1: + if (seq._anims > 1) { + int rnd; + do { + rnd = _randomSource.getRandomNumber(seq._anims - 1); + } while (rnd == seq._currRelative); + seq._currRelative = rnd; + seq._current = _backAnimList[i].backAnims[rnd]._basaData._num; + activeSubAnim = rnd; + setBackAnim(_backAnimList[i].backAnims[activeSubAnim]); + seq._counter = 0; + } + break; + case 2: + if (seq._currRelative) { + seq._currRelative = 0; + seq._current = _backAnimList[i].backAnims[0]._basaData._num; + activeSubAnim = 0; + setBackAnim(_backAnimList[i].backAnims[activeSubAnim]); + seq._counter = 0; + } + break; + case 3: + seq._currRelative = 0; + seq._current = _backAnimList[i].backAnims[0]._basaData._num; + seq._counter = 0; + seq._data2 = _randomSource.getRandomNumber(seq._data - 1); + continue; // for bug in original game + break; + } + } else { + _backAnimList[i].backAnims[activeSubAnim]._frame++; + } + _backAnimList[i].backAnims[activeSubAnim]._showFrame = _backAnimList[i].backAnims[activeSubAnim]._frame; + showAnim(_backAnimList[i].backAnims[activeSubAnim]); + } + } + } + } +} + +void PrinceEngine::removeSingleBackAnim(int slot) { + if (!_backAnimList[slot].backAnims.empty()) { + for (uint j = 0; j < _backAnimList[slot].backAnims.size(); j++) { + if (_backAnimList[slot].backAnims[j]._animData != nullptr) { + delete _backAnimList[slot].backAnims[j]._animData; + _backAnimList[slot].backAnims[j]._animData = nullptr; + } + if (_backAnimList[slot].backAnims[j]._shadowData != nullptr) { + delete _backAnimList[slot].backAnims[j]._shadowData; + _backAnimList[slot].backAnims[j]._shadowData = nullptr; + } + } + _backAnimList[slot].backAnims.clear(); + _backAnimList[slot]._seq._currRelative = 0; + } +} + +void PrinceEngine::clearBackAnimList() { + for (int i = 0; i < kMaxBackAnims; i++) { + removeSingleBackAnim(i); + } +} + +void PrinceEngine::grabMap() { + _graph->_frontScreen->copyFrom(*_roomBmp->getSurface()); + showObjects(); + runDrawNodes(); + _graph->_mapScreen->copyFrom(*_graph->_frontScreen); +} + +void PrinceEngine::initZoomIn(int slot) { + freeZoomObject(slot); + Object *object = _objList[slot]; + if (object != nullptr) { + Graphics::Surface *zoomSource = object->getSurface(); + if (zoomSource != nullptr) { + object->_flags |= 0x8000; + object->_zoomSurface = new Graphics::Surface(); + object->_zoomSurface->create(zoomSource->w, zoomSource->h, Graphics::PixelFormat::createFormatCLUT8()); + object->_zoomSurface->fillRect(Common::Rect(zoomSource->w, zoomSource->h), 0xFF); + object->_zoomTime = 20; + } + } +} + +void PrinceEngine::initZoomOut(int slot) { + freeZoomObject(slot); + Object *object = _objList[slot]; + if (object != nullptr) { + Graphics::Surface *zoomSource = object->getSurface(); + if (zoomSource != nullptr) { + object->_flags |= 0x4000; + object->_zoomSurface = new Graphics::Surface(); + object->_zoomSurface->copyFrom(*zoomSource); + object->_zoomTime = 10; + } + } +} + +void PrinceEngine::doZoomIn(int slot) { + Object *object = _objList[slot]; + if (object != nullptr) { + Graphics::Surface *orgSurface = object->getSurface(); + if (orgSurface != nullptr) { + byte *src1 = (byte *)orgSurface->getBasePtr(0, 0); + byte *dst1 = (byte *)object->_zoomSurface->getBasePtr(0, 0); + int x = 0; + int surfaceHeight = orgSurface->h; + for (int y = 0; y < surfaceHeight; y++) { + byte *src2 = src1; + byte *dst2 = dst1; + int w = orgSurface->w - x; + src2 += x; + dst2 += x; + while (w > 0) { + int randVal = _randomSource.getRandomNumber(zoomInStep - 1); + if (randVal < w) { + *(dst2 + randVal) = *(src2 + randVal); + src2 += zoomInStep; + dst2 += zoomInStep; + } else if (y + 1 != surfaceHeight) { + *(dst1 + orgSurface->pitch + randVal - w) = *(src1 + orgSurface->pitch + randVal - w); + } + w -= zoomInStep; + } + x = -1 * w; + src1 += orgSurface->pitch; + dst1 += orgSurface->pitch; + } + } + } +} + +void PrinceEngine::doZoomOut(int slot) { + Object *object = _objList[slot]; + if (object != nullptr) { + Graphics::Surface *orgSurface = object->getSurface(); + if (orgSurface != nullptr) { + byte *dst1 = (byte *)object->_zoomSurface->getBasePtr(0, 0); + int x = 0; + int surfaceHeight = orgSurface->h; + for (int y = 0; y < surfaceHeight; y++) { + byte *dst2 = dst1; + int w = orgSurface->w - x; + dst2 += x; + while (w > 0) { + int randVal = _randomSource.getRandomNumber(zoomInStep - 1); + if (randVal < w) { + *(dst2 + randVal) = 255; + dst2 += zoomInStep; + } else if (y + 1 != surfaceHeight) { + *(dst1 + orgSurface->pitch + randVal - w) = 255; + } + w -= zoomInStep; + } + x = -1 * w; + dst1 += orgSurface->pitch; + } + } + } +} + +void PrinceEngine::freeZoomObject(int slot) { + Object *object = _objList[slot]; + if (object != nullptr) { + if (object->_zoomSurface != nullptr) { + object->_zoomSurface->free(); + delete object->_zoomSurface; + object->_zoomSurface = nullptr; + } + } +} + +void PrinceEngine::showObjects() { + for (int i = 0; i < kMaxObjects; i++) { + int nr = _objSlot[i]; + if (nr != 0xFF) { + Graphics::Surface *objSurface = nullptr; + if ((_objList[nr]->_flags & 0x8000)) { + _objList[nr]->_zoomTime--; + if (!_objList[nr]->_zoomTime) { + freeZoomObject(nr); + _objList[nr]->_flags &= 0x7FFF; + objSurface = _objList[nr]->getSurface(); + } else { + doZoomIn(nr); + objSurface = _objList[nr]->_zoomSurface; + } + } else if ((_objList[nr]->_flags & 0x4000)) { + _objList[nr]->_zoomTime--; + if (!_objList[nr]->_zoomTime) { + freeZoomObject(nr); + _objList[nr]->_flags &= 0xBFFF; + objSurface = _objList[nr]->getSurface(); + } else { + doZoomOut(nr); + objSurface = _objList[nr]->_zoomSurface; + } + } else { + objSurface = _objList[nr]->getSurface(); + } + + if (objSurface != nullptr) { + if (spriteCheck(objSurface->w, objSurface->h, _objList[nr]->_x, _objList[nr]->_y)) { + int destX = _objList[nr]->_x - _picWindowX; + int destY = _objList[nr]->_y - _picWindowY; + DrawNode newDrawNode; + newDrawNode.posX = destX; + newDrawNode.posY = destY; + newDrawNode.posZ = _objList[nr]->_z; + newDrawNode.width = 0; + newDrawNode.height = 0; + newDrawNode.s = objSurface; + newDrawNode.originalRoomSurface = nullptr; + if ((_objList[nr]->_flags & 0x2000)) { + newDrawNode.data = nullptr; + newDrawNode.drawFunction = &_graph->drawBackSpriteDrawNode; + } else { + newDrawNode.data = _transTable; + if (_flags->getFlagValue(Flags::NOANTIALIAS)) { + newDrawNode.drawFunction = &_graph->drawTransparentDrawNode; + } else { + newDrawNode.drawFunction = &_graph->drawTransparentWithTransDrawNode; + } + } + _drawNodeList.push_back(newDrawNode); + } + + if ((_objList[nr]->_flags & 1)) { + checkMasks(_objList[nr]->_x, _objList[nr]->_y, objSurface->w, objSurface->h, _objList[nr]->_z); + } + } + } + } +} + +void PrinceEngine::showParallax() { + if (!_pscrList.empty()) { + for (uint i = 0; i < _pscrList.size(); i++) { + Graphics::Surface *pscrSurface = _pscrList[i]->getSurface(); + if (pscrSurface != nullptr) { + int x = _pscrList[i]->_x - (_pscrList[i]->_step * _picWindowX / 4); + int y = _pscrList[i]->_y; + int z = PScr::kPScrZ; + if (spriteCheck(pscrSurface->w, pscrSurface->h, x, y)) { + showSprite(pscrSurface, x, y, z); + } + } + } + } +} + +bool PrinceEngine::compareDrawNodes(DrawNode d1, DrawNode d2) { + if (d1.posZ < d2.posZ) { + return true; + } + return false; +} + +void PrinceEngine::runDrawNodes() { + Common::sort(_drawNodeList.begin(), _drawNodeList.end(), compareDrawNodes); + + for (uint i = 0; i < _drawNodeList.size(); i++) { + (*_drawNodeList[i].drawFunction)(_graph->_frontScreen, &_drawNodeList[i]); + } + _graph->change(); +} + +void PrinceEngine::drawScreen() { + if (!_showInventoryFlag || _inventoryBackgroundRemember) { + clsMasks(); + + _mainHero->showHero(); + _mainHero->scrollHero(); + _mainHero->drawHero(); + + _secondHero->showHero(); + _secondHero->_drawX -= _picWindowX; + _secondHero->drawHero(); + + const Graphics::Surface *roomSurface; + if (_locationNr != 50) { + roomSurface = _roomBmp->getSurface(); + } else { + roomSurface = _graph->_mapScreen; + } + Graphics::Surface visiblePart; + if (roomSurface) { + visiblePart = roomSurface->getSubArea(Common::Rect(_picWindowX, 0, roomSurface->w, roomSurface->h)); + _graph->draw(_graph->_frontScreen, &visiblePart); + } + + showBackAnims(); + + showNormAnims(); + + playNextFLCFrame(); + + showObjects(); + + if (roomSurface) { + insertMasks(&visiblePart); + } + + showParallax(); + + runDrawNodes(); + + _drawNodeList.clear(); + + if (!_inventoryBackgroundRemember && !_dialogFlag) { + if (!_optionsFlag) { + _selectedMob = checkMob(_graph->_frontScreen, _mobList, true); + } + showTexts(_graph->_frontScreen); + checkOptions(); + } else { + _inventoryBackgroundRemember = false; + } + + showPower(); + + getDebugger()->onFrame(); + + } else { + displayInventory(); + } +} + +void PrinceEngine::blackPalette() { + byte *paletteBackup = (byte *)malloc(256 * 3); + byte *blackPalette1 = (byte *)malloc(256 * 3); + + int fadeStep = kFadeStep - 1; + for (int i = 0; i < kFadeStep; i++) { + _system->getPaletteManager()->grabPalette(paletteBackup, 0, 256); + for (int j = 0; j < 256; j++) { + blackPalette1[3 * j] = paletteBackup[3 * j] * fadeStep / 4; + blackPalette1[3 * j + 1] = paletteBackup[3 * j + 1] * fadeStep / 4; + blackPalette1[3 * j + 2] = paletteBackup[3 * j + 2] * fadeStep / 4; + } + fadeStep--; + _graph->setPalette(blackPalette1); + _system->updateScreen(); + Common::Event event; + Common::EventManager *eventMan = _system->getEventManager(); + eventMan->pollEvent(event); + if (shouldQuit()) { + free(paletteBackup); + free(blackPalette1); + return; + } + pausePrinceEngine(); + } + free(paletteBackup); + free(blackPalette1); +} + +void PrinceEngine::setPalette(const byte *palette) { + if (palette != nullptr) { + byte *blackPalette_ = (byte *)malloc(256 * 3); + int fadeStep = 0; + for (int i = 0; i <= kFadeStep; i++) { + for (int j = 0; j < 256; j++) { + blackPalette_[3 * j] = palette[3 * j] * fadeStep / 4; + blackPalette_[3 * j + 1] = palette[3 * j + 1] * fadeStep / 4; + blackPalette_[3 * j + 2] = palette[3 * j + 2] * fadeStep / 4; + } + fadeStep++; + _graph->setPalette(blackPalette_); + _system->updateScreen(); + Common::Event event; + Common::EventManager *eventMan = _system->getEventManager(); + eventMan->pollEvent(event); + if (shouldQuit()) { + _graph->setPalette(palette); + free(blackPalette_); + return; + } + pausePrinceEngine(); + } + _graph->setPalette(palette); + free(blackPalette_); + } +} + +void PrinceEngine::doTalkAnim(int animNumber, int slot, AnimType animType) { + Text &text = _textSlots[slot]; + int lines = calcTextLines((const char *)_interpreter->getString()); + int time = lines * 30; + if (animType == kNormalAnimation) { + Anim &normAnim = _normAnimList[animNumber]; + if (normAnim._animData != nullptr) { + if (!normAnim._state) { + if (normAnim._currW && normAnim._currH) { + text._color = _flags->getFlagValue(Flags::KOLOR); + text._x = normAnim._currX + normAnim._currW / 2; + text._y = normAnim._currY - 10; + } + } + } + } else if (animType == kBackgroundAnimation) { + if (!_backAnimList[animNumber].backAnims.empty()) { + int currAnim = _backAnimList[animNumber]._seq._currRelative; + Anim &backAnim = _backAnimList[animNumber].backAnims[currAnim]; + if (backAnim._animData != nullptr) { + if (!backAnim._state) { + if (backAnim._currW && backAnim._currH) { + text._color = _flags->getFlagValue(Flags::KOLOR); + text._x = backAnim._currX + backAnim._currW / 2; + text._y = backAnim._currY - 10; + } + } + } + } + } else { + error("doTalkAnim() - wrong animType: %d", animType); + } + text._time = time; + if (getLanguage() == Common::DE_DEU) { + correctStringDEU((char *)_interpreter->getString()); + } + text._str = (const char *)_interpreter->getString(); + _interpreter->increaseString(); +} + +void PrinceEngine::freeNormAnim(int slot) { + if (!_normAnimList.empty()) { + _normAnimList[slot]._state = 1; + if (_normAnimList[slot]._animData != nullptr) { + delete _normAnimList[slot]._animData; + _normAnimList[slot]._animData = nullptr; + } + if (_normAnimList[slot]._shadowData != nullptr) { + delete _normAnimList[slot]._shadowData; + _normAnimList[slot]._shadowData = nullptr; + } + } +} + +void PrinceEngine::freeAllNormAnims() { + for (int i = 0; i < kMaxNormAnims; i++) { + freeNormAnim(i); + } +} + +} // End of namespace Prince diff --git a/engines/prince/hero.cpp b/engines/prince/hero.cpp index 037f11e95a..8ade649a9f 100644 --- a/engines/prince/hero.cpp +++ b/engines/prince/hero.cpp @@ -68,7 +68,7 @@ bool Hero::loadAnimSet(uint32 animSetNr) { _moveSet.resize(kMoveSetSize); for (uint32 i = 0; i < kMoveSetSize; i++) { - debug("Anim set item %d %s", i, animSet[i]); + debug(5, "Anim set item %d %s", i, animSet[i]); Animation *anim = nullptr; if (animSet[i] != nullptr) { anim = new Animation(); diff --git a/engines/prince/inventory.cpp b/engines/prince/inventory.cpp new file mode 100644 index 0000000000..3183b94334 --- /dev/null +++ b/engines/prince/inventory.cpp @@ -0,0 +1,709 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can 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 "prince/prince.h" + +#include "prince/graphics.h" +#include "prince/hero.h" +#include "prince/script.h" +#include "prince/mhwanh.h" +#include "prince/variatxt.h" +#include "prince/option_text.h" +#include "prince/font.h" + +namespace Prince { + +void PrinceEngine::addInv(int heroId, int item, bool addItemQuiet) { + Hero *hero = nullptr; + if (!heroId) { + hero = _mainHero; + } else if (heroId == 1) { + hero = _secondHero; + } + if (hero != nullptr) { + if (hero->_inventory.size() < kMaxItems) { + if (item != 0x7FFF) { + hero->_inventory.push_back(item); + } + if (!addItemQuiet) { + addInvObj(); + } + _interpreter->setResult(0); + } else { + _interpreter->setResult(1); + } + } +} + +void PrinceEngine::remInv(int heroId, int item) { + Hero *hero = nullptr; + if (!heroId) { + hero = _mainHero; + } else if (heroId == 1) { + hero = _secondHero; + } + if (hero != nullptr) { + for (uint i = 0; i < hero->_inventory.size(); i++) { + if (hero->_inventory[i] == item) { + hero->_inventory.remove_at(i); + _interpreter->setResult(0); + return; + } + } + } + _interpreter->setResult(1); +} + +void PrinceEngine::clearInv(int heroId) { + switch (heroId) { + case 0: + _mainHero->_inventory.clear(); + break; + case 1: + _secondHero->_inventory.clear(); + break; + default: + error("clearInv() - wrong hero slot"); + break; + } +} + +void PrinceEngine::swapInv(int heroId) { + Common::Array<int> tempInv; + Hero *hero = nullptr; + if (!heroId) { + hero = _mainHero; + } else if (heroId == 1) { + hero = _secondHero; + } + if (hero != nullptr) { + for (uint i = 0; i < hero->_inventory.size(); i++) { + tempInv.push_back(hero->_inventory[i]); + } + hero->_inventory.clear(); + for (uint i = 0; i < hero->_inventory2.size(); i++) { + hero->_inventory.push_back(hero->_inventory2[i]); + } + hero->_inventory2.clear(); + for (uint i = 0; i < tempInv.size(); i++) { + hero->_inventory2.push_back(tempInv[i]); + } + tempInv.clear(); + } +} + +void PrinceEngine::addInvObj() { + changeCursor(0); + prepareInventoryToView(); + + _inventoryBackgroundRemember = true; + drawScreen(); + + Graphics::Surface *suitcase = _suitcaseBmp->getSurface(); + + if (!_flags->getFlagValue(Flags::CURSEBLINK)) { + + loadSample(27, "PRZEDMIO.WAV"); + playSample(27, 0); + + _mst_shadow2 = 1; + + while (_mst_shadow2 < 512) { + rememberScreenInv(); + _graph->drawTransparentSurface(_graph->_screenForInventory, 0, 0, suitcase); + drawInvItems(); + _graph->update(_graph->_screenForInventory); + _mst_shadow2 += 50; + Common::Event event; + Common::EventManager *eventMan = _system->getEventManager(); + eventMan->pollEvent(event); + if (shouldQuit()) { + return; + } + pausePrinceEngine(); + } + while (_mst_shadow2 > 256) { + rememberScreenInv(); + _graph->drawTransparentSurface(_graph->_screenForInventory, 0, 0, suitcase); + drawInvItems(); + _graph->update(_graph->_screenForInventory); + _mst_shadow2 -= 42; + Common::Event event; + Common::EventManager *eventMan = _system->getEventManager(); + eventMan->pollEvent(event); + if (shouldQuit()) { + return; + } + pausePrinceEngine(); + } + } else { + //CURSEBLINK: + for (int i = 0; i < 3; i++) { + _mst_shadow2 = 256; + while (_mst_shadow2 < 512) { + rememberScreenInv(); + _graph->drawTransparentSurface(_graph->_screenForInventory, 0, 0, suitcase); + drawInvItems(); + _graph->update(_graph->_screenForInventory); + _mst_shadow2 += 50; + Common::Event event; + Common::EventManager *eventMan = _system->getEventManager(); + eventMan->pollEvent(event); + if (shouldQuit()) { + return; + } + pausePrinceEngine(); + } + while (_mst_shadow2 > 256) { + rememberScreenInv(); + _graph->drawTransparentSurface(_graph->_screenForInventory, 0, 0, suitcase); + drawInvItems(); + _graph->update(_graph->_screenForInventory); + _mst_shadow2 -= 50; + Common::Event event; + Common::EventManager *eventMan = _system->getEventManager(); + eventMan->pollEvent(event); + if (shouldQuit()) { + return; + } + pausePrinceEngine(); + } + } + } + _mst_shadow2 = 0; + for (int i = 0; i < 20; i++) { + rememberScreenInv(); + _graph->drawTransparentSurface(_graph->_screenForInventory, 0, 0, suitcase); + drawInvItems(); + _graph->update(_graph->_screenForInventory); + Common::Event event; + Common::EventManager *eventMan = _system->getEventManager(); + eventMan->pollEvent(event); + if (shouldQuit()) { + return; + } + pausePrinceEngine(); + } +} + +void PrinceEngine::rememberScreenInv() { + _graph->_screenForInventory->copyFrom(*_graph->_frontScreen); +} + +void PrinceEngine::inventoryFlagChange(bool inventoryState) { + if (inventoryState) { + _showInventoryFlag = true; + _inventoryBackgroundRemember = true; + } else { + _showInventoryFlag = false; + } +} + +void PrinceEngine::prepareInventoryToView() { + _invMobList.clear(); + int invItem = _mainHero->_inventory.size(); + _invLine = invItem / 3; + if (invItem % 3) { + _invLine++; + } + if (_invLine < 4) { + _invLine = 4; + } + _maxInvW = (374 - 2 * _invLine) / _invLine; + _invLineW = _maxInvW - 2; + + int currInvX = _invLineX; + int currInvY = _invLineY; + + Common::MemoryReadStream stream(_invTxt, _invTxtSize); + byte c; + + uint item = 0; + for (int i = 0; i < _invLines; i++) { + for (int j = 0; j < _invLine; j++) { + Mob tempMobItem; + if (item < _mainHero->_inventory.size()) { + int itemNr = _mainHero->_inventory[item]; + tempMobItem._visible = 0; + tempMobItem._mask = itemNr; + tempMobItem._rect = Common::Rect(currInvX + _picWindowX, currInvY, currInvX + _picWindowX + _invLineW - 1, currInvY + _invLineH - 1); + tempMobItem._type = 0; // to work with checkMob() + + tempMobItem._name = ""; + tempMobItem._examText = ""; + int txtOffset = READ_LE_UINT32(&_invTxt[itemNr * 8]); + int examTxtOffset = READ_LE_UINT32(&_invTxt[itemNr * 8 + 4]); + + stream.seek(txtOffset); + while ((c = stream.readByte())) { + tempMobItem._name += c; + } + + stream.seek(examTxtOffset); + while ((c = stream.readByte())) { + tempMobItem._examText += c; + } + _invMobList.push_back(tempMobItem); + } + currInvX += _invLineW + _invLineSkipX; + item++; + } + currInvX = _invLineX; + currInvY += _invLineSkipY + _invLineH; + } +} + +void PrinceEngine::drawInvItems() { + int currInvX = _invLineX; + int currInvY = _invLineY; + uint item = 0; + for (int i = 0; i < _invLines; i++) { + for (int j = 0; j < _invLine; j++) { + if (item < _mainHero->_inventory.size()) { + int itemNr = _mainHero->_inventory[item]; + _mst_shadow = 0; + if (_mst_shadow2) { + if (!_flags->getFlagValue(Flags::CURSEBLINK)) { + if (item + 1 == _mainHero->_inventory.size()) { // last item in inventory + _mst_shadow = 1; + } + } else if (itemNr == 1 || itemNr == 3 || itemNr == 4 || itemNr == 7) { + _mst_shadow = 1; + } + } + + int drawX = currInvX; + int drawY = currInvY; + Graphics::Surface *itemSurface = nullptr; + if (itemNr != 68) { + itemSurface = _allInvList[itemNr].getSurface(); + if (itemSurface->h < _maxInvH) { + drawY += (_maxInvH - itemSurface->h) / 2; + } + } else { + // candle item: + if (_candleCounter == 8) { + _candleCounter = 0; + } + itemNr = _candleCounter; + _candleCounter++; + itemNr &= 7; + itemNr += 71; + itemSurface = _allInvList[itemNr].getSurface(); + drawY += _allInvList[itemNr]._y + (_maxInvH - 76) / 2 - 200; + } + if (itemSurface->w < _maxInvW) { + drawX += (_maxInvW - itemSurface->w) / 2; + } + if (!_mst_shadow) { + _graph->drawTransparentSurface(_graph->_screenForInventory, drawX, drawY, itemSurface); + } else { + _mst_shadow = _mst_shadow2; + _graph->drawTransparentWithBlendSurface(_graph->_screenForInventory, drawX, drawY, itemSurface); + } + } + currInvX += _invLineW + _invLineSkipX; + item++; + } + currInvX = _invLineX; + currInvY += _invLineSkipY + _invLineH; + } +} + +void PrinceEngine::inventoryLeftMouseButton() { + if (!_mouseFlag) { + _textSlots[0]._time = 0; + _textSlots[0]._str = nullptr; + stopSample(28); + } + + if (_optionsFlag == 1) { + if (_selectedMob != -1) { + if (_optionEnabled < _invOptionsNumber) { + _optionsFlag = 0; + } else { + return; + } + } else { + error("PrinceEngine::inventoryLeftMouseButton() - optionsFlag = 1, selectedMob = 0"); + if (_currentPointerNumber == 2) { + changeCursor(1); + _currentPointerNumber = 1; + _selectedMob = -1; + _optionsMob = -1; + return; + } else { + return; + } + } + } else { + if (_selectedMob != -1) { + if (_currentPointerNumber != 2) { + if (_invMobList[_selectedMob]._mask != 29) { + _optionEnabled = 0; + } else { + // map item + _optionEnabled = 1; + } + } else { + //use_item_on_item + int invObjUU = _script->scanMobEventsWithItem(_invMobList[_selectedMob]._mask, _script->_scriptInfo.invObjUU, _selectedItem); + if (invObjUU == -1) { + int textNr = 80011; // "I can't do it." + if (_selectedItem == 31 || _invMobList[_selectedMob]._mask == 31) { + textNr = 80020; // "Nothing is happening." + } + _interpreter->setCurrentString(textNr); + printAt(0, 216, (char *)_variaTxt->getString(textNr - 80000), kNormalWidth / 2, 100); + setVoice(0, 28, 1); + playSample(28, 0); + _selectedMob = -1; + _optionsMob = -1; + return; + } else { + _interpreter->storeNewPC(invObjUU); + _flags->setFlagValue(Flags::CURRMOB, _invMobList[_selectedMob]._mask); + _showInventoryFlag = false; + } + } + } else { + return; + } + } + //do_option + if (_optionEnabled == 0) { + int invObjExamEvent = _script->scanMobEvents(_invMobList[_selectedMob]._mask, _script->_scriptInfo.invObjExam); + if (invObjExamEvent == -1) { + // do_standard + // FIXME: UB? + // Constness of the pointer returned by c_str() is cast away (which generates a compiler warning) + // while it potentially gets modified inside printAt() + printAt(0, 216, (char *)_invMobList[_selectedMob]._examText.c_str(), kNormalWidth / 2, _invExamY); + _interpreter->setCurrentString(_invMobList[_selectedMob]._mask + 70000); + setVoice(0, 28, 1); + playSample(28, 0); + // disableuseuse + changeCursor(0); + _currentPointerNumber = 1; + } else { + _interpreter->storeNewPC(invObjExamEvent); + _flags->setFlagValue(Flags::CURRMOB, _invMobList[_selectedMob]._mask); + _showInventoryFlag = false; + } + } else if (_optionEnabled == 1) { + // not_examine + int invObjUse = _script->scanMobEvents(_invMobList[_selectedMob]._mask, _script->_scriptInfo.invObjUse); + if (invObjUse == -1) { + // do_standard_use + _selectedMode = 0; + _selectedItem = _invMobList[_selectedMob]._mask; + makeInvCursor(_invMobList[_selectedMob]._mask); + _currentPointerNumber = 2; + changeCursor(2); + } else { + _interpreter->storeNewPC(invObjUse); + _flags->setFlagValue(Flags::CURRMOB, _invMobList[_selectedMob]._mask); + _showInventoryFlag = false; + } + } else if (_optionEnabled == 4) { + // do_standard_give + _selectedMode = 1; + _selectedItem = _invMobList[_selectedMob]._mask; + makeInvCursor(_invMobList[_selectedMob]._mask); + _currentPointerNumber = 2; + changeCursor(2); + } else { + // use_item_on_item + int invObjUU = _script->scanMobEventsWithItem(_invMobList[_selectedMob]._mask, _script->_scriptInfo.invObjUU, _selectedItem); + if (invObjUU == -1) { + int textNr = 80011; // "I can't do it." + if (_selectedItem == 31 || _invMobList[_selectedMob]._mask == 31) { + textNr = 80020; // "Nothing is happening." + } + _interpreter->setCurrentString(textNr); + printAt(0, 216, (char *)_variaTxt->getString(textNr - 80000), kNormalWidth / 2, 100); + setVoice(0, 28, 1); + playSample(28, 0); + } else { + _interpreter->storeNewPC(invObjUU); + _flags->setFlagValue(Flags::CURRMOB, _invMobList[_selectedMob]._mask); + _showInventoryFlag = false; + } + } + _selectedMob = -1; + _optionsMob = -1; +} + +void PrinceEngine::inventoryRightMouseButton() { + if (_textSlots[0]._str == nullptr) { + enableOptions(false); + } +} + +void PrinceEngine::enableOptions(bool checkType) { + if (_optionsFlag != 1) { + changeCursor(1); + _currentPointerNumber = 1; + if (_selectedMob != -1) { + if (checkType) { + if (_mobList[_selectedMob]._type & 0x100) { + return; + } + } + Common::Point mousePos = _system->getEventManager()->getMousePos(); + int x1 = mousePos.x - _optionsWidth / 2; + int x2 = mousePos.x + _optionsWidth / 2; + if (x1 < 0) { + x1 = 0; + x2 = _optionsWidth; + } else if (x2 >= kNormalWidth) { + x1 = kNormalWidth - _optionsWidth; + x2 = kNormalWidth; + } + int y1 = mousePos.y - 10; + if (y1 < 0) { + y1 = 0; + } + if (y1 + _optionsHeight >= kNormalHeight) { + y1 = kNormalHeight - _optionsHeight; + } + _optionsMob = _selectedMob; + _optionsX = x1; + _optionsY = y1; + _optionsFlag = 1; + } + } +} + +void PrinceEngine::checkOptions() { + if (_optionsFlag) { + Common::Rect optionsRect(_optionsX, _optionsY, _optionsX + _optionsWidth, _optionsY + _optionsHeight); + Common::Point mousePos = _system->getEventManager()->getMousePos(); + if (!optionsRect.contains(mousePos)) { + _optionsFlag = 0; + _selectedMob = -1; + return; + } + _graph->drawAsShadowSurface(_graph->_frontScreen, _optionsX, _optionsY, _optionsPic, _graph->_shadowTable50); + + _optionEnabled = -1; + int optionsYCord = mousePos.y - (_optionsY + 16); + if (optionsYCord >= 0) { + int selectedOptionNr = optionsYCord / _optionsStep; + if (selectedOptionNr < _optionsNumber) { + _optionEnabled = selectedOptionNr; + } + } + int optionsColor; + int textY = _optionsY + 16; + for (int i = 0; i < _optionsNumber; i++) { + if (i != _optionEnabled) { + optionsColor = _optionsColor1; + } else { + optionsColor = _optionsColor2; + } + Common::String optText; + switch(getLanguage()) { + case Common::PL_POL: + optText = optionsTextPL[i]; + break; + case Common::DE_DEU: + optText = optionsTextDE[i]; + break; + case Common::EN_ANY: + optText = optionsTextEN[i]; + break; + case Common::RU_RUS: + optText = optionsTextRU[i]; + break; + default: + break; + }; + uint16 textW = getTextWidth(optText.c_str()); + uint16 textX = _optionsX + _optionsWidth / 2 - textW / 2; + _font->drawString(_graph->_frontScreen, optText, textX, textY, textW, optionsColor); + textY += _optionsStep; + } + } +} + +void PrinceEngine::checkInvOptions() { + if (_optionsFlag) { + Common::Rect optionsRect(_optionsX, _optionsY, _optionsX + _invOptionsWidth, _optionsY + _invOptionsHeight); + Common::Point mousePos = _system->getEventManager()->getMousePos(); + if (!optionsRect.contains(mousePos)) { + _optionsFlag = 0; + _selectedMob = -1; + return; + } + _graph->drawAsShadowSurface(_graph->_screenForInventory, _optionsX, _optionsY, _optionsPicInInventory, _graph->_shadowTable50); + + _optionEnabled = -1; + int optionsYCord = mousePos.y - (_optionsY + 16); + if (optionsYCord >= 0) { + int selectedOptionNr = optionsYCord / _invOptionsStep; + if (selectedOptionNr < _invOptionsNumber) { + _optionEnabled = selectedOptionNr; + } + } + int optionsColor; + int textY = _optionsY + 16; + for (int i = 0; i < _invOptionsNumber; i++) { + if (i != _optionEnabled) { + optionsColor = _optionsColor1; + } else { + optionsColor = _optionsColor2; + } + Common::String invText; + switch(getLanguage()) { + case Common::PL_POL: + invText = invOptionsTextPL[i]; + break; + case Common::DE_DEU: + invText = invOptionsTextDE[i]; + break; + case Common::EN_ANY: + invText = invOptionsTextEN[i]; + break; + case Common::RU_RUS: + invText = invOptionsTextRU[i]; + break; + default: + error("Unknown game language %d", getLanguage()); + break; + }; + uint16 textW = getTextWidth(invText.c_str()); + uint16 textX = _optionsX + _invOptionsWidth / 2 - textW / 2; + _font->drawString(_graph->_screenForInventory, invText, textX, textY, _graph->_screenForInventory->w, optionsColor); + textY += _invOptionsStep; + } + } +} + +void PrinceEngine::displayInventory() { + + _mainHero->freeOldMove(); + _secondHero->freeOldMove(); + + _interpreter->setFgOpcodePC(0); + + stopAllSamples(); + + prepareInventoryToView(); + + while (!shouldQuit()) { + + if (_textSlots[0]._str != nullptr) { + changeCursor(0); + } else { + changeCursor(_currentPointerNumber); + + Common::Rect inventoryRect(_invX1, _invY1, _invX1 + _invWidth, _invY1 + _invHeight); + Common::Point mousePos = _system->getEventManager()->getMousePos(); + + if (!_invCurInside && inventoryRect.contains(mousePos)) { + _invCurInside = true; + } + + if (_invCurInside && !inventoryRect.contains(mousePos)) { + inventoryFlagChange(false); + _invCurInside = false; + break; + } + } + + rememberScreenInv(); + + Graphics::Surface *suitcase = _suitcaseBmp->getSurface(); + _graph->drawTransparentSurface(_graph->_screenForInventory, 0, 0, suitcase); + + drawInvItems(); + + showTexts(_graph->_screenForInventory); + + if (!_optionsFlag && _textSlots[0]._str == nullptr) { + _selectedMob = checkMob(_graph->_screenForInventory, _invMobList, false); + } + + checkInvOptions(); + + Common::Event event; + Common::EventManager *eventMan = _system->getEventManager(); + while (eventMan->pollEvent(event)) { + switch (event.type) { + case Common::EVENT_KEYDOWN: + keyHandler(event); + break; + case Common::EVENT_LBUTTONDOWN: + inventoryLeftMouseButton(); + break; + case Common::EVENT_RBUTTONDOWN: + inventoryRightMouseButton(); + break; + default: + break; + } + } + + if (!_showInventoryFlag) { + break; + } + + if (shouldQuit()) + return; + + getDebugger()->onFrame(); + _graph->update(_graph->_screenForInventory); + pausePrinceEngine(); + } + + if (_currentPointerNumber == 2) { + _flags->setFlagValue(Flags::SELITEM, _selectedItem); + } else { + _flags->setFlagValue(Flags::SELITEM, 0); + } +} + +void PrinceEngine::openInventoryCheck() { + if (!_optionsFlag) { + if (_mouseFlag == 1 || _mouseFlag == 2) { + if (_mainHero->_visible) { + if (!_flags->getFlagValue(Flags::INVALLOWED)) { + // 29 - Basement, 50 - Map + if (_locationNr != 29 && _locationNr != 50) { + Common::Point mousePos = _system->getEventManager()->getMousePos(); + if (mousePos.y < 4 && !_showInventoryFlag) { + _invCounter++; + } else { + _invCounter = 0; + } + if (_invCounter >= _invMaxCount) { + inventoryFlagChange(true); + } + } + } + } + } + } +} + +} // End of namespace Prince diff --git a/engines/prince/mob.cpp b/engines/prince/mob.cpp index b170ba324b..de15991dba 100644 --- a/engines/prince/mob.cpp +++ b/engines/prince/mob.cpp @@ -20,7 +20,11 @@ * */ +#include "prince/prince.h" + #include "prince/mob.h" +#include "prince/animation.h" +#include "prince/font.h" namespace Prince { @@ -105,4 +109,158 @@ uint16 Mob::getData(AttrId dataId) { } } +int PrinceEngine::getMob(Common::Array<Mob> &mobList, bool usePriorityList, int posX, int posY) { + + Common::Point pointPos(posX, posY); + + int mobListSize; + if (usePriorityList) { + mobListSize = _mobPriorityList.size(); + } else { + mobListSize = mobList.size(); + } + + for (int mobNumber = 0; mobNumber < mobListSize; mobNumber++) { + Mob *mob = nullptr; + if (usePriorityList) { + mob = &mobList[_mobPriorityList[mobNumber]]; + } else { + mob = &mobList[mobNumber]; + } + + if (mob->_visible) { + continue; + } + + int type = mob->_type & 7; + switch (type) { + case 0: + case 1: + //normal_mob + if (!mob->_rect.contains(pointPos)) { + continue; + } + break; + case 3: + //mob_obj + if (mob->_mask < kMaxObjects) { + int nr = _objSlot[mob->_mask]; + if (nr != 0xFF) { + Object &obj = *_objList[nr]; + Common::Rect objectRect(obj._x, obj._y, obj._x + obj._width, obj._y + obj._height); + if (objectRect.contains(pointPos)) { + Graphics::Surface *objSurface = obj.getSurface(); + byte *pixel = (byte *)objSurface->getBasePtr(posX - obj._x, posY - obj._y); + if (*pixel != 255) { + break; + } + } + } + } + continue; + break; + case 2: + case 5: + //check_ba_mob + if (!_backAnimList[mob->_mask].backAnims.empty()) { + int currentAnim = _backAnimList[mob->_mask]._seq._currRelative; + Anim &backAnim = _backAnimList[mob->_mask].backAnims[currentAnim]; + if (backAnim._animData != nullptr) { + if (!backAnim._state) { + Common::Rect backAnimRect(backAnim._currX, backAnim._currY, backAnim._currX + backAnim._currW, backAnim._currY + backAnim._currH); + if (backAnimRect.contains(pointPos)) { + int phase = backAnim._showFrame; + int phaseFrameIndex = backAnim._animData->getPhaseFrameIndex(phase); + Graphics::Surface *backAnimSurface = backAnim._animData->getFrame(phaseFrameIndex); + byte pixel = *(byte *)backAnimSurface->getBasePtr(posX - backAnim._currX, posY - backAnim._currY); + if (pixel != 255) { + if (type == 5) { + if (mob->_rect.contains(pointPos)) { + break; + } + } else { + break; + } + } + } + } + } + } + continue; + break; + default: + //not_part_ba + continue; + break; + } + + if (usePriorityList) { + return _mobPriorityList[mobNumber]; + } else { + return mobNumber; + } + } + return -1; +} + +int PrinceEngine::checkMob(Graphics::Surface *screen, Common::Array<Mob> &mobList, bool usePriorityList) { + if (_mouseFlag == 0 || _mouseFlag == 3) { + return -1; + } + Common::Point mousePos = _system->getEventManager()->getMousePos(); + int mobNumber = getMob(mobList, usePriorityList, mousePos.x + _picWindowX, mousePos.y); + + if (mobNumber != -1) { + Common::String mobName = mobList[mobNumber]._name; + + if (getLanguage() == Common::DE_DEU) { + for (uint i = 0; i < mobName.size(); i++) { + switch (mobName[i]) { + case '\xc4': + mobName.setChar('\x83', i); + break; + case '\xd6': + mobName.setChar('\x84', i); + break; + case '\xdc': + mobName.setChar('\x85', i); + break; + case '\xdf': + mobName.setChar('\x7f', i); + break; + case '\xe4': + mobName.setChar('\x80', i); + break; + case '\xf6': + mobName.setChar('\x81', i); + break; + case '\xfc': + mobName.setChar('\x82', i); + break; + } + } + } + + uint16 textW = getTextWidth(mobName.c_str()); + + uint16 x = mousePos.x - textW / 2; + if (x > screen->w) { + x = 0; + } + + if (x + textW > screen->w) { + x = screen->w - textW; + } + + uint16 y = mousePos.y - _font->getFontHeight(); + if (y > screen->h) { + y = _font->getFontHeight() - 2; + } + + _font->drawString(screen, mobName, x, y, screen->w, 216); + } + + return mobNumber; +} + } // End of namespace Prince diff --git a/engines/prince/module.mk b/engines/prince/module.mk index 27bbc21700..0a24c0c243 100644 --- a/engines/prince/module.mk +++ b/engines/prince/module.mk @@ -7,20 +7,25 @@ MODULE_OBJS = \ debugger.o \ decompress.o \ detection.o \ + draw.o \ flags.o \ font.o \ graphics.o \ hero.o \ + inventory.o \ mhwanh.o \ + music.o \ mob.o \ object.o \ prince.o \ pscr.o \ + resource.o \ saveload.o \ script.o \ sound.o \ variatxt.o \ - videoplayer.o + videoplayer.o \ + walk.o # This module can be built as a plugin ifeq ($(ENABLE_PRINCE), DYNAMIC_PLUGIN) diff --git a/engines/prince/music.cpp b/engines/prince/music.cpp new file mode 100644 index 0000000000..58a1c1c02a --- /dev/null +++ b/engines/prince/music.cpp @@ -0,0 +1,236 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can 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 "prince/prince.h" +#include "prince/music.h" +#include "prince/musNum.h" +#include "prince/resource.h" + +#include "common/archive.h" +#include "common/debug.h" +#include "audio/mididrv.h" +#include "audio/midiparser.h" + +namespace Prince { + +const char *MusicPlayer::_musTable[] = { + "", + "Battlfld.mid", + "Cave.mid", + "Cemetery.mid", + "Credits.mid", + "Fjord.mid", + "Guitar.mid", + "Hell.mid", + "Jingle.mid", + "Main.mid", + "Night.mid", + "Reality.mid", + "Sunlord.mid", + "Tavern.mid", + "Temple.mid", + "Boruta.mid", + "Intro.mid" +}; + +const uint8 MusicPlayer::_musRoomTable[] = { + 0, + ROOM01MUS, + ROOM02MUS, + ROOM03MUS, + ROOM04MUS, + ROOM05MUS, + ROOM06MUS, + ROOM07MUS, + ROOM08MUS, + ROOM09MUS, + ROOM10MUS, + ROOM11MUS, + ROOM12MUS, + ROOM13MUS, + ROOM14MUS, + ROOM15MUS, + ROOM16MUS, + ROOM17MUS, + ROOM18MUS, + ROOM19MUS, + ROOM20MUS, + ROOM21MUS, + ROOM22MUS, + ROOM23MUS, + ROOM24MUS, + ROOM25MUS, + ROOM26MUS, + ROOM27MUS, + ROOM28MUS, + ROOM29MUS, + ROOM30MUS, + ROOM31MUS, + ROOM32MUS, + ROOM33MUS, + ROOM34MUS, + ROOM35MUS, + ROOM36MUS, + ROOM37MUS, + ROOM38MUS, + ROOM39MUS, + ROOM40MUS, + ROOM41MUS, + ROOM42MUS, + ROOM43MUS, + 0, + 0, + ROOM46MUS, + ROOM47MUS, + ROOM48MUS, + ROOM49MUS, + ROOM50MUS, + ROOM51MUS, + ROOM52MUS, + ROOM53MUS, + ROOM54MUS, + ROOM55MUS, + ROOM56MUS, + ROOM57MUS, + ROOM58MUS, + ROOM59MUS, + ROOM60MUS, + ROOM61MUS +}; + + +MusicPlayer::MusicPlayer(PrinceEngine *vm) : _vm(vm) { + _data = nullptr; + _dataSize = 0; + _isGM = false; + + MidiPlayer::createDriver(); + + int ret = _driver->open(); + if (ret == 0) { + if (_nativeMT32) + _driver->sendMT32Reset(); + else + _driver->sendGMReset(); + + _driver->setTimerCallback(this, &timerCallback); + } +} + +MusicPlayer::~MusicPlayer() { + killMidi(); +} + +void MusicPlayer::killMidi() { + Audio::MidiPlayer::stop(); + + free(_data); + _data = nullptr; +} + +void MusicPlayer::loadMidi(const char *name) { + Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(name); + if (!stream) { + warning("Can't load midi stream %s", name); + return; + } + + stream = Resource::getDecompressedStream(stream); + + // Stop any currently playing MIDI file + killMidi(); + + // Read in the data for the file + _dataSize = stream->size(); + _data = (byte *)malloc(_dataSize); + stream->read(_data, _dataSize); + + delete stream; + + // Start playing the music + sndMidiStart(); +} + +void MusicPlayer::sndMidiStart() { + _isGM = true; + + MidiParser *parser = MidiParser::createParser_SMF(); + if (parser->loadMusic(_data, _dataSize)) { + parser->setTrack(0); + parser->setMidiDriver(this); + parser->setTimerRate(_driver->getBaseTempo()); + parser->property(MidiParser::mpCenterPitchWheelOnUnload, 1); + + _parser = parser; + + syncVolume(); + + // Al the tracks are supposed to loop + _isLooping = true; + _isPlaying = true; + } +} + +void MusicPlayer::send(uint32 b) { + if ((b & 0xF0) == 0xC0 && !_isGM && !_nativeMT32) { + b = (b & 0xFFFF00FF) | MidiDriver::_mt32ToGm[(b >> 8) & 0xFF] << 8; + } + + Audio::MidiPlayer::send(b); +} + +void MusicPlayer::sendToChannel(byte channel, uint32 b) { + if (!_channelsTable[channel]) { + _channelsTable[channel] = (channel == 9) ? _driver->getPercussionChannel() : _driver->allocateChannel(); + // If a new channel is allocated during the playback, make sure + // its volume is correctly initialized. + if (_channelsTable[channel]) + _channelsTable[channel]->volume(_channelsVolume[channel] * _masterVolume / 255); + } + + if (_channelsTable[channel]) + _channelsTable[channel]->send(b); +} + +bool PrinceEngine::loadMusic(int musNumber) { + uint8 midiNumber = MusicPlayer::_musRoomTable[musNumber]; + if (midiNumber) { + if (midiNumber != 100) { + if (_currentMidi != midiNumber) { + _currentMidi = midiNumber; + const char *musName = MusicPlayer::_musTable[_currentMidi]; + _midiPlayer->loadMidi(musName); + } + } + } else { + stopMusic(); + } + return true; +} + +void PrinceEngine::stopMusic() { + if (_midiPlayer->isPlaying()) { + _midiPlayer->stop(); + } +} + +} // End of namespace Prince diff --git a/engines/prince/sound.h b/engines/prince/music.h index 21f9e48b37..061e10c8b8 100644 --- a/engines/prince/sound.h +++ b/engines/prince/music.h @@ -20,8 +20,8 @@ * */ -#ifndef PRINCE_SOUND_H -#define PRINCE_SOUND_H +#ifndef PRINCE_MUSIC_H +#define PRINCE_MUSIC_H #include "audio/midiplayer.h" #include "common/memstream.h" diff --git a/engines/prince/object.cpp b/engines/prince/object.cpp index e4a3eda689..109d180355 100644 --- a/engines/prince/object.cpp +++ b/engines/prince/object.cpp @@ -28,6 +28,7 @@ #include "graphics/surface.h" #include "prince/object.h" +#include "prince/resource.h" namespace Prince { @@ -73,6 +74,8 @@ bool Object::loadFromStream(Common::SeekableReadStream &stream) { const Common::String obStreamName = Common::String::format("OB%02d", stream.readUint16LE()); Common::SeekableReadStream *obStream = SearchMan.createReadStreamForMember(obStreamName); if (obStream) { + obStream = Resource::getDecompressedStream(obStream); + loadSurface(*obStream); } delete obStream; diff --git a/engines/prince/option_text.h b/engines/prince/option_text.h index f56dd421ec..cc97f042af 100644 --- a/engines/prince/option_text.h +++ b/engines/prince/option_text.h @@ -82,4 +82,23 @@ const char *optionsTextEN[] = { "Talk to" }; +// RU +const char *invOptionsTextRU[] = { + "Cvjnhtnm", + "Bcgjkmp.", + "Jnrhsnm/""\x83""bnm ", + "Pfrhsnm/Nzyenm ", + "Lfnm " +}; + +const char *optionsTextRU[] = { + "Gjlevfnm", + "Jcvjnhtnm", + "Dpznm ", + "Bcgjkmp.", + "Jnrhsnm/""\x83""bnm ", + "Pfrhsnm/Nzyenm ", + "Ujdjhbnm " +}; + } // End of namespace Prince diff --git a/engines/prince/prince.cpp b/engines/prince/prince.cpp index 016428935c..cb314aa058 100644 --- a/engines/prince/prince.cpp +++ b/engines/prince/prince.cpp @@ -32,34 +32,25 @@ #include "common/substream.h" #include "common/str.h" -#include "graphics/cursorman.h" #include "graphics/surface.h" -#include "graphics/palette.h" #include "graphics/pixelformat.h" #include "engines/util.h" -#include "audio/audiostream.h" -#include "audio/decoders/wave.h" - #include "prince/prince.h" -#include "prince/font.h" #include "prince/graphics.h" #include "prince/script.h" #include "prince/debugger.h" #include "prince/object.h" #include "prince/mob.h" -#include "prince/sound.h" +#include "prince/music.h" #include "prince/variatxt.h" -#include "prince/flags.h" #include "prince/font.h" #include "prince/mhwanh.h" #include "prince/cursor.h" #include "prince/archive.h" #include "prince/hero.h" -#include "prince/resource.h" #include "prince/animation.h" -#include "prince/option_text.h" #include "prince/curve_values.h" namespace Prince { @@ -217,14 +208,17 @@ void PrinceEngine::init() { debugEngine("Adding all path: %s", gameDataDir.getPath().c_str()); - if (getLanguage() != Common::RU_RUS) { + if (!(getFeatures() & GF_EXTRACTED)) { PtcArchive *all = new PtcArchive(); if (!all->open("all/databank.ptc")) error("Can't open all/databank.ptc"); PtcArchive *voices = new PtcArchive(); - if (!voices->open("voices/databank.ptc")) - error("Can't open voices/databank.ptc"); + + if (!(getFeatures() & GF_NOVOICES)) { + if (!voices->open("voices/databank.ptc")) + error("Can't open voices/databank.ptc"); + } PtcArchive *sound = new PtcArchive(); if (!sound->open("sound/databank.ptc")) @@ -331,6 +325,9 @@ void PrinceEngine::init() { error("Can't load dialogDatStream"); return; } + + dialogDatStream = Resource::getDecompressedStream(dialogDatStream); + _dialogDatSize = dialogDatStream->size(); _dialogDat = (byte *)malloc(_dialogDatSize); dialogDatStream->read(_dialogDat, _dialogDatSize); @@ -464,148 +461,11 @@ void PrinceEngine::pauseEngineIntern(bool pause) { Engine::pauseEngineIntern(pause); if (pause) { _midiPlayer->pause(); - } - else { + } else { _midiPlayer->resume(); } } -bool AnimListItem::loadFromStream(Common::SeekableReadStream &stream) { - int32 pos = stream.pos(); - - uint16 type = stream.readUint16LE(); - if (type == 0xFFFF) { - return false; - } - _type = type; - _fileNumber = stream.readUint16LE(); - _startPhase = stream.readUint16LE(); - _endPhase = stream.readUint16LE(); - _loopPhase = stream.readUint16LE(); - _x = stream.readSint16LE(); - _y = stream.readSint16LE(); - _loopType = stream.readUint16LE(); - _nextAnim = stream.readUint16LE(); - _flags = stream.readUint16LE(); - - //debug("AnimListItem type %d, fileNumber %d, x %d, y %d, flags %d", _type, _fileNumber, _x, _y, _flags); - //debug("startPhase %d, endPhase %d, loopPhase %d", _startPhase, _endPhase, _loopPhase); - - // 32 byte aligment - stream.seek(pos + 32); - - return true; -} - -bool PrinceEngine::loadLocation(uint16 locationNr) { - - blackPalette(); - - _flicPlayer.close(); - - memset(_textSlots, 0, sizeof(_textSlots)); - freeAllSamples(); - - debugEngine("PrinceEngine::loadLocation %d", locationNr); - const Common::FSNode gameDataDir(ConfMan.get("path")); - SearchMan.remove(Common::String::format("%02d", _locationNr)); - - _locationNr = locationNr; - _debugger->_locationNr = locationNr; - - _flags->setFlagValue(Flags::CURRROOM, _locationNr); - _interpreter->stopBg(); - - changeCursor(0); - - const Common::String locationNrStr = Common::String::format("%02d", _locationNr); - debugEngine("loadLocation %s", locationNrStr.c_str()); - - PtcArchive *locationArchive = new PtcArchive(); - if (!locationArchive->open(locationNrStr + "/databank.ptc")) - error("Can't open location %s", locationNrStr.c_str()); - - SearchMan.add(locationNrStr, locationArchive); - - loadMusic(_locationNr); - - // load location background, replace old one - Resource::loadResource(_roomBmp, "room", true); - if (_roomBmp->getSurface()) { - _sceneWidth = _roomBmp->getSurface()->w; - } - - loadZoom(_zoomBitmap, kZoomBitmapLen, "zoom"); - loadShadow(_shadowBitmap, kShadowBitmapSize, "shadow", "shadow2"); - loadTrans(_transTable, "trans"); - loadPath("path"); - - for (uint32 i = 0; i < _pscrList.size(); i++) { - delete _pscrList[i]; - } - _pscrList.clear(); - Resource::loadResource(_pscrList, "pscr.lst", false); - - loadMobPriority("mobpri"); - - _mobList.clear(); - if (getGameType() == kPrinceDataDE) { - const Common::String mobLstName = Common::String::format("mob%02d.lst", _locationNr); - debug("name: %s", mobLstName.c_str()); - Resource::loadResource(_mobList, mobLstName.c_str(), false); - } else if (getGameType() == kPrinceDataPL) { - Resource::loadResource(_mobList, "mob.lst", false); - } - if (getFeatures() & GF_TRANSLATED) { - // update Mob texts for translated version - setMobTranslationTexts(); - } - - _animList.clear(); - Resource::loadResource(_animList, "anim.lst", false); - - for (uint32 i = 0; i < _objList.size(); i++) { - delete _objList[i]; - } - _objList.clear(); - Resource::loadResource(_objList, "obj.lst", false); - - _room->loadRoom(_script->getRoomOffset(_locationNr)); - - for (uint i = 0; i < _maskList.size(); i++) { - free(_maskList[i]._data); - } - _maskList.clear(); - _script->loadAllMasks(_maskList, _room->_nak); - - _picWindowX = 0; - - _lightX = _script->getLightX(_locationNr); - _lightY = _script->getLightY(_locationNr); - setShadowScale(_script->getShadowScale(_locationNr)); - - for (uint i = 0; i < _mobList.size(); i++) { - _mobList[i]._visible = _script->getMobVisible(_room->_mobs, i); - } - - _script->installObjects(_room->_obj); - - freeAllNormAnims(); - - clearBackAnimList(); - _script->installBackAnims(_backAnimList, _room->_backAnim); - - _graph->makeShadowTable(70, _graph->_shadowTable70); - _graph->makeShadowTable(50, _graph->_shadowTable50); - - _mainHero->freeOldMove(); - _secondHero->freeOldMove(); - - _mainHero->scrollHero(); - - return true; -} - void PrinceEngine::setShadowScale(int32 shadowScale) { shadowScale = 100 - shadowScale; if (!shadowScale) { @@ -622,122 +482,6 @@ void PrinceEngine::plotShadowLinePoint(int x, int y, int color, void *data) { vm->_shadLineLen++; } -void PrinceEngine::changeCursor(uint16 curId) { - _debugger->_cursorNr = curId; - _mouseFlag = curId; - _flags->setFlagValue(Flags::MOUSEENABLED, curId); - - const Graphics::Surface *curSurface = nullptr; - - switch (curId) { - default: - error("Unknown cursor Id: %d", curId); - case 0: - CursorMan.showMouse(false); - _optionsFlag = 0; - _selectedMob = -1; - return; - case 1: - curSurface = _cursor1->getSurface(); - break; - case 2: - curSurface = _cursor2; - break; - case 3: - curSurface = _cursor3->getSurface(); - Common::Point mousePos = _system->getEventManager()->getMousePos(); - mousePos.x = CLIP(mousePos.x, (int16) 315, (int16) 639); - mousePos.y = CLIP(mousePos.y, (int16) 0, (int16) 170); - _system->warpMouse(mousePos.x, mousePos.y); - break; - } - - CursorMan.replaceCursorPalette(_roomBmp->getPalette(), 0, 255); - CursorMan.replaceCursor( - curSurface->getBasePtr(0, 0), - curSurface->w, curSurface->h, - 0, 0, - 255, false, - &curSurface->format - ); - CursorMan.showMouse(true); -} - -void PrinceEngine::makeInvCursor(int itemNr) { - const Graphics::Surface *cur1Surface = _cursor1->getSurface(); - int cur1W = cur1Surface->w; - int cur1H = cur1Surface->h; - const Common::Rect cur1Rect(0, 0, cur1W, cur1H); - - const Graphics::Surface *itemSurface = _allInvList[itemNr].getSurface(); - int itemW = itemSurface->w; - int itemH = itemSurface->h; - - int cur2W = cur1W + itemW / 2; - int cur2H = cur1H + itemH / 2; - - if (_cursor2 != nullptr) { - _cursor2->free(); - delete _cursor2; - } - _cursor2 = new Graphics::Surface(); - _cursor2->create(cur2W, cur2H, Graphics::PixelFormat::createFormatCLUT8()); - Common::Rect cur2Rect(0, 0, cur2W, cur2H); - _cursor2->fillRect(cur2Rect, 255); - _cursor2->copyRectToSurface(*cur1Surface, 0, 0, cur1Rect); - - const byte *src1 = (const byte *)itemSurface->getBasePtr(0, 0); - byte *dst1 = (byte *)_cursor2->getBasePtr(cur1W, cur1H); - - if (itemH % 2) { - itemH--; - } - if (itemW % 2) { - itemW--; - } - - for (int y = 0; y < itemH; y++) { - const byte *src2 = src1; - byte *dst2 = dst1; - if (y % 2 == 0) { - for (int x = 0; x < itemW; x++, src2++) { - if (x % 2 == 0) { - if (*src2) { - *dst2 = *src2; - } else { - *dst2 = 255; - } - dst2++; - } - } - dst1 += _cursor2->pitch; - } - src1 += itemSurface->pitch; - } -} - -bool PrinceEngine::loadMusic(int musNumber) { - uint8 midiNumber = MusicPlayer::_musRoomTable[musNumber]; - if (midiNumber) { - if (midiNumber != 100) { - if (_currentMidi != midiNumber) { - _currentMidi = midiNumber; - const char *musName = MusicPlayer::_musTable[_currentMidi]; - _midiPlayer->loadMidi(musName); - } - } - } else { - stopMusic(); - } - return true; -} - -void PrinceEngine::stopMusic() { - if (_midiPlayer->isPlaying()) { - _midiPlayer->stop(); - } -} - bool PrinceEngine::playNextFLCFrame() { if (!_flicPlayer.isVideoLoaded()) return false; @@ -758,285 +502,6 @@ bool PrinceEngine::playNextFLCFrame() { return true; } -void PrinceEngine::playSample(uint16 sampleId, uint16 loopType) { - if (_audioStream[sampleId]) { - if (_mixer->isSoundIDActive(sampleId)) { - return; - } - _audioStream[sampleId]->rewind(); - if (sampleId < 28) { - _mixer->playStream(Audio::Mixer::kSFXSoundType, &_soundHandle[sampleId], _audioStream[sampleId], sampleId, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO); - } else { - _mixer->playStream(Audio::Mixer::kSpeechSoundType, &_soundHandle[sampleId], _audioStream[sampleId], sampleId, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO); - } - } -} - -void PrinceEngine::stopSample(uint16 sampleId) { - _mixer->stopID(sampleId); -} - -void PrinceEngine::stopAllSamples() { - _mixer->stopAll(); -} - -void PrinceEngine::freeSample(uint16 sampleId) { - stopSample(sampleId); - if (_audioStream[sampleId] != nullptr) { - delete _audioStream[sampleId]; - _audioStream[sampleId] = nullptr; - } -} - -void PrinceEngine::freeAllSamples() { - for (int sampleId = 0; sampleId < kMaxSamples; sampleId++) { - freeSample(sampleId); - } -} - -bool PrinceEngine::loadSample(uint32 sampleSlot, const Common::String &streamName) { - // FIXME: This is just a workaround streamName is a path - // SOUND\\SCIERKA1.WAV for now only last path component is used - Common::String normalizedPath = lastPathComponent(streamName, '\\'); - - // WALKAROUND: Wrong name in script, not existing sound in data files - if (!normalizedPath.compareTo("9997BEKA.WAV")) { - return 0; - } - - debugEngine("loadSample slot %d, name %s", sampleSlot, normalizedPath.c_str()); - - freeSample(sampleSlot); - Common::SeekableReadStream *sampleStream = SearchMan.createReadStreamForMember(normalizedPath); - if (sampleStream == nullptr) { - delete sampleStream; - error("Can't load sample %s to slot %d", normalizedPath.c_str(), sampleSlot); - } - _audioStream[sampleSlot] = Audio::makeWAVStream(sampleStream, DisposeAfterUse::NO); - delete sampleStream; - return true; -} - -bool PrinceEngine::loadVoice(uint32 slot, uint32 sampleSlot, const Common::String &streamName) { - debugEngine("Loading wav %s slot %d", streamName.c_str(), slot); - - if (slot >= kMaxTexts) { - error("Text slot bigger than MAXTEXTS %d", kMaxTexts - 1); - return false; - } - - freeSample(sampleSlot); - Common::SeekableReadStream *sampleStream = SearchMan.createReadStreamForMember(streamName); - if (sampleStream == nullptr) { - debug("Can't open %s", streamName.c_str()); - return false; - } - - uint32 id = sampleStream->readUint32LE(); - if (id != MKTAG('F', 'F', 'I', 'R')) { - error("It's not RIFF file %s", streamName.c_str()); - return false; - } - - sampleStream->skip(0x20); - id = sampleStream->readUint32LE(); - if (id != MKTAG('a', 't', 'a', 'd')) { - error("No data section in %s id %04x", streamName.c_str(), id); - return false; - } - - id = sampleStream->readUint32LE(); - debugEngine("SetVoice slot %d time %04x", slot, id); - id <<= 3; - id /= 22050; - id += 2; - - _textSlots[slot]._time = id; - if (!slot) { - _mainHero->_talkTime = id; - } else if (slot == 1) { - _secondHero->_talkTime = id; - } - - debugEngine("SetVoice slot %d time %04x", slot, id); - sampleStream->seek(SEEK_SET); - _audioStream[sampleSlot] = Audio::makeWAVStream(sampleStream, DisposeAfterUse::NO); - delete sampleStream; - return true; -} - -void PrinceEngine::setVoice(uint16 slot, uint32 sampleSlot, uint16 flag) { - Common::String sampleName; - uint32 currentString = _interpreter->getCurrentString(); - - if (currentString >= 80000) { - uint32 nr = currentString - 80000; - sampleName = Common::String::format("%02d0%02d-%02d.WAV", nr / 100, nr % 100, flag); - } else if (currentString >= 70000) { - sampleName = Common::String::format("inv%02d-01.WAV", currentString - 70000); - } else if (currentString >= 60000) { - sampleName = Common::String::format("M%04d-%02d.WAV", currentString - 60000, flag); - } else if (currentString >= 2000) { - return; - } else if (flag >= 100) { - sampleName = Common::String::format("%03d-%03d.WAV", currentString, flag); - } else { - sampleName = Common::String::format("%03d-%02d.WAV", currentString, flag); - } - - loadVoice(slot, sampleSlot, sampleName); -} - -bool PrinceEngine::loadAnim(uint16 animNr, bool loop) { - Common::String streamName = Common::String::format("AN%02d", animNr); - Common::SeekableReadStream *flicStream = SearchMan.createReadStreamForMember(streamName); - - if (!flicStream) { - error("Can't open %s", streamName.c_str()); - return false; - } - - if (!_flicPlayer.loadStream(flicStream)) { - error("Can't load flic stream %s", streamName.c_str()); - } - - debugEngine("%s loaded", streamName.c_str()); - _flicLooped = loop; - _flicPlayer.start(); - playNextFLCFrame(); - return true; -} - -bool PrinceEngine::loadZoom(byte *zoomBitmap, uint32 dataSize, const char *resourceName) { - Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(resourceName); - if (!stream) { - delete stream; - return false; - } - if (stream->read(zoomBitmap, dataSize) != dataSize) { - free(zoomBitmap); - delete stream; - return false; - } - delete stream; - return true; -} - -bool PrinceEngine::loadShadow(byte *shadowBitmap, uint32 dataSize, const char *resourceName1, const char *resourceName2) { - Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(resourceName1); - if (!stream) { - delete stream; - return false; - } - - if (stream->read(shadowBitmap, dataSize) != dataSize) { - free(shadowBitmap); - delete stream; - return false; - } - - Common::SeekableReadStream *stream2 = SearchMan.createReadStreamForMember(resourceName2); - if (!stream2) { - delete stream; - delete stream2; - return false; - } - - byte *shadowBitmap2 = shadowBitmap + dataSize; - if (stream2->read(shadowBitmap2, dataSize) != dataSize) { - free(shadowBitmap); - delete stream; - delete stream2; - return false; - } - - delete stream; - delete stream2; - return true; -} - -bool PrinceEngine::loadTrans(byte *transTable, const char *resourceName) { - Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(resourceName); - if (!stream) { - delete stream; - for (int i = 0; i < 256; i++) { - for (int j = 0; j < 256; j++) { - transTable[i * 256 + j] = j; - } - } - return true; - } - if (stream->read(transTable, kTransTableSize) != kTransTableSize) { - delete stream; - return false; - } - delete stream; - return true; -} - -bool PrinceEngine::loadPath(const char *resourceName) { - Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(resourceName); - if (!stream) { - delete stream; - return false; - } - if (stream->read(_roomPathBitmap, kPathBitmapLen) != kPathBitmapLen) { - delete stream; - return false; - } - delete stream; - return true; -} - -bool PrinceEngine::loadAllInv() { - for (int i = 0; i < kMaxInv; i++) { - InvItem tempInvItem; - - const Common::String invStreamName = Common::String::format("INV%02d", i); - Common::SeekableReadStream *invStream = SearchMan.createReadStreamForMember(invStreamName); - if (!invStream) { - delete invStream; - return true; - } - - tempInvItem._x = invStream->readUint16LE(); - tempInvItem._y = invStream->readUint16LE(); - int width = invStream->readUint16LE(); - int height = invStream->readUint16LE(); - tempInvItem._surface = new Graphics::Surface(); - tempInvItem._surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8()); - - for (int h = 0; h < tempInvItem._surface->h; h++) { - invStream->read(tempInvItem._surface->getBasePtr(0, h), tempInvItem._surface->w); - } - - _allInvList.push_back(tempInvItem); - delete invStream; - } - - return true; -} - -bool PrinceEngine::loadMobPriority(const char *resourceName) { - Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(resourceName); - if (!stream) { - delete stream; - return false; - } - - _mobPriorityList.clear(); - uint mobId; - while (1) { - mobId = stream->readUint32LE(); - if (mobId == 0xFFFFFFFF) { - break; - } - _mobPriorityList.push_back(mobId); - } - delete stream; - return true; -} - void PrinceEngine::loadMobTranslationTexts() { Common::SeekableReadStream *mobTranslationStream = SearchMan.createReadStreamForMember("mob_translate.dat"); if (!mobTranslationStream) { @@ -1079,6 +544,14 @@ void PrinceEngine::setMobTranslationTexts() { void PrinceEngine::keyHandler(Common::Event event) { uint16 nChar = event.kbd.keycode; switch (nChar) { + case Common::KEYCODE_F1: + if (canLoadGameStateCurrently()) + scummVMSaveLoadDialog(false); + break; + case Common::KEYCODE_F2: + if (canSaveGameStateCurrently()) + scummVMSaveLoadDialog(true); + break; case Common::KEYCODE_d: if (event.kbd.hasFlags(Common::KBD_CTRL)) { getDebugger()->attach(); @@ -1100,160 +573,6 @@ void PrinceEngine::keyHandler(Common::Event event) { } } -int PrinceEngine::getMob(Common::Array<Mob> &mobList, bool usePriorityList, int posX, int posY) { - - Common::Point pointPos(posX, posY); - - int mobListSize; - if (usePriorityList) { - mobListSize = _mobPriorityList.size(); - } else { - mobListSize = mobList.size(); - } - - for (int mobNumber = 0; mobNumber < mobListSize; mobNumber++) { - Mob *mob = nullptr; - if (usePriorityList) { - mob = &mobList[_mobPriorityList[mobNumber]]; - } else { - mob = &mobList[mobNumber]; - } - - if (mob->_visible) { - continue; - } - - int type = mob->_type & 7; - switch (type) { - case 0: - case 1: - //normal_mob - if (!mob->_rect.contains(pointPos)) { - continue; - } - break; - case 3: - //mob_obj - if (mob->_mask < kMaxObjects) { - int nr = _objSlot[mob->_mask]; - if (nr != 0xFF) { - Object &obj = *_objList[nr]; - Common::Rect objectRect(obj._x, obj._y, obj._x + obj._width, obj._y + obj._height); - if (objectRect.contains(pointPos)) { - Graphics::Surface *objSurface = obj.getSurface(); - byte *pixel = (byte *)objSurface->getBasePtr(posX - obj._x, posY - obj._y); - if (*pixel != 255) { - break; - } - } - } - } - continue; - break; - case 2: - case 5: - //check_ba_mob - if (!_backAnimList[mob->_mask].backAnims.empty()) { - int currentAnim = _backAnimList[mob->_mask]._seq._currRelative; - Anim &backAnim = _backAnimList[mob->_mask].backAnims[currentAnim]; - if (backAnim._animData != nullptr) { - if (!backAnim._state) { - Common::Rect backAnimRect(backAnim._currX, backAnim._currY, backAnim._currX + backAnim._currW, backAnim._currY + backAnim._currH); - if (backAnimRect.contains(pointPos)) { - int phase = backAnim._showFrame; - int phaseFrameIndex = backAnim._animData->getPhaseFrameIndex(phase); - Graphics::Surface *backAnimSurface = backAnim._animData->getFrame(phaseFrameIndex); - byte pixel = *(byte *)backAnimSurface->getBasePtr(posX - backAnim._currX, posY - backAnim._currY); - if (pixel != 255) { - if (type == 5) { - if (mob->_rect.contains(pointPos)) { - break; - } - } else { - break; - } - } - } - } - } - } - continue; - break; - default: - //not_part_ba - continue; - break; - } - - if (usePriorityList) { - return _mobPriorityList[mobNumber]; - } else { - return mobNumber; - } - } - return -1; -} - -int PrinceEngine::checkMob(Graphics::Surface *screen, Common::Array<Mob> &mobList, bool usePriorityList) { - if (_mouseFlag == 0 || _mouseFlag == 3) { - return -1; - } - Common::Point mousePos = _system->getEventManager()->getMousePos(); - int mobNumber = getMob(mobList, usePriorityList, mousePos.x + _picWindowX, mousePos.y); - - if (mobNumber != -1) { - Common::String mobName = mobList[mobNumber]._name; - - if (getLanguage() == Common::DE_DEU) { - for (uint i = 0; i < mobName.size(); i++) { - switch (mobName[i]) { - case '\xc4': - mobName.setChar('\x83', i); - break; - case '\xd6': - mobName.setChar('\x84', i); - break; - case '\xdc': - mobName.setChar('\x85', i); - break; - case '\xdf': - mobName.setChar('\x7f', i); - break; - case '\xe4': - mobName.setChar('\x80', i); - break; - case '\xf6': - mobName.setChar('\x81', i); - break; - case '\xfc': - mobName.setChar('\x82', i); - break; - } - } - } - - uint16 textW = getTextWidth(mobName.c_str()); - - uint16 x = mousePos.x - textW / 2; - if (x > screen->w) { - x = 0; - } - - if (x + textW > screen->w) { - x = screen->w - textW; - } - - uint16 y = mousePos.y - _font->getFontHeight(); - if (y > screen->h) { - y = _font->getFontHeight() - 2; - } - - _font->drawString(screen, mobName, x, y, screen->w, 216); - } - - return mobNumber; -} - void PrinceEngine::printAt(uint32 slot, uint8 color, char *s, uint16 x, uint16 y) { debugC(1, DebugChannel::kEngine, "PrinceEngine::printAt slot %d, color %d, x %02d, y %02d, str %s", slot, color, x, y, s); @@ -1383,677 +702,6 @@ void PrinceEngine::showTexts(Graphics::Surface *screen) { } } -bool PrinceEngine::spriteCheck(int sprWidth, int sprHeight, int destX, int destY) { - destX -= _picWindowX; - destY -= _picWindowY; - - // if x1 is on visible part of screen - if (destX < 0) { - if (destX + sprWidth < 1) { - //x2 is negative - out of window - return false; - } - } - // if x1 is outside of screen on right side - if (destX >= kNormalWidth) { - return false; - } - - if (destY < 0) { - if (destY + sprHeight < 1) { - //y2 is negative - out of window - return false; - } - } - if (destY >= kNormalHeight) { - return false; - } - - return true; -} - -// CheckNak -void PrinceEngine::checkMasks(int x1, int y1, int sprWidth, int sprHeight, int z) { - int x2 = x1 + sprWidth - 1; - int y2 = y1 + sprHeight - 1; - if (x1 < 0) { - x1 = 0; - } - for (uint i = 0; i < _maskList.size(); i++) { - if (!_maskList[i]._state && !_maskList[i]._flags) { - if (_maskList[i]._z > z) { - if (_maskList[i]._x1 <= x2 && _maskList[i]._x2 >= x1) { - if (_maskList[i]._y1 <= y2 && _maskList[i]._y2 >= y1) { - _maskList[i]._state = 1; - } - } - } - } - } -} - -// ClsNak -void PrinceEngine::clsMasks() { - for (uint i = 0; i < _maskList.size(); i++) { - if (_maskList[i]._state) { - _maskList[i]._state = 0; - } - } -} - -// InsertNakladki -void PrinceEngine::insertMasks(Graphics::Surface *originalRoomSurface) { - for (uint i = 0; i < _maskList.size(); i++) { - if (_maskList[i]._state) { - if (_maskList[i]._data != nullptr) { - showMask(i, originalRoomSurface); - } else { - error("insertMasks() - Wrong mask data- nr %d", i); - } - } - } -} - -// ShowNak -void PrinceEngine::showMask(int maskNr, Graphics::Surface *originalRoomSurface) { - if (!_maskList[maskNr]._flags) { - if (spriteCheck(_maskList[maskNr]._width, _maskList[maskNr]._height, _maskList[maskNr]._x1, _maskList[maskNr]._y1)) { - int destX = _maskList[maskNr]._x1 - _picWindowX; - int destY = _maskList[maskNr]._y1 - _picWindowY; - DrawNode newDrawNode; - newDrawNode.posX = destX; - newDrawNode.posY = destY; - newDrawNode.posZ = _maskList[maskNr]._z; - newDrawNode.width = _maskList[maskNr]._width; - newDrawNode.height = _maskList[maskNr]._height; - newDrawNode.s = nullptr; - newDrawNode.originalRoomSurface = originalRoomSurface; - newDrawNode.data = _maskList[maskNr].getMask(); - newDrawNode.drawFunction = &_graph->drawMaskDrawNode; - _drawNodeList.push_back(newDrawNode); - } - } -} - -void PrinceEngine::showSprite(Graphics::Surface *spriteSurface, int destX, int destY, int destZ) { - if (spriteCheck(spriteSurface->w, spriteSurface->h, destX, destY)) { - destX -= _picWindowX; - destY -= _picWindowY; - DrawNode newDrawNode; - newDrawNode.posX = destX; - newDrawNode.posY = destY; - newDrawNode.posZ = destZ; - newDrawNode.width = 0; - newDrawNode.height = 0; - newDrawNode.s = spriteSurface; - newDrawNode.originalRoomSurface = nullptr; - newDrawNode.data = _transTable; - newDrawNode.drawFunction = &_graph->drawTransparentWithTransDrawNode; - _drawNodeList.push_back(newDrawNode); - } -} - -void PrinceEngine::showSpriteShadow(Graphics::Surface *shadowSurface, int destX, int destY, int destZ) { - if (spriteCheck(shadowSurface->w, shadowSurface->h, destX, destY)) { - destX -= _picWindowX; - destY -= _picWindowY; - DrawNode newDrawNode; - newDrawNode.posX = destX; - newDrawNode.posY = destY; - newDrawNode.posZ = destZ; - newDrawNode.width = 0; - newDrawNode.height = 0; - newDrawNode.s = shadowSurface; - newDrawNode.originalRoomSurface = nullptr; - newDrawNode.data = _graph->_shadowTable70; - newDrawNode.drawFunction = &_graph->drawAsShadowDrawNode; - _drawNodeList.push_back(newDrawNode); - } -} - -void PrinceEngine::showAnim(Anim &anim) { - //ShowFrameCode - //ShowAnimFrame - int phase = anim._showFrame; - int phaseFrameIndex = anim._animData->getPhaseFrameIndex(phase); - int x = anim._x + anim._animData->getPhaseOffsetX(phase); - int y = anim._y + anim._animData->getPhaseOffsetY(phase); - int animFlag = anim._flags; - int checkMaskFlag = (animFlag & 1); - int maxFrontFlag = (animFlag & 2); - int specialZFlag = anim._nextAnim; - int z = anim._nextAnim; - Graphics::Surface *animSurface = anim._animData->getFrame(phaseFrameIndex); - int frameWidth = animSurface->w; - int frameHeight = animSurface->h; - int shadowZ = 0; - - if (checkMaskFlag) { - if (!anim._nextAnim) { - z = y + frameHeight - 1; - } - checkMasks(x, y, frameWidth, frameHeight, z); - } - - if (specialZFlag) { - z = specialZFlag; - } else if (maxFrontFlag) { - z = kMaxPicHeight + 1; - } else { - z = y + frameHeight - 1; - } - shadowZ = z; - - anim._currX = x; - anim._currY = y; - anim._currW = frameWidth; - anim._currH = frameHeight; - showSprite(animSurface, x, y, z); - - // make_special_shadow - if ((anim._flags & 0x80)) { - DrawNode newDrawNode; - newDrawNode.posX = x; - newDrawNode.posY = y + animSurface->h - anim._shadowBack; - newDrawNode.posZ = Hero::kHeroShadowZ; - newDrawNode.width = 0; - newDrawNode.height = 0; - newDrawNode.scaleValue = _scaleValue; - newDrawNode.originalRoomSurface = nullptr; - newDrawNode.data = this; - newDrawNode.drawFunction = &Hero::showHeroShadow; - newDrawNode.s = animSurface; - _drawNodeList.push_back(newDrawNode); - } - - //ShowFrameCodeShadow - //ShowAnimFrameShadow - if (anim._shadowData != nullptr) { - int shadowPhaseFrameIndex = anim._shadowData->getPhaseFrameIndex(phase); - int shadowX = anim._shadowData->getBaseX() + anim._shadowData->getPhaseOffsetX(phase); - int shadowY = anim._shadowData->getBaseY() + anim._shadowData->getPhaseOffsetY(phase); - Graphics::Surface *shadowSurface = anim._shadowData->getFrame(shadowPhaseFrameIndex); - int shadowFrameWidth = shadowSurface->w; - int shadowFrameHeight = shadowSurface->h; - - if (checkMaskFlag) { - checkMasks(shadowX, shadowY, shadowFrameWidth, shadowFrameHeight, shadowY + shadowFrameWidth - 1); - } - - if (!shadowZ) { - if (maxFrontFlag) { - shadowZ = kMaxPicHeight + 1; - } else { - shadowZ = shadowY + shadowFrameWidth - 1; - } - } - showSpriteShadow(shadowSurface, shadowX, shadowY, shadowZ); - } -} - -void PrinceEngine::showNormAnims() { - for (int i = 0; i < kMaxNormAnims; i++) { - Anim &anim = _normAnimList[i]; - if (anim._animData != nullptr) { - int phaseCount = anim._animData->getPhaseCount(); - if (!anim._state) { - if (anim._frame == anim._lastFrame - 1) { - if (anim._loopType) { - if (anim._loopType == 1) { - anim._frame = anim._loopFrame; - } else { - continue; - } - } - } else { - anim._frame++; - } - anim._showFrame = anim._frame; - if (anim._showFrame >= phaseCount) { - anim._showFrame = phaseCount - 1; - } - showAnim(anim); - } - } - } -} - -void PrinceEngine::setBackAnim(Anim &backAnim) { - int start = backAnim._basaData._start; - if (start != -1) { - backAnim._frame = start; - backAnim._showFrame = start; - backAnim._loopFrame = start; - } - int end = backAnim._basaData._end; - if (end != -1) { - backAnim._lastFrame = end; - } - backAnim._state = 0; -} - -void PrinceEngine::showBackAnims() { - for (int i = 0; i < kMaxBackAnims; i++) { - BAS &seq = _backAnimList[i]._seq; - int activeSubAnim = seq._currRelative; - if (!_backAnimList[i].backAnims.empty()) { - if (_backAnimList[i].backAnims[activeSubAnim]._animData != nullptr) { - if (!_backAnimList[i].backAnims[activeSubAnim]._state) { - seq._counter++; - if (seq._type == 2) { - if (!seq._currRelative) { - if (seq._counter >= seq._data) { - if (seq._anims > 2) { - seq._currRelative = _randomSource.getRandomNumber(seq._anims - 2) + 1; - activeSubAnim = seq._currRelative; - seq._current = _backAnimList[i].backAnims[activeSubAnim]._basaData._num; - } - setBackAnim(_backAnimList[i].backAnims[activeSubAnim]); - seq._counter = 0; - } - } - } - - if (seq._type == 3) { - if (!seq._currRelative) { - if (seq._counter < seq._data2) { - continue; - } else { - setBackAnim(_backAnimList[i].backAnims[activeSubAnim]); - } - } - } - - if (_backAnimList[i].backAnims[activeSubAnim]._frame == _backAnimList[i].backAnims[activeSubAnim]._lastFrame - 1) { - _backAnimList[i].backAnims[activeSubAnim]._frame = _backAnimList[i].backAnims[activeSubAnim]._loopFrame; - switch (seq._type) { - case 1: - if (seq._anims > 1) { - int rnd; - do { - rnd = _randomSource.getRandomNumber(seq._anims - 1); - } while (rnd == seq._currRelative); - seq._currRelative = rnd; - seq._current = _backAnimList[i].backAnims[rnd]._basaData._num; - activeSubAnim = rnd; - setBackAnim(_backAnimList[i].backAnims[activeSubAnim]); - seq._counter = 0; - } - break; - case 2: - if (seq._currRelative) { - seq._currRelative = 0; - seq._current = _backAnimList[i].backAnims[0]._basaData._num; - activeSubAnim = 0; - setBackAnim(_backAnimList[i].backAnims[activeSubAnim]); - seq._counter = 0; - } - break; - case 3: - seq._currRelative = 0; - seq._current = _backAnimList[i].backAnims[0]._basaData._num; - seq._counter = 0; - seq._data2 = _randomSource.getRandomNumber(seq._data - 1); - continue; // for bug in original game - break; - } - } else { - _backAnimList[i].backAnims[activeSubAnim]._frame++; - } - _backAnimList[i].backAnims[activeSubAnim]._showFrame = _backAnimList[i].backAnims[activeSubAnim]._frame; - showAnim(_backAnimList[i].backAnims[activeSubAnim]); - } - } - } - } -} - -void PrinceEngine::removeSingleBackAnim(int slot) { - if (!_backAnimList[slot].backAnims.empty()) { - for (uint j = 0; j < _backAnimList[slot].backAnims.size(); j++) { - if (_backAnimList[slot].backAnims[j]._animData != nullptr) { - delete _backAnimList[slot].backAnims[j]._animData; - _backAnimList[slot].backAnims[j]._animData = nullptr; - } - if (_backAnimList[slot].backAnims[j]._shadowData != nullptr) { - delete _backAnimList[slot].backAnims[j]._shadowData; - _backAnimList[slot].backAnims[j]._shadowData = nullptr; - } - } - _backAnimList[slot].backAnims.clear(); - _backAnimList[slot]._seq._currRelative = 0; - } -} - -void PrinceEngine::clearBackAnimList() { - for (int i = 0; i < kMaxBackAnims; i++) { - removeSingleBackAnim(i); - } -} - -void PrinceEngine::grabMap() { - _graph->_frontScreen->copyFrom(*_roomBmp->getSurface()); - showObjects(); - runDrawNodes(); - _graph->_mapScreen->copyFrom(*_graph->_frontScreen); -} - -void PrinceEngine::initZoomIn(int slot) { - freeZoomObject(slot); - Object *object = _objList[slot]; - if (object != nullptr) { - Graphics::Surface *zoomSource = object->getSurface(); - if (zoomSource != nullptr) { - object->_flags |= 0x8000; - object->_zoomSurface = new Graphics::Surface(); - object->_zoomSurface->create(zoomSource->w, zoomSource->h, Graphics::PixelFormat::createFormatCLUT8()); - object->_zoomSurface->fillRect(Common::Rect(zoomSource->w, zoomSource->h), 0xFF); - object->_zoomTime = 20; - } - } -} - -void PrinceEngine::initZoomOut(int slot) { - freeZoomObject(slot); - Object *object = _objList[slot]; - if (object != nullptr) { - Graphics::Surface *zoomSource = object->getSurface(); - if (zoomSource != nullptr) { - object->_flags |= 0x4000; - object->_zoomSurface = new Graphics::Surface(); - object->_zoomSurface->copyFrom(*zoomSource); - object->_zoomTime = 10; - } - } -} - -void PrinceEngine::doZoomIn(int slot) { - Object *object = _objList[slot]; - if (object != nullptr) { - Graphics::Surface *orgSurface = object->getSurface(); - if (orgSurface != nullptr) { - byte *src1 = (byte *)orgSurface->getBasePtr(0, 0); - byte *dst1 = (byte *)object->_zoomSurface->getBasePtr(0, 0); - int x = 0; - int surfaceHeight = orgSurface->h; - for (int y = 0; y < surfaceHeight; y++) { - byte *src2 = src1; - byte *dst2 = dst1; - int w = orgSurface->w - x; - src2 += x; - dst2 += x; - while (w > 0) { - int randVal = _randomSource.getRandomNumber(zoomInStep - 1); - if (randVal < w) { - *(dst2 + randVal) = *(src2 + randVal); - src2 += zoomInStep; - dst2 += zoomInStep; - } else if (y + 1 != surfaceHeight) { - *(dst1 + orgSurface->pitch + randVal - w) = *(src1 + orgSurface->pitch + randVal - w); - } - w -= zoomInStep; - } - x = -1 * w; - src1 += orgSurface->pitch; - dst1 += orgSurface->pitch; - } - } - } -} - -void PrinceEngine::doZoomOut(int slot) { - Object *object = _objList[slot]; - if (object != nullptr) { - Graphics::Surface *orgSurface = object->getSurface(); - if (orgSurface != nullptr) { - byte *dst1 = (byte *)object->_zoomSurface->getBasePtr(0, 0); - int x = 0; - int surfaceHeight = orgSurface->h; - for (int y = 0; y < surfaceHeight; y++) { - byte *dst2 = dst1; - int w = orgSurface->w - x; - dst2 += x; - while (w > 0) { - int randVal = _randomSource.getRandomNumber(zoomInStep - 1); - if (randVal < w) { - *(dst2 + randVal) = 255; - dst2 += zoomInStep; - } else if (y + 1 != surfaceHeight) { - *(dst1 + orgSurface->pitch + randVal - w) = 255; - } - w -= zoomInStep; - } - x = -1 * w; - dst1 += orgSurface->pitch; - } - } - } -} - -void PrinceEngine::freeZoomObject(int slot) { - Object *object = _objList[slot]; - if (object != nullptr) { - if (object->_zoomSurface != nullptr) { - object->_zoomSurface->free(); - delete object->_zoomSurface; - object->_zoomSurface = nullptr; - } - } -} - -void PrinceEngine::showObjects() { - for (int i = 0; i < kMaxObjects; i++) { - int nr = _objSlot[i]; - if (nr != 0xFF) { - Graphics::Surface *objSurface = nullptr; - if ((_objList[nr]->_flags & 0x8000)) { - _objList[nr]->_zoomTime--; - if (!_objList[nr]->_zoomTime) { - freeZoomObject(nr); - _objList[nr]->_flags &= 0x7FFF; - objSurface = _objList[nr]->getSurface(); - } else { - doZoomIn(nr); - objSurface = _objList[nr]->_zoomSurface; - } - } else if ((_objList[nr]->_flags & 0x4000)) { - _objList[nr]->_zoomTime--; - if (!_objList[nr]->_zoomTime) { - freeZoomObject(nr); - _objList[nr]->_flags &= 0xBFFF; - objSurface = _objList[nr]->getSurface(); - } else { - doZoomOut(nr); - objSurface = _objList[nr]->_zoomSurface; - } - } else { - objSurface = _objList[nr]->getSurface(); - } - - if (objSurface != nullptr) { - if (spriteCheck(objSurface->w, objSurface->h, _objList[nr]->_x, _objList[nr]->_y)) { - int destX = _objList[nr]->_x - _picWindowX; - int destY = _objList[nr]->_y - _picWindowY; - DrawNode newDrawNode; - newDrawNode.posX = destX; - newDrawNode.posY = destY; - newDrawNode.posZ = _objList[nr]->_z; - newDrawNode.width = 0; - newDrawNode.height = 0; - newDrawNode.s = objSurface; - newDrawNode.originalRoomSurface = nullptr; - if ((_objList[nr]->_flags & 0x2000)) { - newDrawNode.data = nullptr; - newDrawNode.drawFunction = &_graph->drawBackSpriteDrawNode; - } else { - newDrawNode.data = _transTable; - if (_flags->getFlagValue(Flags::NOANTIALIAS)) { - newDrawNode.drawFunction = &_graph->drawTransparentDrawNode; - } else { - newDrawNode.drawFunction = &_graph->drawTransparentWithTransDrawNode; - } - } - _drawNodeList.push_back(newDrawNode); - } - - if ((_objList[nr]->_flags & 1)) { - checkMasks(_objList[nr]->_x, _objList[nr]->_y, objSurface->w, objSurface->h, _objList[nr]->_z); - } - } - } - } -} - -void PrinceEngine::showParallax() { - if (!_pscrList.empty()) { - for (uint i = 0; i < _pscrList.size(); i++) { - Graphics::Surface *pscrSurface = _pscrList[i]->getSurface(); - if (pscrSurface != nullptr) { - int x = _pscrList[i]->_x - (_pscrList[i]->_step * _picWindowX / 4); - int y = _pscrList[i]->_y; - int z = PScr::kPScrZ; - if (spriteCheck(pscrSurface->w, pscrSurface->h, x, y)) { - showSprite(pscrSurface, x, y, z); - } - } - } - } -} - -bool PrinceEngine::compareDrawNodes(DrawNode d1, DrawNode d2) { - if (d1.posZ < d2.posZ) { - return true; - } - return false; -} - -void PrinceEngine::runDrawNodes() { - Common::sort(_drawNodeList.begin(), _drawNodeList.end(), compareDrawNodes); - - for (uint i = 0; i < _drawNodeList.size(); i++) { - (*_drawNodeList[i].drawFunction)(_graph->_frontScreen, &_drawNodeList[i]); - } - _graph->change(); -} - -void PrinceEngine::drawScreen() { - if (!_showInventoryFlag || _inventoryBackgroundRemember) { - clsMasks(); - - _mainHero->showHero(); - _mainHero->scrollHero(); - _mainHero->drawHero(); - - _secondHero->showHero(); - _secondHero->_drawX -= _picWindowX; - _secondHero->drawHero(); - - const Graphics::Surface *roomSurface; - if (_locationNr != 50) { - roomSurface = _roomBmp->getSurface(); - } else { - roomSurface = _graph->_mapScreen; - } - Graphics::Surface visiblePart; - if (roomSurface) { - visiblePart = roomSurface->getSubArea(Common::Rect(_picWindowX, 0, roomSurface->w, roomSurface->h)); - _graph->draw(_graph->_frontScreen, &visiblePart); - } - - showBackAnims(); - - showNormAnims(); - - playNextFLCFrame(); - - showObjects(); - - if (roomSurface) { - insertMasks(&visiblePart); - } - - showParallax(); - - runDrawNodes(); - - _drawNodeList.clear(); - - if (!_inventoryBackgroundRemember && !_dialogFlag) { - if (!_optionsFlag) { - _selectedMob = checkMob(_graph->_frontScreen, _mobList, true); - } - showTexts(_graph->_frontScreen); - checkOptions(); - } else { - _inventoryBackgroundRemember = false; - } - - showPower(); - - getDebugger()->onFrame(); - - } else { - displayInventory(); - } -} - -void PrinceEngine::blackPalette() { - byte *paletteBackup = (byte *)malloc(256 * 3); - byte *blackPalette1 = (byte *)malloc(256 * 3); - - int fadeStep = kFadeStep - 1; - for (int i = 0; i < kFadeStep; i++) { - _system->getPaletteManager()->grabPalette(paletteBackup, 0, 256); - for (int j = 0; j < 256; j++) { - blackPalette1[3 * j] = paletteBackup[3 * j] * fadeStep / 4; - blackPalette1[3 * j + 1] = paletteBackup[3 * j + 1] * fadeStep / 4; - blackPalette1[3 * j + 2] = paletteBackup[3 * j + 2] * fadeStep / 4; - } - fadeStep--; - _graph->setPalette(blackPalette1); - _system->updateScreen(); - Common::Event event; - Common::EventManager *eventMan = _system->getEventManager(); - eventMan->pollEvent(event); - if (shouldQuit()) { - free(paletteBackup); - free(blackPalette1); - return; - } - pausePrinceEngine(); - } - free(paletteBackup); - free(blackPalette1); -} - -void PrinceEngine::setPalette(const byte *palette) { - if (palette != nullptr) { - byte *blackPalette_ = (byte *)malloc(256 * 3); - int fadeStep = 0; - for (int i = 0; i <= kFadeStep; i++) { - for (int j = 0; j < 256; j++) { - blackPalette_[3 * j] = palette[3 * j] * fadeStep / 4; - blackPalette_[3 * j + 1] = palette[3 * j + 1] * fadeStep / 4; - blackPalette_[3 * j + 2] = palette[3 * j + 2] * fadeStep / 4; - } - fadeStep++; - _graph->setPalette(blackPalette_); - _system->updateScreen(); - Common::Event event; - Common::EventManager *eventMan = _system->getEventManager(); - eventMan->pollEvent(event); - if (shouldQuit()) { - _graph->setPalette(palette); - free(blackPalette_); - return; - } - pausePrinceEngine(); - } - _graph->setPalette(palette); - free(blackPalette_); - } -} - void PrinceEngine::pausePrinceEngine(int fps) { int delay = 1000 / fps - int32(_system->getMillis() - _currentTime); delay = delay < 0 ? 0 : delay; @@ -2061,369 +709,6 @@ void PrinceEngine::pausePrinceEngine(int fps) { _currentTime = _system->getMillis(); } -void PrinceEngine::addInv(int heroId, int item, bool addItemQuiet) { - Hero *hero = nullptr; - if (!heroId) { - hero = _mainHero; - } else if (heroId == 1) { - hero = _secondHero; - } - if (hero != nullptr) { - if (hero->_inventory.size() < kMaxItems) { - if (item != 0x7FFF) { - hero->_inventory.push_back(item); - } - if (!addItemQuiet) { - addInvObj(); - } - _interpreter->setResult(0); - } else { - _interpreter->setResult(1); - } - } -} - -void PrinceEngine::remInv(int heroId, int item) { - Hero *hero = nullptr; - if (!heroId) { - hero = _mainHero; - } else if (heroId == 1) { - hero = _secondHero; - } - if (hero != nullptr) { - for (uint i = 0; i < hero->_inventory.size(); i++) { - if (hero->_inventory[i] == item) { - hero->_inventory.remove_at(i); - _interpreter->setResult(0); - return; - } - } - } - _interpreter->setResult(1); -} - -void PrinceEngine::clearInv(int heroId) { - switch (heroId) { - case 0: - _mainHero->_inventory.clear(); - break; - case 1: - _secondHero->_inventory.clear(); - break; - default: - error("clearInv() - wrong hero slot"); - break; - } -} - -void PrinceEngine::swapInv(int heroId) { - Common::Array<int> tempInv; - Hero *hero = nullptr; - if (!heroId) { - hero = _mainHero; - } else if (heroId == 1) { - hero = _secondHero; - } - if (hero != nullptr) { - for (uint i = 0; i < hero->_inventory.size(); i++) { - tempInv.push_back(hero->_inventory[i]); - } - hero->_inventory.clear(); - for (uint i = 0; i < hero->_inventory2.size(); i++) { - hero->_inventory.push_back(hero->_inventory2[i]); - } - hero->_inventory2.clear(); - for (uint i = 0; i < tempInv.size(); i++) { - hero->_inventory2.push_back(tempInv[i]); - } - tempInv.clear(); - } -} - -void PrinceEngine::addInvObj() { - changeCursor(0); - prepareInventoryToView(); - - _inventoryBackgroundRemember = true; - drawScreen(); - - Graphics::Surface *suitcase = _suitcaseBmp->getSurface(); - - if (!_flags->getFlagValue(Flags::CURSEBLINK)) { - - loadSample(27, "PRZEDMIO.WAV"); - playSample(27, 0); - - _mst_shadow2 = 1; - - while (_mst_shadow2 < 512) { - rememberScreenInv(); - _graph->drawTransparentSurface(_graph->_screenForInventory, 0, 0, suitcase); - drawInvItems(); - _graph->update(_graph->_screenForInventory); - _mst_shadow2 += 50; - Common::Event event; - Common::EventManager *eventMan = _system->getEventManager(); - eventMan->pollEvent(event); - if (shouldQuit()) { - return; - } - pausePrinceEngine(); - } - while (_mst_shadow2 > 256) { - rememberScreenInv(); - _graph->drawTransparentSurface(_graph->_screenForInventory, 0, 0, suitcase); - drawInvItems(); - _graph->update(_graph->_screenForInventory); - _mst_shadow2 -= 42; - Common::Event event; - Common::EventManager *eventMan = _system->getEventManager(); - eventMan->pollEvent(event); - if (shouldQuit()) { - return; - } - pausePrinceEngine(); - } - } else { - //CURSEBLINK: - for (int i = 0; i < 3; i++) { - _mst_shadow2 = 256; - while (_mst_shadow2 < 512) { - rememberScreenInv(); - _graph->drawTransparentSurface(_graph->_screenForInventory, 0, 0, suitcase); - drawInvItems(); - _graph->update(_graph->_screenForInventory); - _mst_shadow2 += 50; - Common::Event event; - Common::EventManager *eventMan = _system->getEventManager(); - eventMan->pollEvent(event); - if (shouldQuit()) { - return; - } - pausePrinceEngine(); - } - while (_mst_shadow2 > 256) { - rememberScreenInv(); - _graph->drawTransparentSurface(_graph->_screenForInventory, 0, 0, suitcase); - drawInvItems(); - _graph->update(_graph->_screenForInventory); - _mst_shadow2 -= 50; - Common::Event event; - Common::EventManager *eventMan = _system->getEventManager(); - eventMan->pollEvent(event); - if (shouldQuit()) { - return; - } - pausePrinceEngine(); - } - } - } - _mst_shadow2 = 0; - for (int i = 0; i < 20; i++) { - rememberScreenInv(); - _graph->drawTransparentSurface(_graph->_screenForInventory, 0, 0, suitcase); - drawInvItems(); - _graph->update(_graph->_screenForInventory); - Common::Event event; - Common::EventManager *eventMan = _system->getEventManager(); - eventMan->pollEvent(event); - if (shouldQuit()) { - return; - } - pausePrinceEngine(); - } -} - -void PrinceEngine::rememberScreenInv() { - _graph->_screenForInventory->copyFrom(*_graph->_frontScreen); -} - -void PrinceEngine::inventoryFlagChange(bool inventoryState) { - if (inventoryState) { - _showInventoryFlag = true; - _inventoryBackgroundRemember = true; - } else { - _showInventoryFlag = false; - } -} - -void PrinceEngine::prepareInventoryToView() { - _invMobList.clear(); - int invItem = _mainHero->_inventory.size(); - _invLine = invItem / 3; - if (invItem % 3) { - _invLine++; - } - if (_invLine < 4) { - _invLine = 4; - } - _maxInvW = (374 - 2 * _invLine) / _invLine; - _invLineW = _maxInvW - 2; - - int currInvX = _invLineX; - int currInvY = _invLineY; - - Common::MemoryReadStream stream(_invTxt, _invTxtSize); - byte c; - - uint item = 0; - for (int i = 0; i < _invLines; i++) { - for (int j = 0; j < _invLine; j++) { - Mob tempMobItem; - if (item < _mainHero->_inventory.size()) { - int itemNr = _mainHero->_inventory[item]; - tempMobItem._visible = 0; - tempMobItem._mask = itemNr; - tempMobItem._rect = Common::Rect(currInvX + _picWindowX, currInvY, currInvX + _picWindowX + _invLineW - 1, currInvY + _invLineH - 1); - tempMobItem._type = 0; // to work with checkMob() - - tempMobItem._name = ""; - tempMobItem._examText = ""; - int txtOffset = READ_LE_UINT32(&_invTxt[itemNr * 8]); - int examTxtOffset = READ_LE_UINT32(&_invTxt[itemNr * 8 + 4]); - - stream.seek(txtOffset); - while ((c = stream.readByte())) { - tempMobItem._name += c; - } - - stream.seek(examTxtOffset); - while ((c = stream.readByte())) { - tempMobItem._examText += c; - } - _invMobList.push_back(tempMobItem); - } - currInvX += _invLineW + _invLineSkipX; - item++; - } - currInvX = _invLineX; - currInvY += _invLineSkipY + _invLineH; - } -} - -void PrinceEngine::drawInvItems() { - int currInvX = _invLineX; - int currInvY = _invLineY; - uint item = 0; - for (int i = 0; i < _invLines; i++) { - for (int j = 0; j < _invLine; j++) { - if (item < _mainHero->_inventory.size()) { - int itemNr = _mainHero->_inventory[item]; - _mst_shadow = 0; - if (_mst_shadow2) { - if (!_flags->getFlagValue(Flags::CURSEBLINK)) { - if (item + 1 == _mainHero->_inventory.size()) { // last item in inventory - _mst_shadow = 1; - } - } else if (itemNr == 1 || itemNr == 3 || itemNr == 4 || itemNr == 7) { - _mst_shadow = 1; - } - } - - int drawX = currInvX; - int drawY = currInvY; - Graphics::Surface *itemSurface = nullptr; - if (itemNr != 68) { - itemSurface = _allInvList[itemNr].getSurface(); - if (itemSurface->h < _maxInvH) { - drawY += (_maxInvH - itemSurface->h) / 2; - } - } else { - // candle item: - if (_candleCounter == 8) { - _candleCounter = 0; - } - itemNr = _candleCounter; - _candleCounter++; - itemNr &= 7; - itemNr += 71; - itemSurface = _allInvList[itemNr].getSurface(); - drawY += _allInvList[itemNr]._y + (_maxInvH - 76) / 2 - 200; - } - if (itemSurface->w < _maxInvW) { - drawX += (_maxInvW - itemSurface->w) / 2; - } - if (!_mst_shadow) { - _graph->drawTransparentSurface(_graph->_screenForInventory, drawX, drawY, itemSurface); - } else { - _mst_shadow = _mst_shadow2; - _graph->drawTransparentWithBlendSurface(_graph->_screenForInventory, drawX, drawY, itemSurface); - } - } - currInvX += _invLineW + _invLineSkipX; - item++; - } - currInvX = _invLineX; - currInvY += _invLineSkipY + _invLineH; - } -} - -void PrinceEngine::walkTo() { - if (_mainHero->_visible) { - _mainHero->freeHeroAnim(); - _mainHero->freeOldMove(); - _interpreter->storeNewPC(_script->_scriptInfo.usdCode); - int destX, destY; - if (_optionsMob != -1) { - destX = _mobList[_optionsMob]._examPosition.x; - destY = _mobList[_optionsMob]._examPosition.y; - _mainHero->_destDirection = _mobList[_optionsMob]._examDirection; - } else { - Common::Point mousePos = _system->getEventManager()->getMousePos(); - destX = mousePos.x + _picWindowX; - destY = mousePos.y + _picWindowY; - _mainHero->_destDirection = 0; - } - _mainHero->_coords = makePath(kMainHero, _mainHero->_middleX, _mainHero->_middleY, destX, destY); - if (_mainHero->_coords != nullptr) { - _mainHero->_currCoords = _mainHero->_coords; - _mainHero->_dirTab = _directionTable; - _mainHero->_currDirTab = _directionTable; - _directionTable = nullptr; - _mainHero->_state = Hero::kHeroStateMove; - moveShandria(); - } - } -} - -void PrinceEngine::moveRunHero(int heroId, int x, int y, int dir, bool runHeroFlag) { - Hero *hero = nullptr; - if (!heroId) { - hero = _mainHero; - } else if (heroId == 1) { - hero = _secondHero; - } - - if (hero != nullptr) { - if (dir) { - hero->_destDirection = dir; - } - if (x || y) { - hero->freeOldMove(); - hero->_coords = makePath(heroId, hero->_middleX, hero->_middleY, x, y); - if (hero->_coords != nullptr) { - hero->_currCoords = hero->_coords; - hero->_dirTab = _directionTable; - hero->_currDirTab = _directionTable; - _directionTable = nullptr; - if (runHeroFlag) { - hero->_state = Hero::kHeroStateRun; - } else { - hero->_state = Hero::kHeroStateMove; - } - if (heroId == kMainHero && _mouseFlag) { - moveShandria(); - } - } - } else { - hero->freeOldMove(); - hero->_state = Hero::kHeroStateTurn; - } - hero->freeHeroAnim(); - hero->_visible = 1; - } -} - void PrinceEngine::leftMouseButton() { _flags->setFlagValue(Flags::ESCAPED2, 1); // skip intro animation _flags->setFlagValue(Flags::LMOUSE, 1); @@ -2524,351 +809,6 @@ void PrinceEngine::rightMouseButton() { } } -void PrinceEngine::inventoryLeftMouseButton() { - if (!_mouseFlag) { - _textSlots[0]._time = 0; - _textSlots[0]._str = nullptr; - stopSample(28); - } - - if (_optionsFlag == 1) { - if (_selectedMob != -1) { - if (_optionEnabled < _invOptionsNumber) { - _optionsFlag = 0; - } else { - return; - } - } else { - error("PrinceEngine::inventoryLeftMouseButton() - optionsFlag = 1, selectedMob = 0"); - if (_currentPointerNumber == 2) { - changeCursor(1); - _currentPointerNumber = 1; - _selectedMob = -1; - _optionsMob = -1; - return; - } else { - return; - } - } - } else { - if (_selectedMob != -1) { - if (_currentPointerNumber != 2) { - if (_invMobList[_selectedMob]._mask != 29) { - _optionEnabled = 0; - } else { - // map item - _optionEnabled = 1; - } - } else { - //use_item_on_item - int invObjUU = _script->scanMobEventsWithItem(_invMobList[_selectedMob]._mask, _script->_scriptInfo.invObjUU, _selectedItem); - if (invObjUU == -1) { - int textNr = 80011; // "I can't do it." - if (_selectedItem == 31 || _invMobList[_selectedMob]._mask == 31) { - textNr = 80020; // "Nothing is happening." - } - _interpreter->setCurrentString(textNr); - printAt(0, 216, (char *)_variaTxt->getString(textNr - 80000), kNormalWidth / 2, 100); - setVoice(0, 28, 1); - playSample(28, 0); - _selectedMob = -1; - _optionsMob = -1; - return; - } else { - _interpreter->storeNewPC(invObjUU); - _flags->setFlagValue(Flags::CURRMOB, _invMobList[_selectedMob]._mask); - _showInventoryFlag = false; - } - } - } else { - return; - } - } - //do_option - if (_optionEnabled == 0) { - int invObjExamEvent = _script->scanMobEvents(_invMobList[_selectedMob]._mask, _script->_scriptInfo.invObjExam); - if (invObjExamEvent == -1) { - // do_standard - printAt(0, 216, (char *)_invMobList[_selectedMob]._examText.c_str(), kNormalWidth / 2, _invExamY); - _interpreter->setCurrentString(_invMobList[_selectedMob]._mask + 70000); - setVoice(0, 28, 1); - playSample(28, 0); - // disableuseuse - changeCursor(0); - _currentPointerNumber = 1; - } else { - _interpreter->storeNewPC(invObjExamEvent); - _flags->setFlagValue(Flags::CURRMOB, _invMobList[_selectedMob]._mask); - _showInventoryFlag = false; - } - } else if (_optionEnabled == 1) { - // not_examine - int invObjUse = _script->scanMobEvents(_invMobList[_selectedMob]._mask, _script->_scriptInfo.invObjUse); - if (invObjUse == -1) { - // do_standard_use - _selectedMode = 0; - _selectedItem = _invMobList[_selectedMob]._mask; - makeInvCursor(_invMobList[_selectedMob]._mask); - _currentPointerNumber = 2; - changeCursor(2); - } else { - _interpreter->storeNewPC(invObjUse); - _flags->setFlagValue(Flags::CURRMOB, _invMobList[_selectedMob]._mask); - _showInventoryFlag = false; - } - } else if (_optionEnabled == 4) { - // do_standard_give - _selectedMode = 1; - _selectedItem = _invMobList[_selectedMob]._mask; - makeInvCursor(_invMobList[_selectedMob]._mask); - _currentPointerNumber = 2; - changeCursor(2); - } else { - // use_item_on_item - int invObjUU = _script->scanMobEventsWithItem(_invMobList[_selectedMob]._mask, _script->_scriptInfo.invObjUU, _selectedItem); - if (invObjUU == -1) { - int textNr = 80011; // "I can't do it." - if (_selectedItem == 31 || _invMobList[_selectedMob]._mask == 31) { - textNr = 80020; // "Nothing is happening." - } - _interpreter->setCurrentString(textNr); - printAt(0, 216, (char *)_variaTxt->getString(textNr - 80000), kNormalWidth / 2, 100); - setVoice(0, 28, 1); - playSample(28, 0); - } else { - _interpreter->storeNewPC(invObjUU); - _flags->setFlagValue(Flags::CURRMOB, _invMobList[_selectedMob]._mask); - _showInventoryFlag = false; - } - } - _selectedMob = -1; - _optionsMob = -1; -} - -void PrinceEngine::inventoryRightMouseButton() { - if (_textSlots[0]._str == nullptr) { - enableOptions(false); - } -} - -void PrinceEngine::enableOptions(bool checkType) { - if (_optionsFlag != 1) { - changeCursor(1); - _currentPointerNumber = 1; - if (_selectedMob != -1) { - if (checkType) { - if (_mobList[_selectedMob]._type & 0x100) { - return; - } - } - Common::Point mousePos = _system->getEventManager()->getMousePos(); - int x1 = mousePos.x - _optionsWidth / 2; - int x2 = mousePos.x + _optionsWidth / 2; - if (x1 < 0) { - x1 = 0; - x2 = _optionsWidth; - } else if (x2 >= kNormalWidth) { - x1 = kNormalWidth - _optionsWidth; - x2 = kNormalWidth; - } - int y1 = mousePos.y - 10; - if (y1 < 0) { - y1 = 0; - } - if (y1 + _optionsHeight >= kNormalHeight) { - y1 = kNormalHeight - _optionsHeight; - } - _optionsMob = _selectedMob; - _optionsX = x1; - _optionsY = y1; - _optionsFlag = 1; - } - } -} - -void PrinceEngine::checkOptions() { - if (_optionsFlag) { - Common::Rect optionsRect(_optionsX, _optionsY, _optionsX + _optionsWidth, _optionsY + _optionsHeight); - Common::Point mousePos = _system->getEventManager()->getMousePos(); - if (!optionsRect.contains(mousePos)) { - _optionsFlag = 0; - _selectedMob = -1; - return; - } - _graph->drawAsShadowSurface(_graph->_frontScreen, _optionsX, _optionsY, _optionsPic, _graph->_shadowTable50); - - _optionEnabled = -1; - int optionsYCord = mousePos.y - (_optionsY + 16); - if (optionsYCord >= 0) { - int selectedOptionNr = optionsYCord / _optionsStep; - if (selectedOptionNr < _optionsNumber) { - _optionEnabled = selectedOptionNr; - } - } - int optionsColor; - int textY = _optionsY + 16; - for (int i = 0; i < _optionsNumber; i++) { - if (i != _optionEnabled) { - optionsColor = _optionsColor1; - } else { - optionsColor = _optionsColor2; - } - Common::String optText; - switch(getLanguage()) { - case Common::PL_POL: - optText = optionsTextPL[i]; - break; - case Common::DE_DEU: - optText = optionsTextDE[i]; - break; - case Common::EN_ANY: - optText = optionsTextEN[i]; - break; - default: - break; - }; - uint16 textW = getTextWidth(optText.c_str()); - uint16 textX = _optionsX + _optionsWidth / 2 - textW / 2; - _font->drawString(_graph->_frontScreen, optText, textX, textY, textW, optionsColor); - textY += _optionsStep; - } - } -} - -void PrinceEngine::checkInvOptions() { - if (_optionsFlag) { - Common::Rect optionsRect(_optionsX, _optionsY, _optionsX + _invOptionsWidth, _optionsY + _invOptionsHeight); - Common::Point mousePos = _system->getEventManager()->getMousePos(); - if (!optionsRect.contains(mousePos)) { - _optionsFlag = 0; - _selectedMob = -1; - return; - } - _graph->drawAsShadowSurface(_graph->_screenForInventory, _optionsX, _optionsY, _optionsPicInInventory, _graph->_shadowTable50); - - _optionEnabled = -1; - int optionsYCord = mousePos.y - (_optionsY + 16); - if (optionsYCord >= 0) { - int selectedOptionNr = optionsYCord / _invOptionsStep; - if (selectedOptionNr < _invOptionsNumber) { - _optionEnabled = selectedOptionNr; - } - } - int optionsColor; - int textY = _optionsY + 16; - for (int i = 0; i < _invOptionsNumber; i++) { - if (i != _optionEnabled) { - optionsColor = _optionsColor1; - } else { - optionsColor = _optionsColor2; - } - Common::String invText; - switch(getLanguage()) { - case Common::PL_POL: - invText = invOptionsTextPL[i]; - break; - case Common::DE_DEU: - invText = invOptionsTextDE[i]; - break; - case Common::EN_ANY: - invText = invOptionsTextEN[i]; - break; - default: - error("Unknown game language %d", getLanguage()); - break; - }; - uint16 textW = getTextWidth(invText.c_str()); - uint16 textX = _optionsX + _invOptionsWidth / 2 - textW / 2; - _font->drawString(_graph->_screenForInventory, invText, textX, textY, _graph->_screenForInventory->w, optionsColor); - textY += _invOptionsStep; - } - } -} - -void PrinceEngine::displayInventory() { - - _mainHero->freeOldMove(); - _secondHero->freeOldMove(); - - _interpreter->setFgOpcodePC(0); - - stopAllSamples(); - - prepareInventoryToView(); - - while (!shouldQuit()) { - - if (_textSlots[0]._str != nullptr) { - changeCursor(0); - } else { - changeCursor(_currentPointerNumber); - - Common::Rect inventoryRect(_invX1, _invY1, _invX1 + _invWidth, _invY1 + _invHeight); - Common::Point mousePos = _system->getEventManager()->getMousePos(); - - if (!_invCurInside && inventoryRect.contains(mousePos)) { - _invCurInside = true; - } - - if (_invCurInside && !inventoryRect.contains(mousePos)) { - inventoryFlagChange(false); - _invCurInside = false; - break; - } - } - - rememberScreenInv(); - - Graphics::Surface *suitcase = _suitcaseBmp->getSurface(); - _graph->drawTransparentSurface(_graph->_screenForInventory, 0, 0, suitcase); - - drawInvItems(); - - showTexts(_graph->_screenForInventory); - - if (!_optionsFlag && _textSlots[0]._str == nullptr) { - _selectedMob = checkMob(_graph->_screenForInventory, _invMobList, false); - } - - checkInvOptions(); - - Common::Event event; - Common::EventManager *eventMan = _system->getEventManager(); - while (eventMan->pollEvent(event)) { - switch (event.type) { - case Common::EVENT_KEYDOWN: - keyHandler(event); - break; - case Common::EVENT_LBUTTONDOWN: - inventoryLeftMouseButton(); - break; - case Common::EVENT_RBUTTONDOWN: - inventoryRightMouseButton(); - break; - default: - break; - } - } - - if (!_showInventoryFlag) { - break; - } - - if (shouldQuit()) - return; - - getDebugger()->onFrame(); - _graph->update(_graph->_screenForInventory); - pausePrinceEngine(); - } - - if (_currentPointerNumber == 2) { - _flags->setFlagValue(Flags::SELITEM, _selectedItem); - } else { - _flags->setFlagValue(Flags::SELITEM, 0); - } -} - void PrinceEngine::createDialogBox(int dialogBoxNr) { _dialogLines = 0; int amountOfDialogOptions = 0; @@ -3037,66 +977,6 @@ void PrinceEngine::talkHero(int slot) { _interpreter->increaseString(); } -void PrinceEngine::doTalkAnim(int animNumber, int slot, AnimType animType) { - Text &text = _textSlots[slot]; - int lines = calcTextLines((const char *)_interpreter->getString()); - int time = lines * 30; - if (animType == kNormalAnimation) { - Anim &normAnim = _normAnimList[animNumber]; - if (normAnim._animData != nullptr) { - if (!normAnim._state) { - if (normAnim._currW && normAnim._currH) { - text._color = _flags->getFlagValue(Flags::KOLOR); - text._x = normAnim._currX + normAnim._currW / 2; - text._y = normAnim._currY - 10; - } - } - } - } else if (animType == kBackgroundAnimation) { - if (!_backAnimList[animNumber].backAnims.empty()) { - int currAnim = _backAnimList[animNumber]._seq._currRelative; - Anim &backAnim = _backAnimList[animNumber].backAnims[currAnim]; - if (backAnim._animData != nullptr) { - if (!backAnim._state) { - if (backAnim._currW && backAnim._currH) { - text._color = _flags->getFlagValue(Flags::KOLOR); - text._x = backAnim._currX + backAnim._currW / 2; - text._y = backAnim._currY - 10; - } - } - } - } - } else { - error("doTalkAnim() - wrong animType: %d", animType); - } - text._time = time; - if (getLanguage() == Common::DE_DEU) { - correctStringDEU((char *)_interpreter->getString()); - } - text._str = (const char *)_interpreter->getString(); - _interpreter->increaseString(); -} - -void PrinceEngine::freeNormAnim(int slot) { - if (!_normAnimList.empty()) { - _normAnimList[slot]._state = 1; - if (_normAnimList[slot]._animData != nullptr) { - delete _normAnimList[slot]._animData; - _normAnimList[slot]._animData = nullptr; - } - if (_normAnimList[slot]._shadowData != nullptr) { - delete _normAnimList[slot]._shadowData; - _normAnimList[slot]._shadowData = nullptr; - } - } -} - -void PrinceEngine::freeAllNormAnims() { - for (int i = 0; i < kMaxNormAnims; i++) { - freeNormAnim(i); - } -} - void PrinceEngine::getCurve() { _flags->setFlagValue(Flags::TORX1, _curveData[_curvPos]); _flags->setFlagValue(Flags::TORY1, _curveData[_curvPos + 1]); @@ -3270,1544 +1150,6 @@ void PrinceEngine::scrollCredits() { blackPalette(); } -// Modified version of Graphics::drawLine() to allow breaking the loop and return value -int PrinceEngine::drawLine(int x0, int y0, int x1, int y1, int (*plotProc)(int, int, void *), void *data) { - // Bresenham's line algorithm, as described by Wikipedia - const bool steep = ABS(y1 - y0) > ABS(x1 - x0); - - if (steep) { - SWAP(x0, y0); - SWAP(x1, y1); - } - - const int delta_x = ABS(x1 - x0); - const int delta_y = ABS(y1 - y0); - const int delta_err = delta_y; - int x = x0; - int y = y0; - int err = 0; - - const int x_step = (x0 < x1) ? 1 : -1; - const int y_step = (y0 < y1) ? 1 : -1; - - int stopFlag = 0; - if (steep) - stopFlag = (*plotProc)(y, x, data); - else - stopFlag = (*plotProc)(x, y, data); - - while (x != x1 && !stopFlag) { - x += x_step; - err += delta_err; - if (2 * err > delta_x) { - y += y_step; - err -= delta_x; - } - if (steep) - stopFlag = (*plotProc)(y, x, data); - else - stopFlag = (*plotProc)(x, y, data); - } - return stopFlag; -} - -int PrinceEngine::getPixelAddr(byte *pathBitmap, int x, int y) { - int mask = 128 >> (x & 7); - byte value = pathBitmap[x / 8 + y * 80]; - return (mask & value); -} - -void PrinceEngine::findPoint(int x, int y) { - _fpX = x; - _fpY = y; - - if (getPixelAddr(_roomPathBitmap, x, y)) { - return; - } - - int fpL = x; - int fpU = y; - int fpR = x; - int fpD = y; - - while (1) { - if (fpD != kMaxPicHeight) { - if (getPixelAddr(_roomPathBitmap, x, fpD)) { - _fpX = x; - _fpY = fpD; - break; - } - fpD++; - } - if (fpU) { - if (getPixelAddr(_roomPathBitmap, x, fpU)) { - _fpX = x; - _fpY = fpU; - break; - } - fpU--; - } - if (fpL) { - if (getPixelAddr(_roomPathBitmap, fpL, y)) { - _fpX = fpL; - _fpY = y; - break; - } - fpL--; - } - if (fpR != _sceneWidth) { - if (getPixelAddr(_roomPathBitmap, fpR, y)) { - _fpX = fpR; - _fpY = y; - break; - } - fpR++; - } - if (!fpU && (fpD == kMaxPicHeight)) { - if (!fpL && (fpR == _sceneWidth)) { - break; - } - } - } -} - -Direction PrinceEngine::makeDirection(int x1, int y1, int x2, int y2) { - if (x1 != x2) { - if (y1 != y2) { - if (x1 > x2) { - if (y1 > y2) { - if (x1 - x2 >= y1 - y2) { - return kDirLU; - } else { - return kDirUL; - } - } else { - if (x1 - x2 >= y2 - y1) { - return kDirLD; - } else { - return kDirDL; - } - } - } else { - if (y1 > y2) { - if (x2 - x1 >= y1 - y2) { - return kDirRU; - } else { - return kDirUR; - } - } else { - if (x2 - x1 >= y2 - y1) { - return kDirRD; - } else { - return kDirDR; - } - } - } - } else { - if (x1 >= x2) { - return kDirL; - } else { - return kDirR; - } - } - } else { - if (y1 >= y2) { - return kDirU; - } else { - return kDirD; - } - } -} - -void PrinceEngine::specialPlot(int x, int y) { - if (_coords < _coordsBufEnd) { - WRITE_LE_UINT16(_coords, x); - _coords += 2; - WRITE_LE_UINT16(_coords, y); - _coords += 2; - specialPlot2(x, y); - } -} - -void PrinceEngine::specialPlot2(int x, int y) { - int mask = 128 >> (x & 7); - _roomPathBitmapTemp[x / 8 + y * 80] |= mask; -} - -void PrinceEngine::specialPlotInside(int x, int y) { - if (_coords < _coordsBufEnd) { - WRITE_LE_UINT16(_coords, x); - _coords += 2; - WRITE_LE_UINT16(_coords, y); - _coords += 2; - } -} - -int PrinceEngine::plotTraceLine(int x, int y, void *data) { - PrinceEngine *traceLine = (PrinceEngine *)data; - if (!traceLine->_traceLineFirstPointFlag) { - if (!traceLine->getPixelAddr(traceLine->_roomPathBitmapTemp, x, y)) { - if (traceLine->getPixelAddr(traceLine->_roomPathBitmap, x, y)) { - traceLine->specialPlotInside(x, y); - traceLine->_traceLineLen++; - return 0; - } else { - return -1; - } - } else { - return 1; - } - } else { - traceLine->_traceLineFirstPointFlag = false; - return 0; - } -} - -int PrinceEngine::leftDownDir() { - if (!checkLeftDownDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkLeftDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkDownDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkLeftUpDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkRightDownDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkUpDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkRightDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkRightUpDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - return -1; -} - -int PrinceEngine::leftDir() { - if (!checkLeftDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkLeftUpDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkLeftDownDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkUpDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkDownDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkRightUpDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkRightDownDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkRightDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - return -1; -} - -int PrinceEngine::leftUpDir() { - if (!checkLeftUpDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkLeftDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkUpDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkLeftDownDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkRightUpDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkDownDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkRightDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkRightDownDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - return -1; -} - -int PrinceEngine::rightDownDir() { - if (!checkRightDownDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkRightDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkDownDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkRightUpDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkLeftDownDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkUpDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkLeftDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkLeftUpDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - return -1; -} - -int PrinceEngine::rightDir() { - if (!checkRightDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkRightUpDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkRightDownDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkUpDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkDownDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkLeftUpDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkLeftDownDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkLeftDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - return -1; -} - -int PrinceEngine::rightUpDir() { - if (!checkRightUpDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkRightDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkUpDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkRightDownDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkLeftUpDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkDownDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkLeftDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkLeftDownDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - return -1; -} - -int PrinceEngine::upLeftDir() { - if (!checkLeftUpDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkUpDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkLeftDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkRightUpDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkLeftDownDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkRightDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkDownDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkRightDownDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - return -1; -} - -int PrinceEngine::upDir() { - if (!checkUpDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkLeftUpDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkRightUpDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkLeftDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkRightDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkLeftDownDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkRightDownDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkDownDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - return -1; -} - -int PrinceEngine::upRightDir() { - if (!checkRightUpDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkUpDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkRightDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkLeftUpDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkRightDownDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkLeftDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkDownDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkLeftDownDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - return -1; -} - -int PrinceEngine::downLeftDir() { - if (!checkLeftDownDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkDownDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkLeftDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkRightDownDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkLeftUpDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkRightDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkUpDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkRightUpDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - return -1; -} - -int PrinceEngine::downDir() { - if (!checkDownDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkRightDownDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkLeftDownDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkRightDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkLeftDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkRightUpDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkLeftUpDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkUpDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - return -1; -} - -int PrinceEngine::downRightDir() { - if (!checkRightDownDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkDownDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkRightDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkLeftDownDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkRightUpDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkLeftDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkUpDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - if (!checkLeftUpDir()) { - specialPlot(_checkX, _checkY); - return 0; - } - return -1; -} - -int PrinceEngine::cpe() { - if ((*(_checkBitmap - kPBW) & _checkMask)) { - if ((*(_checkBitmap + kPBW) & _checkMask)) { - int value; - switch (_checkMask) { - case 128: - value = READ_LE_UINT16(_checkBitmap - 1); - value &= 0x4001; - if (value != 0x4001) { - return 0; - } - break; - case 64: - value = *_checkBitmap; - value &= 0xA0; - if (value != 0xA0) { - return 0; - } - break; - case 32: - value = *_checkBitmap; - value &= 0x50; - if (value != 0x50) { - return 0; - } - break; - case 16: - value = *_checkBitmap; - value &= 0x28; - if (value != 0x28) { - return 0; - } - break; - case 8: - value = *_checkBitmap; - value &= 0x14; - if (value != 0x14) { - return 0; - } - break; - case 4: - value = *_checkBitmap; - value &= 0xA; - if (value != 0xA) { - return 0; - } - break; - case 2: - value = *_checkBitmap; - value &= 0x5; - if (value != 0x5) { - return 0; - } - break; - case 1: - value = READ_LE_UINT16(_checkBitmap); - value &= 0x8002; - if (value != 0x8002) { - return 0; - } - break; - default: - error("Wrong _checkMask value - cpe()"); - break; - } - _checkX = _rembX; - _checkY = _rembY; - _checkBitmapTemp = _rembBitmapTemp; - _checkBitmap = _rembBitmap; - _checkMask = _rembMask; - return -1; - } - return 0; - } - return 0; -} - -int PrinceEngine::checkLeftDownDir() { - if (_checkX && _checkY != (kMaxPicHeight / 2 - 1)) { - int tempMask = _checkMask; - if (tempMask != 128) { - tempMask <<= 1; - if ((*(_checkBitmap + kPBW) & tempMask)) { - if (!(*(_checkBitmapTemp + kPBW) & tempMask)) { - _checkBitmap += kPBW; - _checkBitmapTemp += kPBW; - _checkMask = tempMask; - } else { - return 1; - } - } else { - return -1; - } - } else { - if ((*(_checkBitmap + kPBW - 1) & 1)) { - if (!(*(_checkBitmapTemp + kPBW - 1) & 1)) { - _checkBitmap += (kPBW - 1); - _checkBitmapTemp += (kPBW - 1); - _checkMask = 1; - } else { - return 1; - } - } else { - return -1; - } - } - _checkX--; - _checkY++; - return cpe(); - } else { - return -1; - } -} - -int PrinceEngine::checkLeftDir() { - if (_checkX) { - int tempMask = _checkMask; - if (tempMask != 128) { - tempMask <<= 1; - if ((*(_checkBitmap) & tempMask)) { - if (!(*(_checkBitmapTemp) & tempMask)) { - _checkMask = tempMask; - } else { - return 1; - } - } else { - return -1; - } - } else { - if ((*(_checkBitmap - 1) & 1)) { - if (!(*(_checkBitmapTemp - 1) & 1)) { - _checkBitmap--; - _checkBitmapTemp--; - _checkMask = 1; - } else { - return 1; - } - } else { - return -1; - } - } - _checkX--; - return cpe(); - } else { - return -1; - } -} - -int PrinceEngine::checkDownDir() { - if (_checkY != (kMaxPicHeight / 2 - 1)) { - if ((*(_checkBitmap + kPBW) & _checkMask)) { - if (!(*(_checkBitmapTemp + kPBW) & _checkMask)) { - _checkBitmap += kPBW; - _checkBitmapTemp += kPBW; - _checkY++; - return cpe(); - } else { - return 1; - } - } else { - return -1; - } - } else { - return -1; - } -} - -int PrinceEngine::checkUpDir() { - if (_checkY) { - if ((*(_checkBitmap - kPBW) & _checkMask)) { - if (!(*(_checkBitmapTemp - kPBW) & _checkMask)) { - _checkBitmap -= kPBW; - _checkBitmapTemp -= kPBW; - _checkY--; - return cpe(); - } else { - return 1; - } - } else { - return -1; - } - } else { - return -1; - } -} - -int PrinceEngine::checkRightDir() { - if (_checkX != (kMaxPicWidth / 2 - 1)) { - int tempMask = _checkMask; - if (tempMask != 1) { - tempMask >>= 1; - if ((*(_checkBitmap) & tempMask)) { - if (!(*(_checkBitmapTemp) & tempMask)) { - _checkMask = tempMask; - } else { - return 1; - } - } else { - return -1; - } - } else { - if ((*(_checkBitmap + 1) & 128)) { - if (!(*(_checkBitmapTemp + 1) & 128)) { - _checkBitmap++; - _checkBitmapTemp++; - _checkMask = 128; - } else { - return 1; - } - } else { - return -1; - } - } - _checkX++; - return cpe(); - } else { - return -1; - } -} - -int PrinceEngine::checkLeftUpDir() { - if (_checkX && _checkY) { - int tempMask = _checkMask; - if (tempMask != 128) { - tempMask <<= 1; - if ((*(_checkBitmap - kPBW) & tempMask)) { - if (!(*(_checkBitmapTemp - kPBW) & tempMask)) { - _checkBitmap -= kPBW; - _checkBitmapTemp -= kPBW; - _checkMask = tempMask; - } else { - return 1; - } - } else { - return -1; - } - } else { - if ((*(_checkBitmap - (kPBW + 1)) & 1)) { - if (!(*(_checkBitmapTemp - (kPBW + 1)) & 1)) { - _checkBitmap -= (kPBW + 1); - _checkBitmapTemp -= (kPBW + 1); - _checkMask = 1; - } else { - return 1; - } - } else { - return -1; - } - } - _checkX--; - _checkY--; - return cpe(); - } else { - return -1; - } -} - -int PrinceEngine::checkRightDownDir() { - if (_checkX != (kMaxPicWidth / 2 - 1) && _checkY != (kMaxPicHeight / 2 - 1)) { - int tempMask = _checkMask; - if (tempMask != 1) { - tempMask >>= 1; - if ((*(_checkBitmap + kPBW) & tempMask)) { - if (!(*(_checkBitmapTemp + kPBW) & tempMask)) { - _checkBitmap += kPBW; - _checkBitmapTemp += kPBW; - _checkMask = tempMask; - } else { - return 1; - } - } else { - return -1; - } - } else { - if ((*(_checkBitmap + kPBW + 1) & 128)) { - if (!(*(_checkBitmapTemp + kPBW + 1) & 128)) { - _checkBitmap += kPBW + 1; - _checkBitmapTemp += kPBW + 1; - _checkMask = 128; - } else { - return 1; - } - } else { - return -1; - } - } - _checkX++; - _checkY++; - return cpe(); - } else { - return -1; - } -} - -int PrinceEngine::checkRightUpDir() { - if (_checkX != (kMaxPicWidth / 2 - 1) && _checkY) { - int tempMask = _checkMask; - if (tempMask != 1) { - tempMask >>= 1; - if ((*(_checkBitmap - kPBW) & tempMask)) { - if (!(*(_checkBitmapTemp - kPBW) & tempMask)) { - _checkBitmap -= kPBW; - _checkBitmapTemp -= kPBW; - _checkMask = tempMask; - } else { - return 1; - } - } else { - return -1; - } - } else { - if ((*(_checkBitmap - kPBW + 1) & 128)) { - if (!(*(_checkBitmapTemp - kPBW + 1) & 128)) { - _checkBitmap -= (kPBW - 1); - _checkBitmapTemp -= (kPBW - 1); - _checkMask = 128; - } else { - return 1; - } - } else { - return -1; - } - } - _checkX++; - _checkY--; - return cpe(); - } else { - return -1; - } -} - -bool PrinceEngine::tracePath(int x1, int y1, int x2, int y2) { - for (uint i = 0; i < kPathBitmapLen; i++) { - _roomPathBitmapTemp[i] = 0; - } - if (x1 != x2 || y1 != y2) { - if (getPixelAddr(_roomPathBitmap, x1, y1)) { - if (getPixelAddr(_roomPathBitmap, x2, y2)) { - _coords = _coordsBuf; - specialPlot(x1, y1); - - int x = x1; - int y = y1; - - while (1) { - int btx = x; - int bty = y; - byte *bcad = _coords; - - _traceLineLen = 0; - _traceLineFirstPointFlag = true; - int drawLineFlag = drawLine(x, y, x2, y2, &this->plotTraceLine, this); - - if (!drawLineFlag) { - return true; - } else if (drawLineFlag == -1 && _traceLineLen >= 2) { - byte *tempCorrds = bcad; - while (tempCorrds != _coords) { - x = READ_LE_UINT16(tempCorrds); - y = READ_LE_UINT16(tempCorrds + 2); - tempCorrds += 4; - specialPlot2(x, y); - } - } else { - _coords = bcad; - x = btx; - y = bty; - } - - Direction dir = makeDirection(x, y, x2, y2); - - _rembBitmapTemp = &_roomPathBitmapTemp[x / 8 + y * 80]; - _rembBitmap = &_roomPathBitmap[x / 8 + y * 80]; - _rembMask = 128 >> (x & 7); - _rembX = x; - _rembY = y; - - _checkBitmapTemp = _rembBitmapTemp; - _checkBitmap = _rembBitmap; - _checkMask = _rembMask; - _checkX = _rembX; - _checkY = _rembY; - - int result; - switch (dir) { - case kDirLD: - result = leftDownDir(); - break; - case kDirL: - result = leftDir(); - break; - case kDirLU: - result = leftUpDir(); - break; - case kDirRD: - result = rightDownDir(); - break; - case kDirR: - result = rightDir(); - break; - case kDirRU: - result = rightUpDir(); - break; - case kDirUL: - result = upLeftDir(); - break; - case kDirU: - result = upDir(); - break; - case kDirUR: - result = upRightDir(); - break; - case kDirDL: - result = downLeftDir(); - break; - case kDirD: - result = downDir(); - break; - case kDirDR: - result = downRightDir(); - break; - default: - result = -1; - error("tracePath: wrong direction %d", dir); - break; - } - - if (result) { - byte *tempCoords = _coords; - tempCoords -= 4; - if (tempCoords > _coordsBuf) { - int tempX = READ_LE_UINT16(tempCoords); - int tempY = READ_LE_UINT16(tempCoords + 2); - if (_checkX == tempX && _checkY == tempY) { - _coords = tempCoords; - } - x = READ_LE_UINT16(tempCoords); - y = READ_LE_UINT16(tempCoords + 2); - } else { - return false; - } - } else { - x = _checkX; - y = _checkY; - } - } - return true; - } else { - error("tracePath: wrong destination point"); - } - } else { - error("tracePath: wrong start point"); - } - } else { - error("tracePath: same point"); - } -} - -void PrinceEngine::specialPlotInside2(int x, int y) { - WRITE_LE_UINT16(_coords2, x); - _coords2 += 2; - WRITE_LE_UINT16(_coords2, y); - _coords2 += 2; -} - -int PrinceEngine::plotTracePoint(int x, int y, void *data) { - PrinceEngine *tracePoint = (PrinceEngine *)data; - if (!tracePoint->_tracePointFirstPointFlag) { - if (tracePoint->getPixelAddr(tracePoint->_roomPathBitmap, x, y)) { - tracePoint->specialPlotInside2(x, y); - return 0; - } else { - return -1; - } - } else { - tracePoint->_tracePointFirstPointFlag = false; - return 0; - } -} - -void PrinceEngine::approxPath() { - byte *oldCoords; - _coords2 = _coordsBuf2; - byte *tempCoordsBuf = _coordsBuf; // first point on path - byte *tempCoords = _coords; - if (tempCoordsBuf != tempCoords) { - tempCoords -= 4; // last point on path - while (tempCoordsBuf != tempCoords) { - int x1 = READ_LE_UINT16(tempCoords); - int y1 = READ_LE_UINT16(tempCoords + 2); - int x2 = READ_LE_UINT16(tempCoordsBuf); - int y2 = READ_LE_UINT16(tempCoordsBuf + 2); - tempCoordsBuf += 4; - //TracePoint - oldCoords = _coords2; - if (_coords2 == _coordsBuf2) { - WRITE_LE_UINT16(_coords2, x1); - WRITE_LE_UINT16(_coords2 + 2, y1); - _coords2 += 4; - } else { - int testX = READ_LE_UINT16(_coords2 - 4); - int testY = READ_LE_UINT16(_coords2 - 2); - if (testX != x1 || testY != y1) { - WRITE_LE_UINT16(_coords2, x1); - WRITE_LE_UINT16(_coords2 + 2, y1); - _coords2 += 4; - } - } - _tracePointFirstPointFlag = true; - bool drawLineFlag = drawLine(x1, y1, x2, y2, &this->plotTracePoint, this); - if (!drawLineFlag) { - tempCoords = tempCoordsBuf - 4; - tempCoordsBuf = _coordsBuf; - } else { - _coords2 = oldCoords; - } - } - } -} - -void PrinceEngine::freeDirectionTable() { - if (_directionTable != nullptr) { - free(_directionTable); - _directionTable = nullptr; - } -} - -int PrinceEngine::scanDirectionsFindNext(byte *tempCoordsBuf, int xDiff, int yDiff) { - - int tempX, tempY, direction; - - tempX = Hero::kHeroDirLeft; - if (xDiff < 0) { - tempX = Hero::kHeroDirRight; - } - - tempY = Hero::kHeroDirUp; - if (yDiff < 0) { - tempY = Hero::kHeroDirDown; - } - - while (1) { - int againPointX1 = READ_LE_UINT16(tempCoordsBuf); - int againPointY1 = READ_LE_UINT16(tempCoordsBuf + 2); - tempCoordsBuf += 4; - - if (tempCoordsBuf == _coords) { - direction = tempX; - break; - } - - int dX = againPointX1 - READ_LE_UINT16(tempCoordsBuf); - int dY = againPointY1 - READ_LE_UINT16(tempCoordsBuf + 2); - - if (dX != xDiff) { - direction = tempY; - break; - } - - if (dY != yDiff) { - direction = tempX; - break; - } - } - return direction; -} - -void PrinceEngine::scanDirections() { - freeDirectionTable(); - byte *tempCoordsBuf = _coordsBuf; - if (tempCoordsBuf != _coords) { - int size = (_coords - tempCoordsBuf) / 4 + 1; // number of coord points plus one for end marker - _directionTable = (byte *)malloc(size); - byte *tempDirTab = _directionTable; - int direction = -1; - int lastDirection = -1; - - while (1) { - int x1 = READ_LE_UINT16(tempCoordsBuf); - int y1 = READ_LE_UINT16(tempCoordsBuf + 2); - tempCoordsBuf += 4; - if (tempCoordsBuf == _coords) { - break; - } - int x2 = READ_LE_UINT16(tempCoordsBuf); - int y2 = READ_LE_UINT16(tempCoordsBuf + 2); - - int xDiff = x1 - x2; - int yDiff = y1 - y2; - - if (xDiff) { - if (yDiff) { - if (lastDirection != -1) { - direction = lastDirection; - if (direction == Hero::kHeroDirLeft) { - if (xDiff < 0) { - direction = scanDirectionsFindNext(tempCoordsBuf, xDiff, yDiff); - } - } else if (direction == Hero::kHeroDirRight) { - if (xDiff >= 0) { - direction = scanDirectionsFindNext(tempCoordsBuf, xDiff, yDiff); - } - } else if (direction == Hero::kHeroDirUp) { - if (yDiff < 0) { - direction = scanDirectionsFindNext(tempCoordsBuf, xDiff, yDiff); - } - } else { - if (yDiff >= 0) { - direction = scanDirectionsFindNext(tempCoordsBuf, xDiff, yDiff); - } - } - } else { - direction = scanDirectionsFindNext(tempCoordsBuf, xDiff, yDiff); - } - } else { - direction = Hero::kHeroDirLeft; - if (xDiff < 0) { - direction = Hero::kHeroDirRight; - } - } - } else { - if (yDiff) { - direction = Hero::kHeroDirUp; - if (yDiff < 0) { - direction = Hero::kHeroDirDown; - } - } else { - direction = lastDirection; - } - } - lastDirection = direction; - *tempDirTab = direction; - tempDirTab++; - } - *tempDirTab = *(tempDirTab - 1); - tempDirTab++; - *tempDirTab = 0; - } -} - -void PrinceEngine::moveShandria() { - int shanLen1 = _shanLen; - if (_flags->getFlagValue(Flags::SHANDOG)) { - _secondHero->freeHeroAnim(); - _secondHero->freeOldMove(); - byte *shanCoords = _mainHero->_currCoords + shanLen1 * 4 - 4; - int shanX = READ_LE_UINT16(shanCoords - 4); - int shanY = READ_LE_UINT16(shanCoords - 2); - int xDiff = shanX - _secondHero->_middleX; - if (xDiff < 0) { - xDiff *= -1; - } - int yDiff = shanY - _secondHero->_middleY; - if (yDiff < 0) { - yDiff *= -1; - } - shanCoords -= 4; - if (shanCoords != _mainHero->_currCoords) { - yDiff *= 1.5; - int shanDis = xDiff * xDiff + yDiff * yDiff; - if (shanDis >= kMinDistance) { - while (1) { - shanCoords -= 4; - if (shanCoords == _mainHero->_currCoords) { - break; - } - int x = READ_LE_UINT16(shanCoords); - int y = READ_LE_UINT16(shanCoords + 2); - int pointDiffX = x - shanX; - if (pointDiffX < 0) { - pointDiffX *= -1; - } - int pointDiffY = y - shanY; - if (pointDiffY < 0) { - pointDiffY *= -1; - } - pointDiffY *= 1.5; - int distance = pointDiffX * pointDiffX + pointDiffY * pointDiffY; - if (distance >= kMinDistance) { - break; - } - } - int pathSizeDiff = (shanCoords - _mainHero->_currCoords) / 4; - int destDir = *(_mainHero->_currDirTab + pathSizeDiff); - _secondHero->_destDirection = destDir; - int destX = READ_LE_UINT16(shanCoords); - int destY = READ_LE_UINT16(shanCoords + 2); - _secondHero->_coords = makePath(kSecondHero, _secondHero->_middleX, _secondHero->_middleY, destX, destY); - if (_secondHero->_coords != nullptr) { - _secondHero->_currCoords = _secondHero->_coords; - int delay = shanLen1 - _shanLen; - if (delay < 6) { - delay = 6; - } - _secondHero->_moveDelay = delay / 2; - _secondHero->_state = Hero::kHeroStateDelayMove; - _secondHero->_dirTab = _directionTable; - _secondHero->_currDirTab = _directionTable; - _directionTable = nullptr; - } - } - } - } -} - -byte *PrinceEngine::makePath(int heroId, int currX, int currY, int destX, int destY) { - int realDestX = destX; - int realDestY = destY; - _flags->setFlagValue(Flags::MOVEDESTX, destX); - _flags->setFlagValue(Flags::MOVEDESTY, destY); - - int x1 = currX / 2; - int y1 = currY / 2; - int x2 = destX / 2; - int y2 = destY / 2; - - if ((x1 != x2) || (y1 != y2)) { - findPoint(x1, y1); - if (!getPixelAddr(_roomPathBitmap, _fpX, _fpY)) { - return nullptr; - } - if ((x1 != _fpX) || (y1 != _fpY)) { - x1 = _fpX; - y1 = _fpY; - } - findPoint(x2, y2); - if (!getPixelAddr(_roomPathBitmap, _fpX, _fpY)) { - return nullptr; - } - if ((x2 != _fpX) || (y2 != _fpY)) { - x2 = _fpX; - y2 = _fpY; - if (!_flags->getFlagValue(Flags::EXACTMOVE)) { - realDestX = x2 * 2; - realDestY = y2 * 2; - _flags->setFlagValue(Flags::MOVEDESTX, realDestX); - _flags->setFlagValue(Flags::MOVEDESTY, realDestY); - } else { - return nullptr; - } - } - - if ((x1 == x2) && (y1 == y2)) { - if (!heroId) { - _mainHero->freeOldMove(); - _mainHero->_state = Hero::kHeroStateTurn; - } else if (heroId == 1) { - _secondHero->freeOldMove(); - _secondHero->_state = Hero::kHeroStateTurn; - } - return nullptr; - } - - int pathLen1 = 0; - int pathLen2 = 0; - int stX = x1; - int stY = y1; - int sizeCoords2 = 0; - - if (tracePath(x1, y1, x2, y2)) { - allocCoords2(); - approxPath(); - sizeCoords2 = _coords2 - _coordsBuf2; - for (int i = 0; i < sizeCoords2; i++) { - _coordsBuf[i] = _coordsBuf2[i]; - } - _coords = _coordsBuf + sizeCoords2; - approxPath(); - _coordsBuf3 = _coordsBuf2; - _coordsBuf2 = nullptr; - _coords3 = _coords2; - _coords2 = nullptr; - pathLen1 = _coords3 - _coordsBuf3; - } - if (tracePath(x2, y2, x1, y1)) { - allocCoords2(); - approxPath(); - sizeCoords2 = _coords2 - _coordsBuf2; - for (int i = 0; i < sizeCoords2; i++) { - _coordsBuf[i] = _coordsBuf2[i]; - } - _coords = _coordsBuf + sizeCoords2; - approxPath(); - pathLen2 = _coords2 - _coordsBuf2; - } - - byte *chosenCoordsBuf = _coordsBuf2; - byte *choosenCoords = _coords2; - int choosenLength = pathLen1; - if (pathLen1 < pathLen2) { - chosenCoordsBuf = _coordsBuf3; - choosenCoords = _coords3; - choosenLength = pathLen2; - } - - if (choosenLength) { - if (chosenCoordsBuf != nullptr) { - int tempXBegin = READ_LE_UINT16(chosenCoordsBuf); - int tempYBegin = READ_LE_UINT16(chosenCoordsBuf + 2); - if (stX != tempXBegin || stY != tempYBegin) { - SWAP(chosenCoordsBuf, choosenCoords); - chosenCoordsBuf -= 4; - byte *tempCoordsBuf = _coordsBuf; - while (1) { - int cord = READ_LE_UINT32(chosenCoordsBuf); - WRITE_LE_UINT32(tempCoordsBuf, cord); - tempCoordsBuf += 4; - if (chosenCoordsBuf == choosenCoords) { - break; - } - chosenCoordsBuf -= 4; - } - _coords = tempCoordsBuf; - } else { - int sizeChoosen = choosenCoords - chosenCoordsBuf; - for (int i = 0; i < sizeChoosen; i++) { - _coordsBuf[i] = chosenCoordsBuf[i]; - } - _coords = _coordsBuf + sizeChoosen; - } - WRITE_LE_UINT32(_coords, 0xFFFFFFFF); - freeCoords2(); - freeCoords3(); - scanDirections(); - - byte *tempCoordsBuf = _coordsBuf; - byte *tempCoords = _coords; - byte *newCoords; - if (tempCoordsBuf != tempCoords) { - int normCoordsSize = _coords - _coordsBuf + 4; - newCoords = (byte *)malloc(normCoordsSize); - byte *newCoordsBegin = newCoords; - while (tempCoordsBuf != tempCoords) { - int newValueX = READ_LE_UINT16(tempCoordsBuf); - WRITE_LE_UINT16(newCoords, newValueX * 2); - newCoords += 2; - int newValueY = READ_LE_UINT16(tempCoordsBuf + 2); - WRITE_LE_UINT16(newCoords, newValueY * 2); - newCoords += 2; - tempCoordsBuf += 4; - } - WRITE_LE_UINT16(newCoords - 4, realDestX); - WRITE_LE_UINT16(newCoords - 2, realDestY); - WRITE_LE_UINT32(newCoords, 0xFFFFFFFF); - newCoords += 4; - _shanLen = (newCoords - newCoordsBegin); - _shanLen /= 4; - return newCoordsBegin; - } - } - } - _coords = _coordsBuf; - freeCoords2(); - freeCoords3(); - return nullptr; - } else { - if (!heroId) { - _mainHero->freeOldMove(); - _mainHero->_state = Hero::kHeroStateTurn; - } else if (heroId == 1) { - _secondHero->freeOldMove(); - _secondHero->_state = Hero::kHeroStateTurn; - } - return nullptr; - } -} - -void PrinceEngine::allocCoords2() { - if (_coordsBuf2 == nullptr) { - _coordsBuf2 = (byte *)malloc(kTracePts * 4); - _coords2 = _coordsBuf2; - } -} - -void PrinceEngine::freeCoords2() { - if (_coordsBuf2 != nullptr) { - free(_coordsBuf2); - _coordsBuf2 = nullptr; - _coords2 = nullptr; - } -} - -void PrinceEngine::freeCoords3() { - if (_coordsBuf3 != nullptr) { - free(_coordsBuf3); - _coordsBuf3 = nullptr; - _coords3 = nullptr; - } -} - -void PrinceEngine::openInventoryCheck() { - if (!_optionsFlag) { - if (_mouseFlag == 1 || _mouseFlag == 2) { - if (_mainHero->_visible) { - if (!_flags->getFlagValue(Flags::INVALLOWED)) { - // 29 - Basement, 50 - Map - if (_locationNr != 29 && _locationNr != 50) { - Common::Point mousePos = _system->getEventManager()->getMousePos(); - if (mousePos.y < 4 && !_showInventoryFlag) { - _invCounter++; - } else { - _invCounter = 0; - } - if (_invCounter >= _invMaxCount) { - inventoryFlagChange(true); - } - } - } - } - } - } -} - void PrinceEngine::mainLoop() { changeCursor(0); _currentTime = _system->getMillis(); diff --git a/engines/prince/prince.h b/engines/prince/prince.h index 32e37e0774..e0b9490c37 100644 --- a/engines/prince/prince.h +++ b/engines/prince/prince.h @@ -38,7 +38,6 @@ #include "gui/debugger.h" -#include "engines/advancedDetector.h" #include "engines/engine.h" #include "engines/util.h" @@ -58,14 +57,10 @@ enum PrinceGameType { kPrinceDataPL }; -struct PrinceGameDescription { - ADGameDescription desc; - PrinceGameType gameType; -}; - struct SavegameHeader; class PrinceEngine; +struct PrinceGameDescription; class GraphicsMan; class Script; class Interpreter; @@ -82,15 +77,18 @@ class Room; class Pscr; enum { - GF_TRANSLATED = 1 << 0 + GF_TRANSLATED = 1 << 0, + GF_EXTRACTED = 1 << 1, + GF_NOVOICES = 1 << 2 }; struct SavegameHeader { uint8 version; Common::String saveName; Graphics::Surface *thumbnail; - int saveYear, saveMonth, saveDay; - int saveHour, saveMinutes; + int16 saveYear, saveMonth, saveDay; + int16 saveHour, saveMinutes; + uint32 playTime; }; #define kSavegameStrSize 14 @@ -281,6 +279,8 @@ public: PrinceEngine(OSystem *syst, const PrinceGameDescription *gameDesc); virtual ~PrinceEngine(); + bool scummVMSaveLoadDialog(bool isSave); + virtual bool hasFeature(EngineFeature f) const; virtual void pauseEngineIntern(bool pause); virtual bool canSaveGameStateCurrently(); @@ -290,7 +290,7 @@ public: void playVideo(Common::String videoFilename); - static bool readSavegameHeader(Common::InSaveFile *in, SavegameHeader &header); + WARN_UNUSED_RESULT static bool readSavegameHeader(Common::InSaveFile *in, SavegameHeader &header, bool skipThumbnail = true); Common::String generateSaveName(int slot); void writeSavegameHeader(Common::OutSaveFile *out, SavegameHeader &header); void syncGame(Common::SeekableReadStream *readStream, Common::WriteStream *writeStream); diff --git a/engines/prince/pscr.cpp b/engines/prince/pscr.cpp index 4f79704e62..481d17cace 100644 --- a/engines/prince/pscr.cpp +++ b/engines/prince/pscr.cpp @@ -24,6 +24,7 @@ #include "common/stream.h" #include "prince/pscr.h" +#include "prince/resource.h" namespace Prince { @@ -64,6 +65,8 @@ bool PScr::loadFromStream(Common::SeekableReadStream &stream) { const Common::String pscrStreamName = Common::String::format("PS%02d", file); Common::SeekableReadStream *pscrStream = SearchMan.createReadStreamForMember(pscrStreamName); if (pscrStream != nullptr) { + pscrStream = Resource::getDecompressedStream(pscrStream); + loadSurface(*pscrStream); } delete pscrStream; diff --git a/engines/prince/resource.cpp b/engines/prince/resource.cpp new file mode 100644 index 0000000000..34de29b388 --- /dev/null +++ b/engines/prince/resource.cpp @@ -0,0 +1,371 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can 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/fs.h" +#include "common/config-manager.h" + +#include "prince/prince.h" +#include "prince/graphics.h" +#include "prince/debugger.h" +#include "prince/script.h" +#include "prince/hero.h" +#include "prince/resource.h" +#include "prince/archive.h" + +namespace Prince { + +Common::SeekableReadStream *Resource::getDecompressedStream(Common::SeekableReadStream *stream) { + if (!(((PrinceEngine *)g_engine)->getFeatures() & GF_EXTRACTED)) + return stream; + + byte header[4]; + + stream->read(header, 4); + stream->seek(0); + + if (READ_BE_UINT32(header) == MKTAG('M', 'A', 'S', 'M')) { + byte *buffer = (byte *)malloc(stream->size()); + stream->read(buffer, stream->size()); + + Decompressor dec; + uint32 decompLen = READ_BE_UINT32(buffer + 14); + byte *decompData = (byte *)malloc(decompLen); + dec.decompress(buffer + 18, decompData, decompLen); + free(buffer); + + debug(8, "Resource::getDecompressedStream: decompressed %d to %d bytes", stream->size(), decompLen); + + return new Common::MemoryReadStream(decompData, decompLen, DisposeAfterUse::YES); + } else { + return stream; + } +} + +bool AnimListItem::loadFromStream(Common::SeekableReadStream &stream) { + int32 pos = stream.pos(); + + uint16 type = stream.readUint16LE(); + if (type == 0xFFFF) { + return false; + } + _type = type; + _fileNumber = stream.readUint16LE(); + _startPhase = stream.readUint16LE(); + _endPhase = stream.readUint16LE(); + _loopPhase = stream.readUint16LE(); + _x = stream.readSint16LE(); + _y = stream.readSint16LE(); + _loopType = stream.readUint16LE(); + _nextAnim = stream.readUint16LE(); + _flags = stream.readUint16LE(); + + debug(7, "AnimListItem type %d, fileNumber %d, x %d, y %d, flags %d", _type, _fileNumber, _x, _y, _flags); + debug(7, "startPhase %d, endPhase %d, loopPhase %d", _startPhase, _endPhase, _loopPhase); + + // 32 byte aligment + stream.seek(pos + 32); + + return true; +} + +bool PrinceEngine::loadLocation(uint16 locationNr) { + + blackPalette(); + + _flicPlayer.close(); + + memset(_textSlots, 0, sizeof(_textSlots)); + freeAllSamples(); + + debugEngine("PrinceEngine::loadLocation %d", locationNr); + const Common::FSNode gameDataDir(ConfMan.get("path")); + SearchMan.remove(Common::String::format("%02d", _locationNr)); + + _locationNr = locationNr; + _debugger->_locationNr = locationNr; + + _flags->setFlagValue(Flags::CURRROOM, _locationNr); + _interpreter->stopBg(); + + changeCursor(0); + + const Common::String locationNrStr = Common::String::format("%02d", _locationNr); + debugEngine("loadLocation %s", locationNrStr.c_str()); + + if (!(getFeatures() & GF_EXTRACTED)) { + PtcArchive *locationArchive = new PtcArchive(); + if (!locationArchive->open(locationNrStr + "/databank.ptc")) + error("Can't open location %s", locationNrStr.c_str()); + + SearchMan.add(locationNrStr, locationArchive); + } else { + SearchMan.addSubDirectoryMatching(gameDataDir, locationNrStr); + } + + loadMusic(_locationNr); + + // load location background, replace old one + Resource::loadResource(_roomBmp, "room", true); + if (_roomBmp->getSurface()) { + _sceneWidth = _roomBmp->getSurface()->w; + } + + loadZoom(_zoomBitmap, kZoomBitmapLen, "zoom"); + loadShadow(_shadowBitmap, kShadowBitmapSize, "shadow", "shadow2"); + loadTrans(_transTable, "trans"); + loadPath("path"); + + for (uint32 i = 0; i < _pscrList.size(); i++) { + delete _pscrList[i]; + } + _pscrList.clear(); + Resource::loadResource(_pscrList, "pscr.lst", false); + + loadMobPriority("mobpri"); + + _mobList.clear(); + if (getGameType() == kPrinceDataDE) { + const Common::String mobLstName = Common::String::format("mob%02d.lst", _locationNr); + debug(3, "moblist name: %s", mobLstName.c_str()); + Resource::loadResource(_mobList, mobLstName.c_str(), false); + } else if (getGameType() == kPrinceDataPL) { + Resource::loadResource(_mobList, "mob.lst", false); + } + if (getFeatures() & GF_TRANSLATED) { + // update Mob texts for translated version + setMobTranslationTexts(); + } + + _animList.clear(); + Resource::loadResource(_animList, "anim.lst", false); + + for (uint32 i = 0; i < _objList.size(); i++) { + delete _objList[i]; + } + _objList.clear(); + Resource::loadResource(_objList, "obj.lst", false); + + _room->loadRoom(_script->getRoomOffset(_locationNr)); + + for (uint i = 0; i < _maskList.size(); i++) { + free(_maskList[i]._data); + } + _maskList.clear(); + _script->loadAllMasks(_maskList, _room->_nak); + + _picWindowX = 0; + + _lightX = _script->getLightX(_locationNr); + _lightY = _script->getLightY(_locationNr); + setShadowScale(_script->getShadowScale(_locationNr)); + + for (uint i = 0; i < _mobList.size(); i++) { + _mobList[i]._visible = _script->getMobVisible(_room->_mobs, i); + } + + _script->installObjects(_room->_obj); + + freeAllNormAnims(); + + clearBackAnimList(); + _script->installBackAnims(_backAnimList, _room->_backAnim); + + _graph->makeShadowTable(70, _graph->_shadowTable70); + _graph->makeShadowTable(50, _graph->_shadowTable50); + + _mainHero->freeOldMove(); + _secondHero->freeOldMove(); + + _mainHero->scrollHero(); + + return true; +} + +bool PrinceEngine::loadAnim(uint16 animNr, bool loop) { + Common::String streamName = Common::String::format("AN%02d", animNr); + Common::SeekableReadStream *flicStream = SearchMan.createReadStreamForMember(streamName); + + if (!flicStream) { + error("Can't open %s", streamName.c_str()); + return false; + } + + flicStream = Resource::getDecompressedStream(flicStream); + + if (!_flicPlayer.loadStream(flicStream)) { + error("Can't load flic stream %s", streamName.c_str()); + } + + debugEngine("%s loaded", streamName.c_str()); + _flicLooped = loop; + _flicPlayer.start(); + playNextFLCFrame(); + return true; +} + +bool PrinceEngine::loadZoom(byte *zoomBitmap, uint32 dataSize, const char *resourceName) { + Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(resourceName); + if (!stream) { + delete stream; + return false; + } + stream = Resource::getDecompressedStream(stream); + + if (stream->read(zoomBitmap, dataSize) != dataSize) { + free(zoomBitmap); + delete stream; + return false; + } + delete stream; + return true; +} + +bool PrinceEngine::loadShadow(byte *shadowBitmap, uint32 dataSize, const char *resourceName1, const char *resourceName2) { + Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(resourceName1); + if (!stream) { + delete stream; + return false; + } + + stream = Resource::getDecompressedStream(stream); + + if (stream->read(shadowBitmap, dataSize) != dataSize) { + free(shadowBitmap); + delete stream; + return false; + } + + Common::SeekableReadStream *stream2 = SearchMan.createReadStreamForMember(resourceName2); + if (!stream2) { + delete stream; + delete stream2; + return false; + } + + stream2 = Resource::getDecompressedStream(stream2); + + byte *shadowBitmap2 = shadowBitmap + dataSize; + if (stream2->read(shadowBitmap2, dataSize) != dataSize) { + free(shadowBitmap); + delete stream; + delete stream2; + return false; + } + + delete stream; + delete stream2; + return true; +} + +bool PrinceEngine::loadTrans(byte *transTable, const char *resourceName) { + Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(resourceName); + if (!stream) { + delete stream; + for (int i = 0; i < 256; i++) { + for (int j = 0; j < 256; j++) { + transTable[i * 256 + j] = j; + } + } + return true; + } + + stream = Resource::getDecompressedStream(stream); + + if (stream->read(transTable, kTransTableSize) != kTransTableSize) { + delete stream; + return false; + } + delete stream; + return true; +} + +bool PrinceEngine::loadPath(const char *resourceName) { + Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(resourceName); + if (!stream) { + delete stream; + return false; + } + + stream = Resource::getDecompressedStream(stream); + + if (stream->read(_roomPathBitmap, kPathBitmapLen) != kPathBitmapLen) { + delete stream; + return false; + } + delete stream; + return true; +} + +bool PrinceEngine::loadAllInv() { + for (int i = 0; i < kMaxInv; i++) { + InvItem tempInvItem; + + const Common::String invStreamName = Common::String::format("INV%02d", i); + Common::SeekableReadStream *invStream = SearchMan.createReadStreamForMember(invStreamName); + if (!invStream) { + delete invStream; + return true; + } + + invStream = Resource::getDecompressedStream(invStream); + + tempInvItem._x = invStream->readUint16LE(); + tempInvItem._y = invStream->readUint16LE(); + int width = invStream->readUint16LE(); + int height = invStream->readUint16LE(); + tempInvItem._surface = new Graphics::Surface(); + tempInvItem._surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8()); + + for (int h = 0; h < tempInvItem._surface->h; h++) { + invStream->read(tempInvItem._surface->getBasePtr(0, h), tempInvItem._surface->w); + } + + _allInvList.push_back(tempInvItem); + delete invStream; + } + + return true; +} + +bool PrinceEngine::loadMobPriority(const char *resourceName) { + Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(resourceName); + if (!stream) { + delete stream; + return false; + } + + stream = Resource::getDecompressedStream(stream); + + _mobPriorityList.clear(); + uint mobId; + while (1) { + mobId = stream->readUint32LE(); + if (mobId == 0xFFFFFFFF) { + break; + } + _mobPriorityList.push_back(mobId); + } + delete stream; + return true; +} + +} // End of namespace Prince diff --git a/engines/prince/resource.h b/engines/prince/resource.h index 96bc2ea26d..1746f2bfb5 100644 --- a/engines/prince/resource.h +++ b/engines/prince/resource.h @@ -24,74 +24,85 @@ #define PRINCE_RESOURCE_H #include "common/stream.h" +#include "common/memstream.h" #include "common/archive.h" #include "common/debug-channels.h" #include "common/ptr.h" +#include "prince/decompress.h" + namespace Prince { namespace Resource { - template <typename T> - bool loadFromStream(T &resource, Common::SeekableReadStream &stream) { - return resource.loadStream(stream); - } +Common::SeekableReadStream *getDecompressedStream(Common::SeekableReadStream *stream); - template<typename T> - bool loadResource(T *resource, const char *resourceName, bool required) { - Common::ScopedPtr<Common::SeekableReadStream> stream(SearchMan.createReadStreamForMember(resourceName)); - if (!stream) { - if (required) - error("Can't load %s", resourceName); - return false; - } +template <typename T> +bool loadFromStream(T &resource, Common::SeekableReadStream &stream) { + return resource.loadStream(stream); +} - return loadFromStream(*resource, *stream); +template<typename T> +bool loadResource(T *resource, const char *resourceName, bool required) { + Common::SeekableReadStream *stream_(SearchMan.createReadStreamForMember(resourceName)); + if (!stream_) { + if (required) + error("Can't load %s", resourceName); + return false; } - template <typename T> - bool loadResource(Common::Array<T> &array, Common::SeekableReadStream &stream, bool required = true) { - T t; - while (t.loadFromStream(stream)) - array.push_back(t); + Common::ScopedPtr<Common::SeekableReadStream> stream(getDecompressedStream(stream_)); - return true; - } + return loadFromStream(*resource, *stream); +} +template <typename T> +bool loadResource(Common::Array<T> &array, Common::SeekableReadStream &stream, bool required = true) { + T t; + while (t.loadFromStream(stream)) + array.push_back(t); - template <typename T> - bool loadResource(Common::Array<T> &array, const char *resourceName, bool required = true) { - Common::ScopedPtr<Common::SeekableReadStream> stream(SearchMan.createReadStreamForMember(resourceName)); - if (!stream) { - if (required) - error("Can't load %s", resourceName); - return false; - } + return true; +} - return loadResource(array, *stream, required); + +template <typename T> +bool loadResource(Common::Array<T> &array, const char *resourceName, bool required = true) { + Common::SeekableReadStream *stream_(SearchMan.createReadStreamForMember(resourceName)); + if (!stream_) { + if (required) + error("Can't load %s", resourceName); + return false; } - template <typename T> - bool loadResource(Common::Array<T *> &array, const char *resourceName, bool required = true) { + Common::ScopedPtr<Common::SeekableReadStream> stream(getDecompressedStream(stream_)); - Common::ScopedPtr<Common::SeekableReadStream> stream(SearchMan.createReadStreamForMember(resourceName)); - if (!stream) { - if (required) - error("Can't load %s", resourceName); - return false; - } + return loadResource(array, *stream, required); +} + +template <typename T> +bool loadResource(Common::Array<T *> &array, const char *resourceName, bool required = true) { - // FIXME: This is stupid. Maybe loadFromStream should be helper method that returns initiailzed object - while (true) { - T* t = new T(); - if (!t->loadFromStream(*stream)) { - delete t; - break; - } - array.push_back(t); + Common::SeekableReadStream *stream_(SearchMan.createReadStreamForMember(resourceName)); + if (!stream_) { + if (required) + error("Can't load %s", resourceName); + return false; + } + + Common::ScopedPtr<Common::SeekableReadStream> stream(getDecompressedStream(stream_)); + + // FIXME: This is stupid. Maybe loadFromStream should be helper method that returns initialized object + while (true) { + T* t = new T(); + if (!t->loadFromStream(*stream)) { + delete t; + break; } - return true; + array.push_back(t); } + return true; +} } diff --git a/engines/prince/saveload.cpp b/engines/prince/saveload.cpp index 14f6078910..b1d9fc49fd 100644 --- a/engines/prince/saveload.cpp +++ b/engines/prince/saveload.cpp @@ -30,22 +30,63 @@ #include "common/system.h" #include "common/config-manager.h" #include "common/memstream.h" +#include "common/translation.h" #include "graphics/thumbnail.h" #include "graphics/surface.h" #include "graphics/palette.h" #include "graphics/scaler.h" +#include "gui/saveload.h" + namespace Prince { -#define kBadSVG 99 #define kSavegameVersion 1 class InterpreterFlags; class Interpreter; -bool PrinceEngine::readSavegameHeader(Common::InSaveFile *in, SavegameHeader &header) { - header.thumbnail = nullptr; +bool PrinceEngine::scummVMSaveLoadDialog(bool isSave) { + GUI::SaveLoadChooser *dialog; + Common::String desc; + int slot; + + if (isSave) { + dialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"), true); + + slot = dialog->runModalWithCurrentTarget(); + desc = dialog->getResultString(); + + if (desc.empty()) { + desc = dialog->createDefaultSaveDescription(slot); + } + } else { + dialog = new GUI::SaveLoadChooser(_("Restore game:"), _("Restore"), false); + slot = dialog->runModalWithCurrentTarget(); + } + + delete dialog; + + if (slot < 0) + return false; + + if (isSave) { + return saveGameState(slot, desc).getCode() == Common::kNoError; + } else { + return loadGameState(slot).getCode() == Common::kNoError; + } +} + +WARN_UNUSED_RESULT bool PrinceEngine::readSavegameHeader(Common::InSaveFile *in, SavegameHeader &header, bool skipThumbnail) { + header.version = 0; + header.saveName.clear(); + header.thumbnail = nullptr; + header.saveYear = 0; + header.saveMonth = 0; + header.saveDay = 0; + header.saveHour = 0; + header.saveMinutes = 0; + header.playTime = 0; // Get the savegame version header.version = in->readByte(); @@ -53,22 +94,22 @@ bool PrinceEngine::readSavegameHeader(Common::InSaveFile *in, SavegameHeader &he return false; // Read in the string - header.saveName.clear(); char ch; while ((ch = (char)in->readByte()) != '\0') header.saveName += ch; // Get the thumbnail - header.thumbnail = Graphics::loadThumbnail(*in); - if (!header.thumbnail) + if (!Graphics::loadThumbnail(*in, header.thumbnail, skipThumbnail)) { return false; + } // Read in save date/time - header.saveYear = in->readSint16LE(); - header.saveMonth = in->readSint16LE(); - header.saveDay = in->readSint16LE(); - header.saveHour = in->readSint16LE(); + header.saveYear = in->readSint16LE(); + header.saveMonth = in->readSint16LE(); + header.saveDay = in->readSint16LE(); + header.saveHour = in->readSint16LE(); header.saveMinutes = in->readSint16LE(); + header.playTime = in->readUint32LE(); return true; } @@ -157,6 +198,8 @@ void PrinceEngine::writeSavegameHeader(Common::OutSaveFile *out, SavegameHeader out->writeSint16LE(td.tm_mday); out->writeSint16LE(td.tm_hour); out->writeSint16LE(td.tm_min); + + out->writeUint32LE(g_engine->getTotalPlayTime() / 1000); } void PrinceEngine::syncGame(Common::SeekableReadStream *readStream, Common::WriteStream *writeStream) { @@ -417,9 +460,7 @@ bool PrinceEngine::loadGame(int slotNumber) { return false; } - // Delete the thumbnail - saveHeader.thumbnail->free(); - delete saveHeader.thumbnail; + g_engine->setTotalPlayTime(saveHeader.playTime * 1000); } // Get in the savegame diff --git a/engines/prince/script.cpp b/engines/prince/script.cpp index 23ef24a268..2581dde1c1 100644 --- a/engines/prince/script.cpp +++ b/engines/prince/script.cpp @@ -222,7 +222,7 @@ void Script::setObjId(int roomObjOffset, int slot, byte objectId) { } int Script::scanMobEvents(int mobMask, int dataEventOffset) { - debug("mobMask: %d", mobMask); + debug(3, "scanMobEvents: mobMask: %d", mobMask); int i = 0; int16 mob; int32 code; @@ -230,7 +230,7 @@ int Script::scanMobEvents(int mobMask, int dataEventOffset) { mob = (int)READ_LE_UINT16(&_data[dataEventOffset + i * 6]); if (mob == mobMask) { code = (int)READ_LE_UINT32(&_data[dataEventOffset + i * 6 + 2]); - debug("code: %d", code); + debug(3, "scanMobEvents: code: %d", code); return code; } i++; @@ -239,7 +239,7 @@ int Script::scanMobEvents(int mobMask, int dataEventOffset) { } int Script::scanMobEventsWithItem(int mobMask, int dataEventOffset, int itemMask) { - debug("mobMask: %d", mobMask); + debug(3, "scanMobEventsWithItem: mobMask: %d", mobMask); int i = 0; int16 mob; int16 item; @@ -250,8 +250,8 @@ int Script::scanMobEventsWithItem(int mobMask, int dataEventOffset, int itemMask item = (int)READ_LE_UINT16(&_data[dataEventOffset + i * 8 + 2]); if (item == itemMask) { code = (int)READ_LE_UINT32(&_data[dataEventOffset + i * 8 + 4]); - debug("itemMask: %d", item); - debug("code: %d", code); + debug(3, "scanMobEventsWithItem: itemMask: %d", item); + debug(3, "scanMobEventsWithItem: code: %d", code); return code; } } @@ -388,9 +388,11 @@ bool Script::loadAllMasks(Common::Array<Mask> &maskList, int offset) { tempMask._width = 0; tempMask._height = 0; tempMask._data = nullptr; - debug("Can't load %s", msStreamName.c_str()); + warning("loadAllMasks: Can't load %s", msStreamName.c_str()); delete msStream; } else { + msStream = Resource::getDecompressedStream(msStream); + int32 dataSize = msStream->size(); if (dataSize != -1) { tempMask._data = (byte *)malloc(dataSize); @@ -450,11 +452,9 @@ void Interpreter::debugInterpreter(const char *s, ...) { Common::String str = Common::String::format("@0x%08X: ", _lastInstruction); str += Common::String::format("op %04d: ", _lastOpcode); - //debugC(10, DebugChannel::kScript, "PrinceEngine::Script %s %s", str.c_str(), buf); if (!strcmp(_mode, "fg")) { debug(10, "PrinceEngine::Script %s %s", str.c_str(), buf); } - //debug("Prince::Script mode %s %s %s", _mode, str.c_str(), buf); } void Interpreter::stepBg() { @@ -916,8 +916,7 @@ void Interpreter::O_ADDFLAG() { _flags->setFlagValue(flagId, _flags->getFlagValue(flagId) + value); if (_flags->getFlagValue(flagId)) { _result = 1; - } - else { + } else { _result = 0; } debugInterpreter("O_ADDFLAG flagId %04x (%s), value %d", flagId, Flags::getFlagName(flagId), value); @@ -936,8 +935,7 @@ void Interpreter::O_SUBFLAG() { _flags->setFlagValue(flagId, _flags->getFlagValue(flagId) - value); if (_flags->getFlagValue(flagId)) { _result = 1; - } - else { + } else { _result = 0; } debugInterpreter("O_SUBFLAG flagId %d, value %d", flagId, value); @@ -949,8 +947,7 @@ void Interpreter::O_SETSTRING() { if (offset >= 80000) { _string = _vm->_variaTxt->getString(offset - 80000); debugInterpreter("GetVaria %s", _string); - } - else if (offset < 2000) { + } else if (offset < 2000) { _vm->_dialogData = &_vm->_dialogDat[offset * 4 - 4]; uint32 of = READ_LE_UINT32(_vm->_talkTxt + offset * 4); const char *txt = (const char *)&_vm->_talkTxt[of]; @@ -1016,6 +1013,12 @@ void Interpreter::O_XORFLAG() { void Interpreter::O_GETMOBTEXT() { int32 mob = readScriptFlagValue(); _currentString = _vm->_locationNr * 100 + mob + 60001; + // FIXME: UB? + // This casts away the constness of the pointer returned by c_str() which is + // stored and potentially modified later (for example in printAt()). + // Also, the pointer is only valid as long as _vm->_mobList[mob] + // is around and _vm->_mobList[mob]._examText hasn't been modified by any of its + // non-const member functions which also might or might not be a problem. _string = (byte *)_vm->_mobList[mob]._examText.c_str(); debugInterpreter("O_GETMOBTEXT mob %d", mob); } @@ -1832,6 +1835,12 @@ void Interpreter::O_DISABLENAK() { void Interpreter::O_GETMOBNAME() { int32 modId = readScriptFlagValue(); + // FIXME: UB? + // This casts away the constness of the pointer returned by c_str() which is + // stored and potentially modified later (for example in printAt()). + // Also, the pointer is only valid as long as _vm->_mobList[mobId] + // is around and _vm->_mobList[mobId]._name hasn't been modified by any of its + // non-const member functions which also might or might not be a problem. _string = (byte *)_vm->_mobList[modId]._name.c_str(); debugInterpreter("O_GETMOBNAME modId %d", modId); } diff --git a/engines/prince/sound.cpp b/engines/prince/sound.cpp index 206b131cd2..49983860be 100644 --- a/engines/prince/sound.cpp +++ b/engines/prince/sound.cpp @@ -20,191 +20,147 @@ * */ -#include "prince/sound.h" -#include "prince/musNum.h" - #include "common/archive.h" -#include "common/debug.h" -#include "audio/mididrv.h" -#include "audio/midiparser.h" + +#include "audio/audiostream.h" +#include "audio/decoders/wave.h" + +#include "prince/prince.h" +#include "prince/hero.h" +#include "prince/script.h" namespace Prince { -const char *MusicPlayer::_musTable[] = { - "", - "Battlfld.mid", - "Cave.mid", - "Cemetery.mid", - "Credits.mid", - "Fjord.mid", - "Guitar.mid", - "Hell.mid", - "Jingle.mid", - "Main.mid", - "Night.mid", - "Reality.mid", - "Sunlord.mid", - "Tavern.mid", - "Temple.mid", - "Boruta.mid", - "Intro.mid" -}; - -const uint8 MusicPlayer::_musRoomTable[] = { - 0, - ROOM01MUS, - ROOM02MUS, - ROOM03MUS, - ROOM04MUS, - ROOM05MUS, - ROOM06MUS, - ROOM07MUS, - ROOM08MUS, - ROOM09MUS, - ROOM10MUS, - ROOM11MUS, - ROOM12MUS, - ROOM13MUS, - ROOM14MUS, - ROOM15MUS, - ROOM16MUS, - ROOM17MUS, - ROOM18MUS, - ROOM19MUS, - ROOM20MUS, - ROOM21MUS, - ROOM22MUS, - ROOM23MUS, - ROOM24MUS, - ROOM25MUS, - ROOM26MUS, - ROOM27MUS, - ROOM28MUS, - ROOM29MUS, - ROOM30MUS, - ROOM31MUS, - ROOM32MUS, - ROOM33MUS, - ROOM34MUS, - ROOM35MUS, - ROOM36MUS, - ROOM37MUS, - ROOM38MUS, - ROOM39MUS, - ROOM40MUS, - ROOM41MUS, - ROOM42MUS, - ROOM43MUS, - 0, - 0, - ROOM46MUS, - ROOM47MUS, - ROOM48MUS, - ROOM49MUS, - ROOM50MUS, - ROOM51MUS, - ROOM52MUS, - ROOM53MUS, - ROOM54MUS, - ROOM55MUS, - ROOM56MUS, - ROOM57MUS, - ROOM58MUS, - ROOM59MUS, - ROOM60MUS, - ROOM61MUS -}; - - -MusicPlayer::MusicPlayer(PrinceEngine *vm) : _vm(vm) { - _data = nullptr; - _dataSize = 0; - _isGM = false; - - MidiPlayer::createDriver(); - - int ret = _driver->open(); - if (ret == 0) { - if (_nativeMT32) - _driver->sendMT32Reset(); - else - _driver->sendGMReset(); - - _driver->setTimerCallback(this, &timerCallback); +void PrinceEngine::playSample(uint16 sampleId, uint16 loopType) { + if (_audioStream[sampleId]) { + if (_mixer->isSoundIDActive(sampleId)) { + return; + } + _audioStream[sampleId]->rewind(); + if (sampleId < 28) { + _mixer->playStream(Audio::Mixer::kSFXSoundType, &_soundHandle[sampleId], _audioStream[sampleId], sampleId, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO); + } else { + _mixer->playStream(Audio::Mixer::kSpeechSoundType, &_soundHandle[sampleId], _audioStream[sampleId], sampleId, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO); + } } } -MusicPlayer::~MusicPlayer() { - killMidi(); +void PrinceEngine::stopSample(uint16 sampleId) { + _mixer->stopID(sampleId); } -void MusicPlayer::killMidi() { - Audio::MidiPlayer::stop(); +void PrinceEngine::stopAllSamples() { + _mixer->stopAll(); +} - free(_data); - _data = nullptr; +void PrinceEngine::freeSample(uint16 sampleId) { + stopSample(sampleId); + if (_audioStream[sampleId] != nullptr) { + delete _audioStream[sampleId]; + _audioStream[sampleId] = nullptr; + } } -void MusicPlayer::loadMidi(const char *name) { - Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(name); - if (!stream) { - debug("Can't load midi stream %s", name); - return; +void PrinceEngine::freeAllSamples() { + for (int sampleId = 0; sampleId < kMaxSamples; sampleId++) { + freeSample(sampleId); } +} - // Stop any currently playing MIDI file - killMidi(); +bool PrinceEngine::loadSample(uint32 sampleSlot, const Common::String &streamName) { + // FIXME: This is just a workaround streamName is a path + // SOUND\\SCIERKA1.WAV for now only last path component is used + Common::String normalizedPath = lastPathComponent(streamName, '\\'); - // Read in the data for the file - _dataSize = stream->size(); - _data = (byte *)malloc(_dataSize); - stream->read(_data, _dataSize); + // WALKAROUND: Wrong name in script, not existing sound in data files + if (!normalizedPath.compareTo("9997BEKA.WAV")) { + return 0; + } - delete stream; + debugEngine("loadSample slot %d, name %s", sampleSlot, normalizedPath.c_str()); - // Start playing the music - sndMidiStart(); + freeSample(sampleSlot); + Common::SeekableReadStream *sampleStream = SearchMan.createReadStreamForMember(normalizedPath); + if (sampleStream == nullptr) { + delete sampleStream; + error("Can't load sample %s to slot %d", normalizedPath.c_str(), sampleSlot); + } + _audioStream[sampleSlot] = Audio::makeWAVStream(sampleStream, DisposeAfterUse::NO); + delete sampleStream; + return true; } -void MusicPlayer::sndMidiStart() { - _isGM = true; +bool PrinceEngine::loadVoice(uint32 slot, uint32 sampleSlot, const Common::String &streamName) { + if (getFeatures() & GF_NOVOICES) + return false; - MidiParser *parser = MidiParser::createParser_SMF(); - if (parser->loadMusic(_data, _dataSize)) { - parser->setTrack(0); - parser->setMidiDriver(this); - parser->setTimerRate(_driver->getBaseTempo()); - parser->property(MidiParser::mpCenterPitchWheelOnUnload, 1); + debugEngine("Loading wav %s slot %d", streamName.c_str(), slot); - _parser = parser; + if (slot >= kMaxTexts) { + error("Text slot bigger than MAXTEXTS %d", kMaxTexts - 1); + return false; + } - syncVolume(); + freeSample(sampleSlot); + Common::SeekableReadStream *sampleStream = SearchMan.createReadStreamForMember(streamName); + if (sampleStream == nullptr) { + warning("loadVoice: Can't open %s", streamName.c_str()); + return false; + } - // Al the tracks are supposed to loop - _isLooping = true; - _isPlaying = true; + uint32 id = sampleStream->readUint32LE(); + if (id != MKTAG('F', 'F', 'I', 'R')) { + error("It's not RIFF file %s", streamName.c_str()); + return false; } -} -void MusicPlayer::send(uint32 b) { - if ((b & 0xF0) == 0xC0 && !_isGM && !_nativeMT32) { - b = (b & 0xFFFF00FF) | MidiDriver::_mt32ToGm[(b >> 8) & 0xFF] << 8; + sampleStream->skip(0x20); + id = sampleStream->readUint32LE(); + if (id != MKTAG('a', 't', 'a', 'd')) { + error("No data section in %s id %04x", streamName.c_str(), id); + return false; } - Audio::MidiPlayer::send(b); + id = sampleStream->readUint32LE(); + debugEngine("SetVoice slot %d time %04x", slot, id); + id <<= 3; + id /= 22050; + id += 2; + + _textSlots[slot]._time = id; + if (!slot) { + _mainHero->_talkTime = id; + } else if (slot == 1) { + _secondHero->_talkTime = id; + } + + debugEngine("SetVoice slot %d time %04x", slot, id); + sampleStream->seek(SEEK_SET); + _audioStream[sampleSlot] = Audio::makeWAVStream(sampleStream, DisposeAfterUse::NO); + delete sampleStream; + return true; } -void MusicPlayer::sendToChannel(byte channel, uint32 b) { - if (!_channelsTable[channel]) { - _channelsTable[channel] = (channel == 9) ? _driver->getPercussionChannel() : _driver->allocateChannel(); - // If a new channel is allocated during the playback, make sure - // its volume is correctly initialized. - if (_channelsTable[channel]) - _channelsTable[channel]->volume(_channelsVolume[channel] * _masterVolume / 255); +void PrinceEngine::setVoice(uint16 slot, uint32 sampleSlot, uint16 flag) { + Common::String sampleName; + uint32 currentString = _interpreter->getCurrentString(); + + if (currentString >= 80000) { + uint32 nr = currentString - 80000; + sampleName = Common::String::format("%02d0%02d-%02d.WAV", nr / 100, nr % 100, flag); + } else if (currentString >= 70000) { + sampleName = Common::String::format("inv%02d-01.WAV", currentString - 70000); + } else if (currentString >= 60000) { + sampleName = Common::String::format("M%04d-%02d.WAV", currentString - 60000, flag); + } else if (currentString >= 2000) { + return; + } else if (flag >= 100) { + sampleName = Common::String::format("%03d-%03d.WAV", currentString, flag); + } else { + sampleName = Common::String::format("%03d-%02d.WAV", currentString, flag); } - if (_channelsTable[channel]) - _channelsTable[channel]->send(b); + loadVoice(slot, sampleSlot, sampleName); } } // End of namespace Prince diff --git a/engines/prince/walk.cpp b/engines/prince/walk.cpp new file mode 100644 index 0000000000..0e81c36f3f --- /dev/null +++ b/engines/prince/walk.cpp @@ -0,0 +1,1610 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can 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 "prince/prince.h" +#include "prince/hero.h" +#include "prince/script.h" + +namespace Prince { + +void PrinceEngine::walkTo() { + if (_mainHero->_visible) { + _mainHero->freeHeroAnim(); + _mainHero->freeOldMove(); + _interpreter->storeNewPC(_script->_scriptInfo.usdCode); + int destX, destY; + if (_optionsMob != -1) { + destX = _mobList[_optionsMob]._examPosition.x; + destY = _mobList[_optionsMob]._examPosition.y; + _mainHero->_destDirection = _mobList[_optionsMob]._examDirection; + } else { + Common::Point mousePos = _system->getEventManager()->getMousePos(); + destX = mousePos.x + _picWindowX; + destY = mousePos.y + _picWindowY; + _mainHero->_destDirection = 0; + } + _mainHero->_coords = makePath(kMainHero, _mainHero->_middleX, _mainHero->_middleY, destX, destY); + if (_mainHero->_coords != nullptr) { + _mainHero->_currCoords = _mainHero->_coords; + _mainHero->_dirTab = _directionTable; + _mainHero->_currDirTab = _directionTable; + _directionTable = nullptr; + _mainHero->_state = Hero::kHeroStateMove; + moveShandria(); + } + } +} + +void PrinceEngine::moveRunHero(int heroId, int x, int y, int dir, bool runHeroFlag) { + Hero *hero = nullptr; + if (!heroId) { + hero = _mainHero; + } else if (heroId == 1) { + hero = _secondHero; + } + + if (hero != nullptr) { + if (dir) { + hero->_destDirection = dir; + } + if (x || y) { + hero->freeOldMove(); + hero->_coords = makePath(heroId, hero->_middleX, hero->_middleY, x, y); + if (hero->_coords != nullptr) { + hero->_currCoords = hero->_coords; + hero->_dirTab = _directionTable; + hero->_currDirTab = _directionTable; + _directionTable = nullptr; + if (runHeroFlag) { + hero->_state = Hero::kHeroStateRun; + } else { + hero->_state = Hero::kHeroStateMove; + } + if (heroId == kMainHero && _mouseFlag) { + moveShandria(); + } + } + } else { + hero->freeOldMove(); + hero->_state = Hero::kHeroStateTurn; + } + hero->freeHeroAnim(); + hero->_visible = 1; + } +} + +// Modified version of Graphics::drawLine() to allow breaking the loop and return value +int PrinceEngine::drawLine(int x0, int y0, int x1, int y1, int (*plotProc)(int, int, void *), void *data) { + // Bresenham's line algorithm, as described by Wikipedia + const bool steep = ABS(y1 - y0) > ABS(x1 - x0); + + if (steep) { + SWAP(x0, y0); + SWAP(x1, y1); + } + + const int delta_x = ABS(x1 - x0); + const int delta_y = ABS(y1 - y0); + const int delta_err = delta_y; + int x = x0; + int y = y0; + int err = 0; + + const int x_step = (x0 < x1) ? 1 : -1; + const int y_step = (y0 < y1) ? 1 : -1; + + int stopFlag = 0; + if (steep) + stopFlag = (*plotProc)(y, x, data); + else + stopFlag = (*plotProc)(x, y, data); + + while (x != x1 && !stopFlag) { + x += x_step; + err += delta_err; + if (2 * err > delta_x) { + y += y_step; + err -= delta_x; + } + if (steep) + stopFlag = (*plotProc)(y, x, data); + else + stopFlag = (*plotProc)(x, y, data); + } + return stopFlag; +} + +int PrinceEngine::getPixelAddr(byte *pathBitmap, int x, int y) { + int mask = 128 >> (x & 7); + byte value = pathBitmap[x / 8 + y * 80]; + return (mask & value); +} + +void PrinceEngine::findPoint(int x, int y) { + _fpX = x; + _fpY = y; + + if (getPixelAddr(_roomPathBitmap, x, y)) { + return; + } + + int fpL = x; + int fpU = y; + int fpR = x; + int fpD = y; + + while (1) { + if (fpD != kMaxPicHeight) { + if (getPixelAddr(_roomPathBitmap, x, fpD)) { + _fpX = x; + _fpY = fpD; + break; + } + fpD++; + } + if (fpU) { + if (getPixelAddr(_roomPathBitmap, x, fpU)) { + _fpX = x; + _fpY = fpU; + break; + } + fpU--; + } + if (fpL) { + if (getPixelAddr(_roomPathBitmap, fpL, y)) { + _fpX = fpL; + _fpY = y; + break; + } + fpL--; + } + if (fpR != _sceneWidth) { + if (getPixelAddr(_roomPathBitmap, fpR, y)) { + _fpX = fpR; + _fpY = y; + break; + } + fpR++; + } + if (!fpU && (fpD == kMaxPicHeight)) { + if (!fpL && (fpR == _sceneWidth)) { + break; + } + } + } +} + +Direction PrinceEngine::makeDirection(int x1, int y1, int x2, int y2) { + if (x1 != x2) { + if (y1 != y2) { + if (x1 > x2) { + if (y1 > y2) { + if (x1 - x2 >= y1 - y2) { + return kDirLU; + } else { + return kDirUL; + } + } else { + if (x1 - x2 >= y2 - y1) { + return kDirLD; + } else { + return kDirDL; + } + } + } else { + if (y1 > y2) { + if (x2 - x1 >= y1 - y2) { + return kDirRU; + } else { + return kDirUR; + } + } else { + if (x2 - x1 >= y2 - y1) { + return kDirRD; + } else { + return kDirDR; + } + } + } + } else { + if (x1 >= x2) { + return kDirL; + } else { + return kDirR; + } + } + } else { + if (y1 >= y2) { + return kDirU; + } else { + return kDirD; + } + } +} + +void PrinceEngine::specialPlot(int x, int y) { + if (_coords < _coordsBufEnd) { + WRITE_LE_UINT16(_coords, x); + _coords += 2; + WRITE_LE_UINT16(_coords, y); + _coords += 2; + specialPlot2(x, y); + } +} + +void PrinceEngine::specialPlot2(int x, int y) { + int mask = 128 >> (x & 7); + _roomPathBitmapTemp[x / 8 + y * 80] |= mask; +} + +void PrinceEngine::specialPlotInside(int x, int y) { + if (_coords < _coordsBufEnd) { + WRITE_LE_UINT16(_coords, x); + _coords += 2; + WRITE_LE_UINT16(_coords, y); + _coords += 2; + } +} + +int PrinceEngine::plotTraceLine(int x, int y, void *data) { + PrinceEngine *traceLine = (PrinceEngine *)data; + if (!traceLine->_traceLineFirstPointFlag) { + if (!traceLine->getPixelAddr(traceLine->_roomPathBitmapTemp, x, y)) { + if (traceLine->getPixelAddr(traceLine->_roomPathBitmap, x, y)) { + traceLine->specialPlotInside(x, y); + traceLine->_traceLineLen++; + return 0; + } else { + return -1; + } + } else { + return 1; + } + } else { + traceLine->_traceLineFirstPointFlag = false; + return 0; + } +} + +int PrinceEngine::leftDownDir() { + if (!checkLeftDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + return -1; +} + +int PrinceEngine::leftDir() { + if (!checkLeftDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + return -1; +} + +int PrinceEngine::leftUpDir() { + if (!checkLeftUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + return -1; +} + +int PrinceEngine::rightDownDir() { + if (!checkRightDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + return -1; +} + +int PrinceEngine::rightDir() { + if (!checkRightDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + return -1; +} + +int PrinceEngine::rightUpDir() { + if (!checkRightUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + return -1; +} + +int PrinceEngine::upLeftDir() { + if (!checkLeftUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + return -1; +} + +int PrinceEngine::upDir() { + if (!checkUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + return -1; +} + +int PrinceEngine::upRightDir() { + if (!checkRightUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + return -1; +} + +int PrinceEngine::downLeftDir() { + if (!checkLeftDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + return -1; +} + +int PrinceEngine::downDir() { + if (!checkDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + return -1; +} + +int PrinceEngine::downRightDir() { + if (!checkRightDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + return -1; +} + +int PrinceEngine::cpe() { + if ((*(_checkBitmap - kPBW) & _checkMask)) { + if ((*(_checkBitmap + kPBW) & _checkMask)) { + int value; + switch (_checkMask) { + case 128: + value = READ_LE_UINT16(_checkBitmap - 1); + value &= 0x4001; + if (value != 0x4001) { + return 0; + } + break; + case 64: + value = *_checkBitmap; + value &= 0xA0; + if (value != 0xA0) { + return 0; + } + break; + case 32: + value = *_checkBitmap; + value &= 0x50; + if (value != 0x50) { + return 0; + } + break; + case 16: + value = *_checkBitmap; + value &= 0x28; + if (value != 0x28) { + return 0; + } + break; + case 8: + value = *_checkBitmap; + value &= 0x14; + if (value != 0x14) { + return 0; + } + break; + case 4: + value = *_checkBitmap; + value &= 0xA; + if (value != 0xA) { + return 0; + } + break; + case 2: + value = *_checkBitmap; + value &= 0x5; + if (value != 0x5) { + return 0; + } + break; + case 1: + value = READ_LE_UINT16(_checkBitmap); + value &= 0x8002; + if (value != 0x8002) { + return 0; + } + break; + default: + error("Wrong _checkMask value - cpe()"); + break; + } + _checkX = _rembX; + _checkY = _rembY; + _checkBitmapTemp = _rembBitmapTemp; + _checkBitmap = _rembBitmap; + _checkMask = _rembMask; + return -1; + } + return 0; + } + return 0; +} + +int PrinceEngine::checkLeftDownDir() { + if (_checkX && _checkY != (kMaxPicHeight / 2 - 1)) { + int tempMask = _checkMask; + if (tempMask != 128) { + tempMask <<= 1; + if ((*(_checkBitmap + kPBW) & tempMask)) { + if (!(*(_checkBitmapTemp + kPBW) & tempMask)) { + _checkBitmap += kPBW; + _checkBitmapTemp += kPBW; + _checkMask = tempMask; + } else { + return 1; + } + } else { + return -1; + } + } else { + if ((*(_checkBitmap + kPBW - 1) & 1)) { + if (!(*(_checkBitmapTemp + kPBW - 1) & 1)) { + _checkBitmap += (kPBW - 1); + _checkBitmapTemp += (kPBW - 1); + _checkMask = 1; + } else { + return 1; + } + } else { + return -1; + } + } + _checkX--; + _checkY++; + return cpe(); + } else { + return -1; + } +} + +int PrinceEngine::checkLeftDir() { + if (_checkX) { + int tempMask = _checkMask; + if (tempMask != 128) { + tempMask <<= 1; + if ((*(_checkBitmap) & tempMask)) { + if (!(*(_checkBitmapTemp) & tempMask)) { + _checkMask = tempMask; + } else { + return 1; + } + } else { + return -1; + } + } else { + if ((*(_checkBitmap - 1) & 1)) { + if (!(*(_checkBitmapTemp - 1) & 1)) { + _checkBitmap--; + _checkBitmapTemp--; + _checkMask = 1; + } else { + return 1; + } + } else { + return -1; + } + } + _checkX--; + return cpe(); + } else { + return -1; + } +} + +int PrinceEngine::checkDownDir() { + if (_checkY != (kMaxPicHeight / 2 - 1)) { + if ((*(_checkBitmap + kPBW) & _checkMask)) { + if (!(*(_checkBitmapTemp + kPBW) & _checkMask)) { + _checkBitmap += kPBW; + _checkBitmapTemp += kPBW; + _checkY++; + return cpe(); + } else { + return 1; + } + } else { + return -1; + } + } else { + return -1; + } +} + +int PrinceEngine::checkUpDir() { + if (_checkY) { + if ((*(_checkBitmap - kPBW) & _checkMask)) { + if (!(*(_checkBitmapTemp - kPBW) & _checkMask)) { + _checkBitmap -= kPBW; + _checkBitmapTemp -= kPBW; + _checkY--; + return cpe(); + } else { + return 1; + } + } else { + return -1; + } + } else { + return -1; + } +} + +int PrinceEngine::checkRightDir() { + if (_checkX != (kMaxPicWidth / 2 - 1)) { + int tempMask = _checkMask; + if (tempMask != 1) { + tempMask >>= 1; + if ((*(_checkBitmap) & tempMask)) { + if (!(*(_checkBitmapTemp) & tempMask)) { + _checkMask = tempMask; + } else { + return 1; + } + } else { + return -1; + } + } else { + if ((*(_checkBitmap + 1) & 128)) { + if (!(*(_checkBitmapTemp + 1) & 128)) { + _checkBitmap++; + _checkBitmapTemp++; + _checkMask = 128; + } else { + return 1; + } + } else { + return -1; + } + } + _checkX++; + return cpe(); + } else { + return -1; + } +} + +int PrinceEngine::checkLeftUpDir() { + if (_checkX && _checkY) { + int tempMask = _checkMask; + if (tempMask != 128) { + tempMask <<= 1; + if ((*(_checkBitmap - kPBW) & tempMask)) { + if (!(*(_checkBitmapTemp - kPBW) & tempMask)) { + _checkBitmap -= kPBW; + _checkBitmapTemp -= kPBW; + _checkMask = tempMask; + } else { + return 1; + } + } else { + return -1; + } + } else { + if ((*(_checkBitmap - (kPBW + 1)) & 1)) { + if (!(*(_checkBitmapTemp - (kPBW + 1)) & 1)) { + _checkBitmap -= (kPBW + 1); + _checkBitmapTemp -= (kPBW + 1); + _checkMask = 1; + } else { + return 1; + } + } else { + return -1; + } + } + _checkX--; + _checkY--; + return cpe(); + } else { + return -1; + } +} + +int PrinceEngine::checkRightDownDir() { + if (_checkX != (kMaxPicWidth / 2 - 1) && _checkY != (kMaxPicHeight / 2 - 1)) { + int tempMask = _checkMask; + if (tempMask != 1) { + tempMask >>= 1; + if ((*(_checkBitmap + kPBW) & tempMask)) { + if (!(*(_checkBitmapTemp + kPBW) & tempMask)) { + _checkBitmap += kPBW; + _checkBitmapTemp += kPBW; + _checkMask = tempMask; + } else { + return 1; + } + } else { + return -1; + } + } else { + if ((*(_checkBitmap + kPBW + 1) & 128)) { + if (!(*(_checkBitmapTemp + kPBW + 1) & 128)) { + _checkBitmap += kPBW + 1; + _checkBitmapTemp += kPBW + 1; + _checkMask = 128; + } else { + return 1; + } + } else { + return -1; + } + } + _checkX++; + _checkY++; + return cpe(); + } else { + return -1; + } +} + +int PrinceEngine::checkRightUpDir() { + if (_checkX != (kMaxPicWidth / 2 - 1) && _checkY) { + int tempMask = _checkMask; + if (tempMask != 1) { + tempMask >>= 1; + if ((*(_checkBitmap - kPBW) & tempMask)) { + if (!(*(_checkBitmapTemp - kPBW) & tempMask)) { + _checkBitmap -= kPBW; + _checkBitmapTemp -= kPBW; + _checkMask = tempMask; + } else { + return 1; + } + } else { + return -1; + } + } else { + if ((*(_checkBitmap - kPBW + 1) & 128)) { + if (!(*(_checkBitmapTemp - kPBW + 1) & 128)) { + _checkBitmap -= (kPBW - 1); + _checkBitmapTemp -= (kPBW - 1); + _checkMask = 128; + } else { + return 1; + } + } else { + return -1; + } + } + _checkX++; + _checkY--; + return cpe(); + } else { + return -1; + } +} + +bool PrinceEngine::tracePath(int x1, int y1, int x2, int y2) { + for (uint i = 0; i < kPathBitmapLen; i++) { + _roomPathBitmapTemp[i] = 0; + } + if (x1 != x2 || y1 != y2) { + if (getPixelAddr(_roomPathBitmap, x1, y1)) { + if (getPixelAddr(_roomPathBitmap, x2, y2)) { + _coords = _coordsBuf; + specialPlot(x1, y1); + + int x = x1; + int y = y1; + + while (1) { + int btx = x; + int bty = y; + byte *bcad = _coords; + + _traceLineLen = 0; + _traceLineFirstPointFlag = true; + int drawLineFlag = drawLine(x, y, x2, y2, &this->plotTraceLine, this); + + if (!drawLineFlag) { + return true; + } else if (drawLineFlag == -1 && _traceLineLen >= 2) { + byte *tempCorrds = bcad; + while (tempCorrds != _coords) { + x = READ_LE_UINT16(tempCorrds); + y = READ_LE_UINT16(tempCorrds + 2); + tempCorrds += 4; + specialPlot2(x, y); + } + } else { + _coords = bcad; + x = btx; + y = bty; + } + + Direction dir = makeDirection(x, y, x2, y2); + + _rembBitmapTemp = &_roomPathBitmapTemp[x / 8 + y * 80]; + _rembBitmap = &_roomPathBitmap[x / 8 + y * 80]; + _rembMask = 128 >> (x & 7); + _rembX = x; + _rembY = y; + + _checkBitmapTemp = _rembBitmapTemp; + _checkBitmap = _rembBitmap; + _checkMask = _rembMask; + _checkX = _rembX; + _checkY = _rembY; + + int result; + switch (dir) { + case kDirLD: + result = leftDownDir(); + break; + case kDirL: + result = leftDir(); + break; + case kDirLU: + result = leftUpDir(); + break; + case kDirRD: + result = rightDownDir(); + break; + case kDirR: + result = rightDir(); + break; + case kDirRU: + result = rightUpDir(); + break; + case kDirUL: + result = upLeftDir(); + break; + case kDirU: + result = upDir(); + break; + case kDirUR: + result = upRightDir(); + break; + case kDirDL: + result = downLeftDir(); + break; + case kDirD: + result = downDir(); + break; + case kDirDR: + result = downRightDir(); + break; + default: + result = -1; + error("tracePath: wrong direction %d", dir); + break; + } + + if (result) { + byte *tempCoords = _coords; + tempCoords -= 4; + if (tempCoords > _coordsBuf) { + int tempX = READ_LE_UINT16(tempCoords); + int tempY = READ_LE_UINT16(tempCoords + 2); + if (_checkX == tempX && _checkY == tempY) { + _coords = tempCoords; + } + x = READ_LE_UINT16(tempCoords); + y = READ_LE_UINT16(tempCoords + 2); + } else { + return false; + } + } else { + x = _checkX; + y = _checkY; + } + } + return true; + } else { + error("tracePath: wrong destination point"); + } + } else { + error("tracePath: wrong start point"); + } + } else { + error("tracePath: same point"); + } +} + +void PrinceEngine::specialPlotInside2(int x, int y) { + WRITE_LE_UINT16(_coords2, x); + _coords2 += 2; + WRITE_LE_UINT16(_coords2, y); + _coords2 += 2; +} + +int PrinceEngine::plotTracePoint(int x, int y, void *data) { + PrinceEngine *tracePoint = (PrinceEngine *)data; + if (!tracePoint->_tracePointFirstPointFlag) { + if (tracePoint->getPixelAddr(tracePoint->_roomPathBitmap, x, y)) { + tracePoint->specialPlotInside2(x, y); + return 0; + } else { + return -1; + } + } else { + tracePoint->_tracePointFirstPointFlag = false; + return 0; + } +} + +void PrinceEngine::approxPath() { + byte *oldCoords; + _coords2 = _coordsBuf2; + byte *tempCoordsBuf = _coordsBuf; // first point on path + byte *tempCoords = _coords; + if (tempCoordsBuf != tempCoords) { + tempCoords -= 4; // last point on path + while (tempCoordsBuf != tempCoords) { + int x1 = READ_LE_UINT16(tempCoords); + int y1 = READ_LE_UINT16(tempCoords + 2); + int x2 = READ_LE_UINT16(tempCoordsBuf); + int y2 = READ_LE_UINT16(tempCoordsBuf + 2); + tempCoordsBuf += 4; + //TracePoint + oldCoords = _coords2; + if (_coords2 == _coordsBuf2) { + WRITE_LE_UINT16(_coords2, x1); + WRITE_LE_UINT16(_coords2 + 2, y1); + _coords2 += 4; + } else { + int testX = READ_LE_UINT16(_coords2 - 4); + int testY = READ_LE_UINT16(_coords2 - 2); + if (testX != x1 || testY != y1) { + WRITE_LE_UINT16(_coords2, x1); + WRITE_LE_UINT16(_coords2 + 2, y1); + _coords2 += 4; + } + } + _tracePointFirstPointFlag = true; + bool drawLineFlag = drawLine(x1, y1, x2, y2, &this->plotTracePoint, this); + if (!drawLineFlag) { + tempCoords = tempCoordsBuf - 4; + tempCoordsBuf = _coordsBuf; + } else { + _coords2 = oldCoords; + } + } + } +} + +void PrinceEngine::freeDirectionTable() { + if (_directionTable != nullptr) { + free(_directionTable); + _directionTable = nullptr; + } +} + +int PrinceEngine::scanDirectionsFindNext(byte *tempCoordsBuf, int xDiff, int yDiff) { + + int tempX, tempY, direction; + + tempX = Hero::kHeroDirLeft; + if (xDiff < 0) { + tempX = Hero::kHeroDirRight; + } + + tempY = Hero::kHeroDirUp; + if (yDiff < 0) { + tempY = Hero::kHeroDirDown; + } + + while (1) { + int againPointX1 = READ_LE_UINT16(tempCoordsBuf); + int againPointY1 = READ_LE_UINT16(tempCoordsBuf + 2); + tempCoordsBuf += 4; + + if (tempCoordsBuf == _coords) { + direction = tempX; + break; + } + + int dX = againPointX1 - READ_LE_UINT16(tempCoordsBuf); + int dY = againPointY1 - READ_LE_UINT16(tempCoordsBuf + 2); + + if (dX != xDiff) { + direction = tempY; + break; + } + + if (dY != yDiff) { + direction = tempX; + break; + } + } + return direction; +} + +void PrinceEngine::scanDirections() { + freeDirectionTable(); + byte *tempCoordsBuf = _coordsBuf; + if (tempCoordsBuf != _coords) { + int size = (_coords - tempCoordsBuf) / 4 + 1; // number of coord points plus one for end marker + _directionTable = (byte *)malloc(size); + byte *tempDirTab = _directionTable; + int direction = -1; + int lastDirection = -1; + + while (1) { + int x1 = READ_LE_UINT16(tempCoordsBuf); + int y1 = READ_LE_UINT16(tempCoordsBuf + 2); + tempCoordsBuf += 4; + if (tempCoordsBuf == _coords) { + break; + } + int x2 = READ_LE_UINT16(tempCoordsBuf); + int y2 = READ_LE_UINT16(tempCoordsBuf + 2); + + int xDiff = x1 - x2; + int yDiff = y1 - y2; + + if (xDiff) { + if (yDiff) { + if (lastDirection != -1) { + direction = lastDirection; + if (direction == Hero::kHeroDirLeft) { + if (xDiff < 0) { + direction = scanDirectionsFindNext(tempCoordsBuf, xDiff, yDiff); + } + } else if (direction == Hero::kHeroDirRight) { + if (xDiff >= 0) { + direction = scanDirectionsFindNext(tempCoordsBuf, xDiff, yDiff); + } + } else if (direction == Hero::kHeroDirUp) { + if (yDiff < 0) { + direction = scanDirectionsFindNext(tempCoordsBuf, xDiff, yDiff); + } + } else { + if (yDiff >= 0) { + direction = scanDirectionsFindNext(tempCoordsBuf, xDiff, yDiff); + } + } + } else { + direction = scanDirectionsFindNext(tempCoordsBuf, xDiff, yDiff); + } + } else { + direction = Hero::kHeroDirLeft; + if (xDiff < 0) { + direction = Hero::kHeroDirRight; + } + } + } else { + if (yDiff) { + direction = Hero::kHeroDirUp; + if (yDiff < 0) { + direction = Hero::kHeroDirDown; + } + } else { + direction = lastDirection; + } + } + lastDirection = direction; + *tempDirTab = direction; + tempDirTab++; + } + *tempDirTab = *(tempDirTab - 1); + tempDirTab++; + *tempDirTab = 0; + } +} + +void PrinceEngine::moveShandria() { + int shanLen1 = _shanLen; + if (_flags->getFlagValue(Flags::SHANDOG)) { + _secondHero->freeHeroAnim(); + _secondHero->freeOldMove(); + byte *shanCoords = _mainHero->_currCoords + shanLen1 * 4 - 4; + int shanX = READ_LE_UINT16(shanCoords - 4); + int shanY = READ_LE_UINT16(shanCoords - 2); + int xDiff = shanX - _secondHero->_middleX; + if (xDiff < 0) { + xDiff *= -1; + } + int yDiff = shanY - _secondHero->_middleY; + if (yDiff < 0) { + yDiff *= -1; + } + shanCoords -= 4; + if (shanCoords != _mainHero->_currCoords) { + yDiff *= 1.5; + int shanDis = xDiff * xDiff + yDiff * yDiff; + if (shanDis >= kMinDistance) { + while (1) { + shanCoords -= 4; + if (shanCoords == _mainHero->_currCoords) { + break; + } + int x = READ_LE_UINT16(shanCoords); + int y = READ_LE_UINT16(shanCoords + 2); + int pointDiffX = x - shanX; + if (pointDiffX < 0) { + pointDiffX *= -1; + } + int pointDiffY = y - shanY; + if (pointDiffY < 0) { + pointDiffY *= -1; + } + pointDiffY *= 1.5; + int distance = pointDiffX * pointDiffX + pointDiffY * pointDiffY; + if (distance >= kMinDistance) { + break; + } + } + int pathSizeDiff = (shanCoords - _mainHero->_currCoords) / 4; + int destDir = *(_mainHero->_currDirTab + pathSizeDiff); + _secondHero->_destDirection = destDir; + int destX = READ_LE_UINT16(shanCoords); + int destY = READ_LE_UINT16(shanCoords + 2); + _secondHero->_coords = makePath(kSecondHero, _secondHero->_middleX, _secondHero->_middleY, destX, destY); + if (_secondHero->_coords != nullptr) { + _secondHero->_currCoords = _secondHero->_coords; + int delay = shanLen1 - _shanLen; + if (delay < 6) { + delay = 6; + } + _secondHero->_moveDelay = delay / 2; + _secondHero->_state = Hero::kHeroStateDelayMove; + _secondHero->_dirTab = _directionTable; + _secondHero->_currDirTab = _directionTable; + _directionTable = nullptr; + } + } + } + } +} + +byte *PrinceEngine::makePath(int heroId, int currX, int currY, int destX, int destY) { + int realDestX = destX; + int realDestY = destY; + _flags->setFlagValue(Flags::MOVEDESTX, destX); + _flags->setFlagValue(Flags::MOVEDESTY, destY); + + int x1 = currX / 2; + int y1 = currY / 2; + int x2 = destX / 2; + int y2 = destY / 2; + + if ((x1 != x2) || (y1 != y2)) { + findPoint(x1, y1); + if (!getPixelAddr(_roomPathBitmap, _fpX, _fpY)) { + return nullptr; + } + if ((x1 != _fpX) || (y1 != _fpY)) { + x1 = _fpX; + y1 = _fpY; + } + findPoint(x2, y2); + if (!getPixelAddr(_roomPathBitmap, _fpX, _fpY)) { + return nullptr; + } + if ((x2 != _fpX) || (y2 != _fpY)) { + x2 = _fpX; + y2 = _fpY; + if (!_flags->getFlagValue(Flags::EXACTMOVE)) { + realDestX = x2 * 2; + realDestY = y2 * 2; + _flags->setFlagValue(Flags::MOVEDESTX, realDestX); + _flags->setFlagValue(Flags::MOVEDESTY, realDestY); + } else { + return nullptr; + } + } + + if ((x1 == x2) && (y1 == y2)) { + if (!heroId) { + _mainHero->freeOldMove(); + _mainHero->_state = Hero::kHeroStateTurn; + } else if (heroId == 1) { + _secondHero->freeOldMove(); + _secondHero->_state = Hero::kHeroStateTurn; + } + return nullptr; + } + + int pathLen1 = 0; + int pathLen2 = 0; + int stX = x1; + int stY = y1; + int sizeCoords2 = 0; + + if (tracePath(x1, y1, x2, y2)) { + allocCoords2(); + approxPath(); + sizeCoords2 = _coords2 - _coordsBuf2; + for (int i = 0; i < sizeCoords2; i++) { + _coordsBuf[i] = _coordsBuf2[i]; + } + _coords = _coordsBuf + sizeCoords2; + approxPath(); + _coordsBuf3 = _coordsBuf2; + _coordsBuf2 = nullptr; + _coords3 = _coords2; + _coords2 = nullptr; + pathLen1 = _coords3 - _coordsBuf3; + } + if (tracePath(x2, y2, x1, y1)) { + allocCoords2(); + approxPath(); + sizeCoords2 = _coords2 - _coordsBuf2; + for (int i = 0; i < sizeCoords2; i++) { + _coordsBuf[i] = _coordsBuf2[i]; + } + _coords = _coordsBuf + sizeCoords2; + approxPath(); + pathLen2 = _coords2 - _coordsBuf2; + } + + byte *chosenCoordsBuf = _coordsBuf2; + byte *choosenCoords = _coords2; + int choosenLength = pathLen1; + if (pathLen1 < pathLen2) { + chosenCoordsBuf = _coordsBuf3; + choosenCoords = _coords3; + choosenLength = pathLen2; + } + + if (choosenLength) { + if (chosenCoordsBuf != nullptr) { + int tempXBegin = READ_LE_UINT16(chosenCoordsBuf); + int tempYBegin = READ_LE_UINT16(chosenCoordsBuf + 2); + if (stX != tempXBegin || stY != tempYBegin) { + SWAP(chosenCoordsBuf, choosenCoords); + chosenCoordsBuf -= 4; + byte *tempCoordsBuf = _coordsBuf; + while (1) { + int cord = READ_LE_UINT32(chosenCoordsBuf); + WRITE_LE_UINT32(tempCoordsBuf, cord); + tempCoordsBuf += 4; + if (chosenCoordsBuf == choosenCoords) { + break; + } + chosenCoordsBuf -= 4; + } + _coords = tempCoordsBuf; + } else { + int sizeChoosen = choosenCoords - chosenCoordsBuf; + for (int i = 0; i < sizeChoosen; i++) { + _coordsBuf[i] = chosenCoordsBuf[i]; + } + _coords = _coordsBuf + sizeChoosen; + } + WRITE_LE_UINT32(_coords, 0xFFFFFFFF); + freeCoords2(); + freeCoords3(); + scanDirections(); + + byte *tempCoordsBuf = _coordsBuf; + byte *tempCoords = _coords; + byte *newCoords; + if (tempCoordsBuf != tempCoords) { + int normCoordsSize = _coords - _coordsBuf + 4; + newCoords = (byte *)malloc(normCoordsSize); + byte *newCoordsBegin = newCoords; + while (tempCoordsBuf != tempCoords) { + int newValueX = READ_LE_UINT16(tempCoordsBuf); + WRITE_LE_UINT16(newCoords, newValueX * 2); + newCoords += 2; + int newValueY = READ_LE_UINT16(tempCoordsBuf + 2); + WRITE_LE_UINT16(newCoords, newValueY * 2); + newCoords += 2; + tempCoordsBuf += 4; + } + WRITE_LE_UINT16(newCoords - 4, realDestX); + WRITE_LE_UINT16(newCoords - 2, realDestY); + WRITE_LE_UINT32(newCoords, 0xFFFFFFFF); + newCoords += 4; + _shanLen = (newCoords - newCoordsBegin); + _shanLen /= 4; + return newCoordsBegin; + } + } + } + _coords = _coordsBuf; + freeCoords2(); + freeCoords3(); + return nullptr; + } else { + if (!heroId) { + _mainHero->freeOldMove(); + _mainHero->_state = Hero::kHeroStateTurn; + } else if (heroId == 1) { + _secondHero->freeOldMove(); + _secondHero->_state = Hero::kHeroStateTurn; + } + return nullptr; + } +} + +void PrinceEngine::allocCoords2() { + if (_coordsBuf2 == nullptr) { + _coordsBuf2 = (byte *)malloc(kTracePts * 4); + _coords2 = _coordsBuf2; + } +} + +void PrinceEngine::freeCoords2() { + if (_coordsBuf2 != nullptr) { + free(_coordsBuf2); + _coordsBuf2 = nullptr; + _coords2 = nullptr; + } +} + +void PrinceEngine::freeCoords3() { + if (_coordsBuf3 != nullptr) { + free(_coordsBuf3); + _coordsBuf3 = nullptr; + _coords3 = nullptr; + } +} + +} // End of namespace Prince diff --git a/engines/queen/detection.cpp b/engines/queen/detection.cpp index aed8b7dcb1..b9a7c5dff6 100644 --- a/engines/queen/detection.cpp +++ b/engines/queen/detection.cpp @@ -422,6 +422,45 @@ static const QueenGameDescription gameDescriptions[] = { }, #endif + // GoG.com Release - German + { + { + "queen", + "Talkie", + AD_ENTRY1s("queen.1", "28f78dbec7e20f603a10c2f8ea889a5c", 108738717), + Common::DE_DEU, + Common::kPlatformDOS, + ADGF_NO_FLAGS, + GUIO1(GAMEOPTION_ALT_INTRO) + }, + }, + + // GoG.com Release - French + { + { + "queen", + "Talkie", + AD_ENTRY1s("queen.1", "67e3020f8a35e1df7b1c753b5aaa71e1", 97382620), + Common::FR_FRA, + Common::kPlatformDOS, + ADGF_NO_FLAGS, + GUIO1(GAMEOPTION_ALT_INTRO) + }, + }, + + // GoG.com Release - Italian + { + { + "queen", + "Talkie", + AD_ENTRY1s("queen.1", "2f72b715ed753cf905a37cdcc7ea611e", 98327801), + Common::IT_ITA, + Common::kPlatformDOS, + ADGF_NO_FLAGS, + GUIO1(GAMEOPTION_ALT_INTRO) + }, + }, + { AD_TABLE_END_MARKER } }; @@ -447,7 +486,7 @@ public: virtual int getMaximumSaveSlot() const { return 99; } virtual void removeSaveState(const char *target, int slot) const; - const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const; + ADDetectedGame fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const override; }; bool QueenMetaEngine::hasFeature(MetaEngineFeature f) const { @@ -457,7 +496,7 @@ bool QueenMetaEngine::hasFeature(MetaEngineFeature f) const { (f == kSupportsDeleteSave); } -const ADGameDescription *QueenMetaEngine::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const { +ADDetectedGame QueenMetaEngine::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const { static ADGameDescription desc; // Iterate over all files in the given directory @@ -492,11 +531,13 @@ const ADGameDescription *QueenMetaEngine::fallbackDetect(const FileMap &allFiles desc.extra = "Talkie"; desc.guiOptions = GAMEOPTION_ALT_INTRO; } - return (const ADGameDescription *)&desc; + + return ADDetectedGame(&desc); } } } - return 0; + + return ADDetectedGame(); } SaveStateList QueenMetaEngine::listSaves(const char *target) const { diff --git a/engines/queen/music.cpp b/engines/queen/music.cpp index 9f74aab915..c65f2de5ef 100644 --- a/engines/queen/music.cpp +++ b/engines/queen/music.cpp @@ -102,10 +102,7 @@ MidiMusic::~MidiMusic() { } void MidiMusic::setVolume(int volume) { - if (volume < 0) - volume = 0; - else if (volume > 255) - volume = 255; + volume = CLIP(volume, 0, 255); if (_masterVolume == volume) return; diff --git a/engines/saga/detection.cpp b/engines/saga/detection.cpp index 6fe4277c27..82c29d3389 100644 --- a/engines/saga/detection.cpp +++ b/engines/saga/detection.cpp @@ -105,7 +105,7 @@ public: _singleId = "saga"; } - virtual GameDescriptor findGame(const char *gameId) const { + PlainGameDescriptor findGame(const char *gameId) const override { return Engines::findGameID(gameId, _gameIds, obsoleteGameIDsTable); } @@ -256,7 +256,11 @@ SaveStateDescriptor SagaMetaEngine::querySaveMetaInfos(const char *target, int s } if (version >= 6) { - Graphics::Surface *const thumbnail = Graphics::loadThumbnail(*in); + Graphics::Surface *thumbnail; + if (!Graphics::loadThumbnail(*in, thumbnail)) { + delete in; + return SaveStateDescriptor(); + } desc.setThumbnail(thumbnail); uint32 saveDate = in->readUint32BE(); diff --git a/engines/saga/events.cpp b/engines/saga/events.cpp index b7c3fa4d6e..912e62d492 100644 --- a/engines/saga/events.cpp +++ b/engines/saga/events.cpp @@ -176,7 +176,7 @@ int Events::handleContinuous(Event *event) { rect.setWidth(w); rect.setHeight(h); - _vm->_render->getBackGroundSurface()->transitionDissolve( maskBuffer, rect, 1, event_pc); + _vm->_render->getBackGroundSurface()->transitionDissolve(maskBuffer, rect, 1, event_pc); _vm->_render->setFullRefresh(true); break; default: diff --git a/engines/saga/interface.cpp b/engines/saga/interface.cpp index e6b196c4cd..250d2914b7 100644 --- a/engines/saga/interface.cpp +++ b/engines/saga/interface.cpp @@ -2804,8 +2804,8 @@ void Interface::mapPanelDrawCrossHair() { if (screen.contains(mapPosition)) { _vm->_sprite->draw(_vm->_sprite->_mainSprites, - _mapPanelCrossHairState? RID_ITE_SPR_CROSSHAIR : RID_ITE_SPR_CROSSHAIR + 1, - mapPosition, 256); + _mapPanelCrossHairState ? RID_ITE_SPR_CROSSHAIR : RID_ITE_SPR_CROSSHAIR + 1, + mapPosition, 256); } } diff --git a/engines/saga/isomap.cpp b/engines/saga/isomap.cpp index cff415868e..ae2b458461 100644 --- a/engines/saga/isomap.cpp +++ b/engines/saga/isomap.cpp @@ -304,18 +304,8 @@ void IsoMap::adjustScroll(bool jump) { maxScrollPos.y = playerPoint.y + SAGA_SCROLL_LIMIT_Y2; if (jump) { - if (_viewScroll.y < minScrollPos.y) { - _viewScroll.y = minScrollPos.y; - } - if (_viewScroll.y > maxScrollPos.y) { - _viewScroll.y = maxScrollPos.y; - } - if (_viewScroll.x < minScrollPos.x) { - _viewScroll.x = minScrollPos.x; - } - if (_viewScroll.x > maxScrollPos.x) { - _viewScroll.x = maxScrollPos.x; - } + _viewScroll.x = CLIP(_viewScroll.x, minScrollPos.x, maxScrollPos.x); + _viewScroll.y = CLIP(_viewScroll.y, minScrollPos.y, maxScrollPos.y); } else { _viewScroll.y = smoothSlide(_viewScroll.y, minScrollPos.y, maxScrollPos.y); _viewScroll.x = smoothSlide(_viewScroll.x, minScrollPos.x, maxScrollPos.x); @@ -453,7 +443,7 @@ void IsoMap::drawTiles(const Location *location) { if (uc != u2 || vc != v2) { metaTileIndex = 0; - switch ( _tileMap.edgeType) { + switch (_tileMap.edgeType) { case kEdgeTypeBlack: continue; case kEdgeTypeFill0: @@ -497,7 +487,7 @@ void IsoMap::drawTiles(const Location *location) { if (uc != u2 || vc != v2) { metaTileIndex = 0; - switch ( _tileMap.edgeType) { + switch (_tileMap.edgeType) { case kEdgeTypeBlack: continue; case kEdgeTypeFill0: @@ -750,7 +740,7 @@ void IsoMap::drawTile(uint16 tileIndex, const Point &point, const Location *loca if (location->z >= 16) { return; } else { - switch (_tilesTable[tileIndex].GetMaskRule()) { + switch (_tilesTable[tileIndex].getMaskRule()) { case kMaskRuleNever: return; case kMaskRuleAlways: @@ -1015,7 +1005,7 @@ int16 IsoMap::getTileIndex(int16 u, int16 v, int16 z) { if ((uc != mtileU) || (vc != mtileV)) { metaTileIndex = 0; - switch ( _tileMap.edgeType) { + switch (_tileMap.edgeType) { case kEdgeTypeBlack: return 0; case kEdgeTypeFill0: @@ -1088,8 +1078,8 @@ void IsoMap::testPossibleDirections(int16 u, int16 v, uint16 terraComp[8], int s #define TEST_TILE_PROLOG(offsetU, offsetV) \ tile = getTile(u + offsetU, v + offsetV , _platformHeight); \ if (tile != NULL) { \ - fgdMask = tile->GetFGDMask(); \ - bgdMask = tile->GetBGDMask(); \ + fgdMask = tile->getFGDMask(); \ + bgdMask = tile->getBGDMask(); \ mask = tile->terrainMask; #define TEST_TILE_EPILOG(index) \ @@ -1193,7 +1183,7 @@ void IsoMap::placeOnTileMap(const Location &start, Location &result, int16 dista _platformHeight = _vm->_actor->_protagonist->_location.z / 8; - memset( &_searchArray, 0, sizeof(_searchArray)); + memset(&_searchArray, 0, sizeof(_searchArray)); for (ActorDataArray::const_iterator actor = _vm->_actor->_actors.begin(); actor != _vm->_actor->_actors.end(); ++actor) { if (!actor->_inScene) continue; @@ -1248,7 +1238,7 @@ void IsoMap::placeOnTileMap(const Location &start, Location &result, int16 dista } } - pushPoint(tilePoint.u + tdir->u,tilePoint.v + tdir->v, tilePoint.cost + tdir->cost, dir); + pushPoint(tilePoint.u + tdir->u, tilePoint.v + tdir->v, tilePoint.cost + tdir->cost, dir); } } @@ -1263,35 +1253,35 @@ bool IsoMap::findNearestChasm(int16 &u0, int16 &v0, uint16 &direction) { v = v0; for (i = 1; i < 5; i++) { - if (getTile( u - i, v, 6) == NULL) { + if (getTile(u - i, v, 6) == NULL) { u0 = u - i - 1; v0 = v; direction = kDirDownLeft; return true; } - if (getTile( u, v - i, 6) == NULL) { + if (getTile(u, v - i, 6) == NULL) { u0 = u; v0 = v - i - 1; direction = kDirDownRight; return true; } - if (getTile( u - i, v - i, 6) == NULL) { + if (getTile(u - i, v - i, 6) == NULL) { u0 = u - i - 1; v0 = v - i - 1; direction = kDirDown; return true; } - if (getTile( u + i, v - i, 6) == NULL) { + if (getTile(u + i, v - i, 6) == NULL) { u0 = u + i + 1; v0 = v - i - 1; direction = kDirDownRight; return true; } - if (getTile( u - i, v + i, 6) == NULL) { + if (getTile(u - i, v + i, 6) == NULL) { u0 = u + i + 1; v0 = v - i - 1; direction = kDirLeft; @@ -1300,21 +1290,21 @@ bool IsoMap::findNearestChasm(int16 &u0, int16 &v0, uint16 &direction) { } for (i = 1; i < 5; i++) { - if (getTile( u + i, v, 6) == NULL) { + if (getTile(u + i, v, 6) == NULL) { u0 = u + i + 1; v0 = v; direction = kDirUpRight; return true; } - if (getTile( u, v + i, 6) == NULL) { + if (getTile(u, v + i, 6) == NULL) { u0 = u; v0 = v + i + 1; direction = kDirUpLeft; return true; } - if (getTile( u + i, v + i, 6) == NULL) { + if (getTile(u + i, v + i, 6) == NULL) { u0 = u + i + 1; v0 = v + i + 1; direction = kDirUp; @@ -1324,7 +1314,7 @@ bool IsoMap::findNearestChasm(int16 &u0, int16 &v0, uint16 &direction) { return false; } -void IsoMap::findDragonTilePath(ActorData* actor,const Location &start, const Location &end, uint16 initialDirection) { +void IsoMap::findDragonTilePath(ActorData* actor, const Location &start, const Location &end, uint16 initialDirection) { byte *res; int i; int16 u; @@ -1360,7 +1350,7 @@ void IsoMap::findDragonTilePath(ActorData* actor,const Location &start, const Lo _platformHeight = _vm->_actor->_protagonist->_location.z / 8; - memset( &_dragonSearchArray, 0, sizeof(_dragonSearchArray)); + memset(&_dragonSearchArray, 0, sizeof(_dragonSearchArray)); for (u = 0; u < SAGA_DRAGON_SEARCH_DIAMETER; u++) { for (v = 0; v < SAGA_DRAGON_SEARCH_DIAMETER; v++) { @@ -1378,8 +1368,8 @@ void IsoMap::findDragonTilePath(ActorData* actor,const Location &start, const Lo tile = getTile(u1, v1, _platformHeight); if (tile != NULL) { mask = tile->terrainMask; - if (((mask != 0) && (tile->GetFGDAttr() >= kTerrBlock)) || - ((mask != 0xFFFF) && (tile->GetBGDAttr() >= kTerrBlock))) { + if (((mask != 0 ) && (tile->getFGDAttr() >= kTerrBlock)) || + ((mask != 0xFFFF) && (tile->getBGDAttr() >= kTerrBlock))) { pcell->visited = 1; } } else { @@ -1390,7 +1380,7 @@ void IsoMap::findDragonTilePath(ActorData* actor,const Location &start, const Lo first = true; _queueCount = _readCount = 0; - pushDragonPoint( SAGA_DRAGON_SEARCH_CENTER, SAGA_DRAGON_SEARCH_CENTER, initialDirection); + pushDragonPoint(SAGA_DRAGON_SEARCH_CENTER, SAGA_DRAGON_SEARCH_CENTER, initialDirection); while (_queueCount != _readCount) { @@ -1414,40 +1404,40 @@ void IsoMap::findDragonTilePath(ActorData* actor,const Location &start, const Lo switch (tilePoint->direction) { case kDirUpRight: - if (checkDragonPoint( tilePoint->u + 1, tilePoint->v + 0, kDirUpRight)) { - pushDragonPoint( tilePoint->u + 2, tilePoint->v + 0, kDirUpRight); - pushDragonPoint( tilePoint->u + 1, tilePoint->v + 1, kDirUpLeft); - pushDragonPoint( tilePoint->u + 1, tilePoint->v - 1, kDirDownRight); + if (checkDragonPoint(tilePoint->u + 1, tilePoint->v + 0, kDirUpRight)) { + pushDragonPoint(tilePoint->u + 2, tilePoint->v + 0, kDirUpRight); + pushDragonPoint(tilePoint->u + 1, tilePoint->v + 1, kDirUpLeft); + pushDragonPoint(tilePoint->u + 1, tilePoint->v - 1, kDirDownRight); } break; case kDirDownRight: - if (checkDragonPoint( tilePoint->u + 0, tilePoint->v - 1, kDirDownRight)) { - pushDragonPoint( tilePoint->u + 0, tilePoint->v - 2, kDirDownRight); - pushDragonPoint( tilePoint->u + 1, tilePoint->v - 1, kDirUpRight); - pushDragonPoint( tilePoint->u - 1, tilePoint->v - 1, kDirDownLeft); + if (checkDragonPoint(tilePoint->u + 0, tilePoint->v - 1, kDirDownRight)) { + pushDragonPoint(tilePoint->u + 0, tilePoint->v - 2, kDirDownRight); + pushDragonPoint(tilePoint->u + 1, tilePoint->v - 1, kDirUpRight); + pushDragonPoint(tilePoint->u - 1, tilePoint->v - 1, kDirDownLeft); } break; case kDirDownLeft: - if (checkDragonPoint( tilePoint->u - 1, tilePoint->v + 0, kDirDownLeft)) { - pushDragonPoint( tilePoint->u - 2, tilePoint->v + 0, kDirDownLeft); - pushDragonPoint( tilePoint->u - 1, tilePoint->v - 1, kDirDownRight); - pushDragonPoint( tilePoint->u - 1, tilePoint->v + 1, kDirUpLeft); + if (checkDragonPoint(tilePoint->u - 1, tilePoint->v + 0, kDirDownLeft)) { + pushDragonPoint(tilePoint->u - 2, tilePoint->v + 0, kDirDownLeft); + pushDragonPoint(tilePoint->u - 1, tilePoint->v - 1, kDirDownRight); + pushDragonPoint(tilePoint->u - 1, tilePoint->v + 1, kDirUpLeft); } break; case kDirUpLeft: - if (checkDragonPoint( tilePoint->u + 0, tilePoint->v + 1, kDirUpLeft)) { - pushDragonPoint( tilePoint->u + 0, tilePoint->v + 2, kDirUpLeft); - pushDragonPoint( tilePoint->u - 1, tilePoint->v + 1, kDirDownLeft); - pushDragonPoint( tilePoint->u + 1, tilePoint->v + 1, kDirUpRight); + if (checkDragonPoint(tilePoint->u + 0, tilePoint->v + 1, kDirUpLeft)) { + pushDragonPoint(tilePoint->u + 0, tilePoint->v + 2, kDirUpLeft); + pushDragonPoint(tilePoint->u - 1, tilePoint->v + 1, kDirDownLeft); + pushDragonPoint(tilePoint->u + 1, tilePoint->v + 1, kDirUpRight); } break; } if (first && (_queueCount == _readCount)) { - pushDragonPoint( tilePoint->u + 1, tilePoint->v + 0, kDirUpRight); - pushDragonPoint( tilePoint->u + 0, tilePoint->v - 1, kDirDownRight); - pushDragonPoint( tilePoint->u - 1, tilePoint->v + 0, kDirDownLeft); - pushDragonPoint( tilePoint->u + 0, tilePoint->v + 1, kDirUpLeft); + pushDragonPoint(tilePoint->u + 1, tilePoint->v + 0, kDirUpRight); + pushDragonPoint(tilePoint->u + 0, tilePoint->v - 1, kDirDownRight); + pushDragonPoint(tilePoint->u - 1, tilePoint->v + 0, kDirDownLeft); + pushDragonPoint(tilePoint->u + 0, tilePoint->v + 1, kDirUpLeft); } first = false; } @@ -1517,7 +1507,7 @@ void IsoMap::findTilePath(ActorData* actor, const Location &start, const Locatio - memset( &_searchArray, 0, sizeof(_searchArray)); + memset(&_searchArray, 0, sizeof(_searchArray)); if (!(actor->_actorFlags & kActorNoCollide) && (_vm->_scene->currentSceneResourceId() != ITE_SCENE_OVERMAP)) { @@ -1658,7 +1648,7 @@ bool IsoMap::nextTileTarget(ActorData* actor) { void IsoMap::screenPointToTileCoords(const Point &position, Location &location) { Point mPos(position); - int x,y; + int x, y; if (_vm->_scene->currentSceneResourceId() == ITE_SCENE_OVERMAP){ if (mPos.y < 16) { diff --git a/engines/saga/isomap.h b/engines/saga/isomap.h index 155d9b8d24..83dfd98b87 100644 --- a/engines/saga/isomap.h +++ b/engines/saga/isomap.h @@ -95,20 +95,20 @@ struct IsoTileData { byte *tilePointer; uint16 terrainMask; byte FGDBGDAttr; - int8 GetMaskRule() const { + int8 getMaskRule() const { return attributes & 0x0F; } - byte GetFGDAttr() const { + byte getFGDAttr() const { return FGDBGDAttr >> 4; } - byte GetBGDAttr() const { + byte getBGDAttr() const { return FGDBGDAttr & 0x0F; } - uint16 GetFGDMask() const { - return 1 << GetFGDAttr(); + uint16 getFGDMask() const { + return 1 << getFGDAttr(); } - uint16 GetBGDMask() const { - return 1 << GetBGDAttr(); + uint16 getBGDMask() const { + return 1 << getBGDAttr(); } }; diff --git a/engines/saga/puzzle.cpp b/engines/saga/puzzle.cpp index 1d014052ed..77090860aa 100644 --- a/engines/saga/puzzle.cpp +++ b/engines/saga/puzzle.cpp @@ -93,25 +93,25 @@ Puzzle::Puzzle(SagaEngine *vm) : _vm(vm), _solved(false), _active(false) { _hintSpeaker = 0; _slidePointX = _slidePointY = 0; - initPieceInfo( 0, 268, 18, 0, 0, 0 + PUZZLE_X_OFFSET, 0 + PUZZLE_Y_OFFSET, 0, 3, - Point(0, 1), Point(0, 62), Point(15, 31), Point(0, 0), Point(0, 0), Point(0,0)); - initPieceInfo( 1, 270, 52, 0, 0, 0 + PUZZLE_X_OFFSET, 32 + PUZZLE_Y_OFFSET, 0, 4, + initPieceInfo(0, 268, 18, 0, 0, 0 + PUZZLE_X_OFFSET, 0 + PUZZLE_Y_OFFSET, 0, 3, + Point(0, 1), Point(0, 62), Point(15, 31), Point(0, 0), Point(0, 0), Point(0, 0)); + initPieceInfo(1, 270, 52, 0, 0, 0 + PUZZLE_X_OFFSET, 32 + PUZZLE_Y_OFFSET, 0, 4, Point(0, 31), Point(0, 47), Point(39, 47), Point(15, 1), Point(0, 0), Point(0, 0)); - initPieceInfo( 2, 19, 51, 0, 0, 0 + PUZZLE_X_OFFSET, 0 + PUZZLE_Y_OFFSET, 0, 4, + initPieceInfo(2, 19, 51, 0, 0, 0 + PUZZLE_X_OFFSET, 0 + PUZZLE_Y_OFFSET, 0, 4, Point(0, 0), Point(23, 46), Point(39, 15), Point(31, 0), Point(0, 0), Point(0, 0)); - initPieceInfo( 3, 73, 0, 0, 0, 32 + PUZZLE_X_OFFSET, 0 + PUZZLE_Y_OFFSET, 0, 6, + initPieceInfo(3, 73, 0, 0, 0, 32 + PUZZLE_X_OFFSET, 0 + PUZZLE_Y_OFFSET, 0, 6, Point(0, 0), Point(8, 16), Point(0, 31), Point(31, 31), Point(39, 15), Point(31, 0)); - initPieceInfo( 4, 0, 35, 0, 0, 64 + PUZZLE_X_OFFSET, 16 + PUZZLE_Y_OFFSET, 0, 4, + initPieceInfo(4, 0, 35, 0, 0, 64 + PUZZLE_X_OFFSET, 16 + PUZZLE_Y_OFFSET, 0, 4, Point(0, 15), Point(15, 46), Point(23, 32), Point(7, 1), Point(0, 0), Point(0, 0)); - initPieceInfo( 5, 215, 0, 0, 0, 24 + PUZZLE_X_OFFSET, 32 + PUZZLE_Y_OFFSET, 0, 6, + initPieceInfo(5, 215, 0, 0, 0, 24 + PUZZLE_X_OFFSET, 32 + PUZZLE_Y_OFFSET, 0, 6, Point(0, 15), Point(8, 31), Point(39, 31), Point(47, 16), Point(39, 0), Point(8, 0)); - initPieceInfo( 6, 159, 0, 0, 0, 32 + PUZZLE_X_OFFSET, 48 + PUZZLE_Y_OFFSET, 0, 5, + initPieceInfo(6, 159, 0, 0, 0, 32 + PUZZLE_X_OFFSET, 48 + PUZZLE_Y_OFFSET, 0, 5, Point(0, 16), Point(8, 31), Point(55, 31), Point(39, 1), Point(32, 15), Point(0, 0)); - initPieceInfo( 7, 9, 70, 0, 0, 80 + PUZZLE_X_OFFSET, 32 + PUZZLE_Y_OFFSET, 0, 5, + initPieceInfo(7, 9, 70, 0, 0, 80 + PUZZLE_X_OFFSET, 32 + PUZZLE_Y_OFFSET, 0, 5, Point(0, 31), Point(8, 47), Point(23, 47), Point(31, 31), Point(15, 1), Point(0, 0)); - initPieceInfo( 8, 288, 18, 0, 0, 96 + PUZZLE_X_OFFSET, 0 + PUZZLE_Y_OFFSET, 0, 4, + initPieceInfo(8, 288, 18, 0, 0, 96 + PUZZLE_X_OFFSET, 0 + PUZZLE_Y_OFFSET, 0, 4, Point(0, 31), Point(15, 62), Point(31, 32), Point(15, 1), Point(0, 0), Point(0, 0)); - initPieceInfo( 9, 112, 0, 0, 0, 112 + PUZZLE_X_OFFSET, 0 + PUZZLE_Y_OFFSET, 0, 4, + initPieceInfo(9, 112, 0, 0, 0, 112 + PUZZLE_X_OFFSET, 0 + PUZZLE_Y_OFFSET, 0, 4, Point(0, 0), Point(16, 31), Point(47, 31), Point(31, 0), Point(0, 0), Point(0, 0)); initPieceInfo(10, 27, 89, 0, 0, 104 + PUZZLE_X_OFFSET, 32 + PUZZLE_Y_OFFSET, 0, 4, Point(0, 47), Point(31, 47), Point(31, 0), Point(24, 0), Point(0, 0), Point(0, 0)); diff --git a/engines/saga/resource_hrs.cpp b/engines/saga/resource_hrs.cpp index ba58830269..9257f65cbc 100644 --- a/engines/saga/resource_hrs.cpp +++ b/engines/saga/resource_hrs.cpp @@ -34,7 +34,6 @@ #include "saga/scene.h" #include "saga/sndres.h" -#include "engines/advancedDetector.h" #include "common/endian.h" namespace Saga { diff --git a/engines/saga/resource_res.cpp b/engines/saga/resource_res.cpp index d57238b2eb..a2822f8b33 100644 --- a/engines/saga/resource_res.cpp +++ b/engines/saga/resource_res.cpp @@ -32,8 +32,6 @@ #include "saga/scene.h" #include "saga/sndres.h" -#include "engines/advancedDetector.h" - namespace Saga { #ifdef ENABLE_IHNM diff --git a/engines/sci/detection.cpp b/engines/sci/detection.cpp index 36496e828b..b0d78f753d 100644 --- a/engines/sci/detection.cpp +++ b/engines/sci/detection.cpp @@ -572,7 +572,7 @@ public: } virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const; - const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const; + ADDetectedGame fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const override; virtual bool hasFeature(MetaEngineFeature f) const; virtual SaveStateList listSaves(const char *target) const; virtual int getMaximumSaveSlot() const; @@ -600,7 +600,7 @@ Common::Language charToScummVMLanguage(const char c) { } } -const ADGameDescription *SciMetaEngine::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const { +ADDetectedGame SciMetaEngine::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const { bool foundResMap = false; bool foundRes000 = false; @@ -657,7 +657,7 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const FileMap &allFiles, // If these files aren't found, it can't be SCI if (!foundResMap && !foundRes000) - return 0; + return ADDetectedGame(); ResourceManager resMan(true); resMan.addAppropriateSourcesForDetection(fslist); @@ -668,7 +668,7 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const FileMap &allFiles, // Is SCI32 compiled in? If not, and this is a SCI32 game, // stop here if (getSciVersionForDetection() >= SCI_VERSION_2) - return 0; + return ADDetectedGame(); #endif ViewType gameViews = resMan.getViewType(); @@ -677,7 +677,7 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const FileMap &allFiles, // Can't be SCI (or unsupported SCI views). Pinball Creep by Sierra also uses resource.map/resource.000 files // but doesn't share SCI format at all if (gameViews == kViewUnknown) - return 0; + return ADDetectedGame(); // Set the platform to Amiga if the game is using Amiga views if (gameViews == kViewAmiga) @@ -688,7 +688,7 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const FileMap &allFiles, // If we don't have a game id, the game is not SCI if (sierraGameId.empty()) - return 0; + return ADDetectedGame(); Common::String gameId = convertSierraGameId(sierraGameId, &s_fallbackDesc.flags, resMan); strncpy(s_fallbackGameIdBuf, gameId.c_str(), sizeof(s_fallbackGameIdBuf) - 1); @@ -763,7 +763,7 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const FileMap &allFiles, s_fallbackDesc.extra = "CD"; } - return &s_fallbackDesc; + return ADDetectedGame(&s_fallbackDesc); } bool SciMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const { @@ -881,7 +881,14 @@ SaveStateDescriptor SciMetaEngine::querySaveMetaInfos(const char *target, int sl descriptor.setDescription(meta.name); - Graphics::Surface *const thumbnail = Graphics::loadThumbnail(*in); + Graphics::Surface *thumbnail; + if (!Graphics::loadThumbnail(*in, thumbnail)) { + // invalid + delete in; + + descriptor.setDescription("*Invalid*"); + return descriptor; + } descriptor.setThumbnail(thumbnail); int day = (meta.saveDate >> 24) & 0xFF; diff --git a/engines/sci/engine/kgraphics.cpp b/engines/sci/engine/kgraphics.cpp index 7e63c3576c..3c23cd515f 100644 --- a/engines/sci/engine/kgraphics.cpp +++ b/engines/sci/engine/kgraphics.cpp @@ -1073,6 +1073,7 @@ reg_t kSetPort(EngineState *s, int argc, reg_t *argv) { case 7: initPriorityBandsFlag = true; + // fall through case 6: picRect.top = argv[0].toSint16(); picRect.left = argv[1].toSint16(); diff --git a/engines/sci/engine/kpathing.cpp b/engines/sci/engine/kpathing.cpp index 937b1cfc2f..eb4d5d3748 100644 --- a/engines/sci/engine/kpathing.cpp +++ b/engines/sci/engine/kpathing.cpp @@ -1919,7 +1919,7 @@ static int intersectDir(const Vertex *v1, const Vertex *v2) { // Direction of edge in degrees from pos. x-axis, between -180 and 180 static int edgeDir(const Vertex *v) { Common::Point p = v->_next->v - v->v; - int deg = (int)Common::rad2deg((float)atan2((double)p.y, (double)p.x)); + int deg = Common::rad2deg<float,int>((float)atan2((double)p.y, (double)p.x)); if (deg < -180) deg += 360; if (deg > 180) deg -= 360; return deg; diff --git a/engines/sci/event.cpp b/engines/sci/event.cpp index aef84e212d..ab56311cd6 100644 --- a/engines/sci/event.cpp +++ b/engines/sci/event.cpp @@ -418,7 +418,7 @@ SciEvent EventManager::getSciEvent(SciEventType mask) { void EventManager::flushEvents() { Common::EventManager *em = g_system->getEventManager(); Common::Event event; - while (em->pollEvent(event)); + while (em->pollEvent(event)) {} _events.clear(); } diff --git a/engines/sci/graphics/animate.cpp b/engines/sci/graphics/animate.cpp index 317e98feab..8875162394 100644 --- a/engines/sci/graphics/animate.cpp +++ b/engines/sci/graphics/animate.cpp @@ -650,6 +650,11 @@ void GfxAnimate::animateShowPic() { } void GfxAnimate::kernelAnimate(reg_t listReference, bool cycle, int argc, reg_t *argv) { + // If necessary, delay this kAnimate for a running PalVary. + // See delayForPalVaryWorkaround() for details. + if (_screen->_picNotValid) + _palette->delayForPalVaryWorkaround(); + byte old_picNotValid = _screen->_picNotValid; if (getSciVersion() >= SCI_VERSION_1_1) diff --git a/engines/sci/graphics/celobj32.h b/engines/sci/graphics/celobj32.h index 02b2859f5c..37e9d39bf5 100644 --- a/engines/sci/graphics/celobj32.h +++ b/engines/sci/graphics/celobj32.h @@ -132,6 +132,9 @@ struct CelInfo32 { default: assert(!"Should never happen"); } + // This code should not be reached but the compiler expects to see a legal + // return from a non-void function. + return Common::String("here be dragons"); } }; diff --git a/engines/sci/graphics/palette.cpp b/engines/sci/graphics/palette.cpp index e36028d984..bc0b8485d2 100644 --- a/engines/sci/graphics/palette.cpp +++ b/engines/sci/graphics/palette.cpp @@ -716,6 +716,7 @@ void GfxPalette::palVaryInit() { _palVaryStepStop = 0; _palVaryDirection = 0; _palVaryTicks = 0; + _palVaryZeroTick = false; } bool GfxPalette::palVaryLoadTargetPalette(GuiResourceId resourceId) { @@ -759,19 +760,13 @@ bool GfxPalette::kernelPalVaryInit(GuiResourceId resourceId, uint16 ticks, uint1 _palVaryStep = 1; _palVaryStepStop = stepStop; _palVaryDirection = direction; + // if no ticks are given, jump directly to destination - if (!_palVaryTicks) { + if (!_palVaryTicks) _palVaryDirection = stepStop; - // sierra sci set the timer to 1 tick instead of calling it directly - // we have to change this to prevent a race condition to happen in - // at least freddy pharkas during nighttime. In that case kPalVary is - // called right before a transition and because we load pictures much - // faster, the 1 tick won't pass sometimes resulting in the palette - // being daytime instead of nighttime during the transition. - palVaryProcess(1, true); - } else { - palVaryInstallTimer(); - } + _palVaryZeroTick = (_palVaryTicks == 0); //see delayForPalVaryWorkaround() + + palVaryInstallTimer(); return true; } return false; @@ -788,14 +783,13 @@ int16 GfxPalette::kernelPalVaryReverse(int16 ticks, uint16 stepStop, int16 direc _palVaryStepStop = stepStop; _palVaryDirection = direction != -1 ? -direction : -_palVaryDirection; - if (!_palVaryTicks) { + // if no ticks are given, jump directly to destination + if (!_palVaryTicks) _palVaryDirection = _palVaryStepStop - _palVaryStep; - // see palVaryInit above, we fix the code here as well - // just in case - palVaryProcess(1, true); - } else { - palVaryInstallTimer(); - } + _palVaryZeroTick = (_palVaryTicks == 0); // see delayForPalVaryWorkaround() + + palVaryInstallTimer(); + return kernelPalVaryGetCurrentStep(); } @@ -855,6 +849,7 @@ void GfxPalette::palVaryIncreaseSignal() { // FIXME: increments from another thread aren't guaranteed to be atomic if (!_palVaryPaused) _palVarySignal++; + _palVaryZeroTick = false; } // Actually do the pal vary processing @@ -865,6 +860,34 @@ void GfxPalette::palVaryUpdate() { } } +void GfxPalette::delayForPalVaryWorkaround() { + if (_palVaryResourceId == -1) + return; + if (_palVaryPaused) + return; + + // This gets called at the very beginning of kAnimate. + // If a zero-tick palVary is running, we delay briefly to give the + // palVary time to trigger. In theory there should be no reason for this + // to have to wait more than a tick, but we time-out after 4 ticks + // to be on the safe side. + // + // This prevents a race condition in Freddy Pharkas during nighttime, + // since we load pictures much faster than on original hardware (bug #5298). + + if (_palVaryZeroTick) { + int i; + for (i = 0; i < 4; ++i) { + g_sci->sleep(17); + if (!_palVaryZeroTick) + break; + } + debugC(kDebugLevelGraphics, "Delayed kAnimate for kPalVary, %d times", i+1); + if (_palVaryZeroTick) + warning("Delayed kAnimate for kPalVary timed out"); + } +} + void GfxPalette::palVaryPrepareForTransition() { if (_palVaryResourceId != -1) { // Before doing transitions, we have to prepare palette diff --git a/engines/sci/graphics/palette.h b/engines/sci/graphics/palette.h index af74169976..2923df9220 100644 --- a/engines/sci/graphics/palette.h +++ b/engines/sci/graphics/palette.h @@ -88,6 +88,8 @@ public: void palVaryPrepareForTransition(); void palVaryProcess(int signal, bool setPalette); + void delayForPalVaryWorkaround(); + Palette _sysPalette; void saveLoadWithSerializer(Common::Serializer &s); @@ -122,6 +124,7 @@ protected: uint16 _palVaryTicks; int _palVaryPaused; int _palVarySignal; + bool _palVaryZeroTick; uint16 _totalScreenColors; void loadMacIconBarPalette(); diff --git a/engines/sci/sound/drivers/midi.cpp b/engines/sci/sound/drivers/midi.cpp index 46d08a5997..889a875804 100644 --- a/engines/sci/sound/drivers/midi.cpp +++ b/engines/sci/sound/drivers/midi.cpp @@ -143,25 +143,25 @@ public: MidiPlayer_Midi(SciVersion version); virtual ~MidiPlayer_Midi(); - int open(ResourceManager *resMan); - void close(); - void send(uint32 b); - void sysEx(const byte *msg, uint16 length); - bool hasRhythmChannel() const { return true; } - byte getPlayId() const; - int getPolyphony() const { + int open(ResourceManager *resMan) override; + void close() override; + void send(uint32 b) override; + void sysEx(const byte *msg, uint16 length) override; + bool hasRhythmChannel() const override { return true; } + byte getPlayId() const override; + int getPolyphony() const override { if (g_sci && g_sci->_features->useAltWinGMSound()) return 16; else return kVoices; } - int getFirstChannel() const; - int getLastChannel() const; - void setVolume(byte volume); + int getFirstChannel() const override; + int getLastChannel() const override; + void setVolume(byte volume) override; virtual void onNewSound() override; - int getVolume(); - void setReverb(int8 reverb); - void playSwitch(bool play); + int getVolume() override; + void setReverb(int8 reverb) override; + void playSwitch(bool play) override; private: bool isMt32GmPatch(const SciSpan<const byte> &data); diff --git a/engines/scumm/camera.cpp b/engines/scumm/camera.cpp index 799fdd7547..5a81057aac 100644 --- a/engines/scumm/camera.cpp +++ b/engines/scumm/camera.cpp @@ -25,6 +25,8 @@ #include "scumm/charset.h" #include "scumm/scumm_v7.h" +#include "common/util.h" + namespace Scumm { void ScummEngine::setCameraAtEx(int at) { @@ -85,17 +87,8 @@ void ScummEngine::setCameraFollows(Actor *a, bool setCamera) { } void ScummEngine::clampCameraPos(Common::Point *pt) { - if (pt->x < VAR(VAR_CAMERA_MIN_X)) - pt->x = (short) VAR(VAR_CAMERA_MIN_X); - - if (pt->x > VAR(VAR_CAMERA_MAX_X)) - pt->x = (short) VAR(VAR_CAMERA_MAX_X); - - if (pt->y < VAR(VAR_CAMERA_MIN_Y)) - pt->y = (short) VAR(VAR_CAMERA_MIN_Y); - - if (pt->y > VAR(VAR_CAMERA_MAX_Y)) - pt->y = (short) VAR(VAR_CAMERA_MAX_Y); + pt->x = CLIP<short>(pt->x, VAR(VAR_CAMERA_MIN_X), VAR(VAR_CAMERA_MAX_X)); + pt->y = CLIP<short>(pt->y, VAR(VAR_CAMERA_MIN_Y), VAR(VAR_CAMERA_MAX_Y)); } void ScummEngine::moveCamera() { diff --git a/engines/scumm/configure.engine b/engines/scumm/configure.engine index e8962a371e..42c75226da 100644 --- a/engines/scumm/configure.engine +++ b/engines/scumm/configure.engine @@ -2,4 +2,4 @@ # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] add_engine scumm "SCUMM" yes "scumm_7_8 he" "v0-v6 games" add_engine scumm_7_8 "v7 & v8 games" yes -add_engine he "HE71+ games" yes "" "" "highres" +add_engine he "HE71+ games" yes "" "" "highres bink" diff --git a/engines/scumm/detection.cpp b/engines/scumm/detection.cpp index 0aa993a53a..fccb30b0fa 100644 --- a/engines/scumm/detection.cpp +++ b/engines/scumm/detection.cpp @@ -959,9 +959,9 @@ public: virtual const char *getOriginalCopyright() const; virtual bool hasFeature(MetaEngineFeature f) const; - virtual GameList getSupportedGames() const; - virtual GameDescriptor findGame(const char *gameid) const; - virtual GameList detectGames(const Common::FSList &fslist) const; + PlainGameList getSupportedGames() const override; + PlainGameDescriptor findGame(const char *gameid) const override; + virtual DetectedGames detectGames(const Common::FSList &fslist) const override; virtual Common::Error createInstance(OSystem *syst, Engine **engine) const; @@ -992,11 +992,11 @@ bool ScummEngine::hasFeature(EngineFeature f) const { (f == kSupportsSubtitleOptions); } -GameList ScummMetaEngine::getSupportedGames() const { - return GameList(gameDescriptions); +PlainGameList ScummMetaEngine::getSupportedGames() const { + return PlainGameList(gameDescriptions); } -GameDescriptor ScummMetaEngine::findGame(const char *gameid) const { +PlainGameDescriptor ScummMetaEngine::findGame(const char *gameid) const { return Engines::findGameID(gameid, gameDescriptions, obsoleteGameIDsTable); } @@ -1026,29 +1026,26 @@ static Common::String generatePreferredTarget(const DetectorResult &x) { return res; } -GameList ScummMetaEngine::detectGames(const Common::FSList &fslist) const { - GameList detectedGames; +DetectedGames ScummMetaEngine::detectGames(const Common::FSList &fslist) const { + DetectedGames detectedGames; Common::List<DetectorResult> results; - ::detectGames(fslist, results, 0); for (Common::List<DetectorResult>::iterator x = results.begin(); x != results.end(); ++x) { const PlainGameDescriptor *g = findPlainGameDescriptor(x->game.gameid, gameDescriptions); assert(g); - GameDescriptor dg(x->game.gameid, g->description, x->language, x->game.platform); - // Append additional information, if set, to the description. - dg.updateDesc(x->extra); + DetectedGame game = DetectedGame(x->game.gameid, g->description, x->language, x->game.platform, x->extra); // Compute and set the preferred target name for this game. // Based on generateComplexID() in advancedDetector.cpp. - dg["preferredtarget"] = generatePreferredTarget(*x); + game.preferredTarget = generatePreferredTarget(*x); - dg.setGUIOptions(x->game.guioptions + MidiDriver::musicType2GUIO(x->game.midi)); - dg.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(x->language)); + game.setGUIOptions(x->game.guioptions + MidiDriver::musicType2GUIO(x->game.midi)); + game.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(x->language)); - detectedGames.push_back(dg); + detectedGames.push_back(game); } return detectedGames; @@ -1324,6 +1321,14 @@ SaveStateDescriptor ScummMetaEngine::querySaveMetaInfos(const char *target, int } SaveStateDescriptor desc(slot, saveDesc); + + // Do not allow save slot 0 (used for auto-saving) to be deleted or + // overwritten. + if (slot == 0) { + desc.setWriteProtectedFlag(true); + desc.setDeletableFlag(false); + } + desc.setThumbnail(thumbnail); if (infoPtr) { diff --git a/engines/scumm/dialogs.h b/engines/scumm/dialogs.h index 9dee4c8a0b..b2cd760ff4 100644 --- a/engines/scumm/dialogs.h +++ b/engines/scumm/dialogs.h @@ -127,15 +127,15 @@ class ValueDisplayDialog : public GUI::Dialog { public: ValueDisplayDialog(const Common::String& label, int minVal, int maxVal, int val, uint16 incKey, uint16 decKey); - virtual void open(); + virtual void open() override; void drawDialog(GUI::DrawLayer layerToDraw) override; - virtual void handleTickle(); - virtual void handleMouseDown(int x, int y, int button, int clickCount) { + virtual void handleTickle() override; + virtual void handleMouseDown(int x, int y, int button, int clickCount) override { close(); } - virtual void handleKeyDown(Common::KeyState state); + virtual void handleKeyDown(Common::KeyState state) override; - virtual void reflowLayout(); + virtual void reflowLayout() override; protected: enum { diff --git a/engines/scumm/he/moonbase/ai_main.cpp b/engines/scumm/he/moonbase/ai_main.cpp index c391105658..c5c1f6a8e4 100644 --- a/engines/scumm/he/moonbase/ai_main.cpp +++ b/engines/scumm/he/moonbase/ai_main.cpp @@ -2811,9 +2811,9 @@ int AI::simulateBuildingLaunch(int x, int y, int power, int angle, int numSteps, numSteps = 1; if (!sXSpeed && !sYSpeed) { - sZSpeed = (static_cast<int>(.70711 * power)) ; - sXSpeed = (static_cast<int>(cos(degToRad(angle)) * sZSpeed)) ; - sYSpeed = (static_cast<int>(sin(degToRad(angle)) * sZSpeed)) ; + sZSpeed = (static_cast<int>(.70711 * power)); + sXSpeed = (static_cast<int>(cos(degToRad(angle)) * sZSpeed)); + sYSpeed = (static_cast<int>(sin(degToRad(angle)) * sZSpeed)); sZSpeed *= SCALE_Z; @@ -2959,9 +2959,9 @@ int AI::simulateWeaponLaunch(int x, int y, int power, int angle, int numSteps) { if (!numSteps) numSteps = 1; if (!sXSpeed && !sYSpeed) { - sZSpeed = (static_cast<int>(.70711 * power)) ; - sXSpeed = (static_cast<int>(cos(degToRad(angle)) * sZSpeed)) ; - sYSpeed = (static_cast<int>(sin(degToRad(angle)) * sZSpeed)) ; + sZSpeed = (static_cast<int>(.70711 * power)); + sXSpeed = (static_cast<int>(cos(degToRad(angle)) * sZSpeed)); + sYSpeed = (static_cast<int>(sin(degToRad(angle)) * sZSpeed)); sZSpeed *= SCALE_Z; diff --git a/engines/scumm/imuse/imuse.cpp b/engines/scumm/imuse/imuse.cpp index 5de921bc6b..f5526ab11d 100644 --- a/engines/scumm/imuse/imuse.cpp +++ b/engines/scumm/imuse/imuse.cpp @@ -1236,11 +1236,8 @@ int32 IMuseInternal::ImSetTrigger(int sound, int id, int a, int b, int c, int d, if (trig->id == id && trig->sound == sound && trig->command[0] == a) break; - uint16 diff; - if (trig->expire <= _snm_trigger_index) - diff = _snm_trigger_index - trig->expire; - else - diff = 0x10000 - trig->expire + _snm_trigger_index; + // The wraparound if trig->expire > _snm_trigger_index is intentional + uint16 diff = _snm_trigger_index - trig->expire; if (!oldest_ptr || oldest_trigger < diff) { oldest_ptr = trig; diff --git a/engines/scumm/imuse_digi/dimuse_track.h b/engines/scumm/imuse_digi/dimuse_track.h index a007903139..ef0a8adb21 100644 --- a/engines/scumm/imuse_digi/dimuse_track.h +++ b/engines/scumm/imuse_digi/dimuse_track.h @@ -66,7 +66,7 @@ struct Track { int32 curRegion; // id of current used region int32 curHookId; // id of current used hook id int32 volGroupId; // id of volume group (IMUSE_VOLGRP_VOICE, IMUSE_VOLGRP_SFX, IMUSE_VOLGRP_MUSIC) - int32 soundType; // type of sound data (kSpeechSoundType, kSFXSoundType, kMusicSoundType) + int32 soundType; // type of sound data (IMUSE_BUNDLE, IMUSE_RESOURCE) int32 feedSize; // size of sound data needed to be filled at each callback iteration int32 dataMod12Bit; // value used between all callback to align 12 bit source of data int32 mixerFlags; // flags for sound mixer's channel (kFlagStereo, kFlag16Bits, kFlagUnsigned) diff --git a/engines/scumm/input.cpp b/engines/scumm/input.cpp index 6ef7e4d7f4..9c5271e51c 100644 --- a/engines/scumm/input.cpp +++ b/engines/scumm/input.cpp @@ -560,7 +560,7 @@ void ScummEngine::processKeyboard(Common::KeyState lastKeyHit) { } else if (pauseKeyEnabled && (lastKeyHit.keycode == Common::KEYCODE_SPACE && lastKeyHit.hasFlags(0))) { pauseGame(); - } else if (talkstopKeyEnabled && (lastKeyHit.keycode == Common::KEYCODE_PERIOD && lastKeyHit.hasFlags(0))) { + } else if (talkstopKeyEnabled && lastKeyHit.ascii == '.') { _talkDelay = 0; if (_sound->_sfxMode & 2) stopTalk(); diff --git a/engines/scumm/object.cpp b/engines/scumm/object.cpp index f4aea93b8b..df0b3e1035 100644 --- a/engines/scumm/object.cpp +++ b/engines/scumm/object.cpp @@ -45,6 +45,7 @@ void ScummEngine::addObjectToInventory(uint obj, uint room) { idx = getObjectIndex(obj); assert(idx >= 0); ptr = getResourceAddress(rtFlObject, _objs[idx].fl_object_index) + 8; + assert(ptr); size = READ_BE_UINT32(ptr + 4); } else { findObjectInRoom(&foir, foCodeHeader, obj, room); @@ -742,6 +743,7 @@ void ScummEngine::resetRoomObjects() { const CodeHeader *cdhd; room = getResourceAddress(rtRoom, _roomResource); + assert(room); if (_numObjectsInRoom == 0) return; @@ -756,7 +758,7 @@ void ScummEngine::resetRoomObjects() { assert(searchptr); // Load in new room objects - ResourceIterator obcds(searchptr, false); + ResourceIterator obcds(searchptr, false); for (i = 0; i < _numObjectsInRoom; i++) { od = &_objs[findLocalObjectSlot()]; @@ -784,7 +786,7 @@ void ScummEngine::resetRoomObjects() { } searchptr = room; - ResourceIterator obims(room, false); + ResourceIterator obims(room, false); for (i = 0; i < _numObjectsInRoom; i++) { ptr = obims.findNext(MKTAG('O','B','I','M')); if (ptr == NULL) @@ -810,6 +812,7 @@ void ScummEngine_v3old::resetRoomObjects() { const byte *room, *ptr; room = getResourceAddress(rtRoom, _roomResource); + assert(room); if (_numObjectsInRoom == 0) return; @@ -854,6 +857,7 @@ void ScummEngine_v4::resetRoomObjects() { const byte *room; room = getResourceAddress(rtRoom, _roomResource); + assert(room); if (_numObjectsInRoom == 0) return; @@ -861,7 +865,7 @@ void ScummEngine_v4::resetRoomObjects() { if (_numObjectsInRoom > _numLocalObjects) error("More than %d objects in room %d", _numLocalObjects, _roomResource); - ResourceIterator obcds(room, true); + ResourceIterator obcds(room, true); for (i = 0; i < _numObjectsInRoom; i++) { od = &_objs[findLocalObjectSlot()]; @@ -878,7 +882,7 @@ void ScummEngine_v4::resetRoomObjects() { } } - ResourceIterator obims(room, true); + ResourceIterator obims(room, true); for (i = 0; i < _numObjectsInRoom; i++) { // In the PC Engine version of Loom, there aren't image blocks // for all objects. @@ -979,10 +983,12 @@ void ScummEngine::resetRoomObject(ObjectData *od, const byte *room, const byte * assert(room); if (searchptr == NULL) { - if (_game.version == 8) + if (_game.version == 8) { searchptr = getResourceAddress(rtRoomScripts, _roomResource); - else + assert(searchptr); + } else { searchptr = room; + } } cdhd = (const CodeHeader *)findResourceData(MKTAG('C','D','H','D'), searchptr + od->OBCDoffset); diff --git a/engines/scumm/players/player_ad.cpp b/engines/scumm/players/player_ad.cpp index 7f0f449a92..40f4ec93c9 100644 --- a/engines/scumm/players/player_ad.cpp +++ b/engines/scumm/players/player_ad.cpp @@ -97,6 +97,7 @@ void Player_AD::startSound(int sound) { // Query the sound resource const byte *res = _vm->getResourceAddress(rtSound, sound); + assert(res); if (res[2] == 0x80) { // Stop the current sounds diff --git a/engines/scumm/players/player_nes.cpp b/engines/scumm/players/player_nes.cpp index 3b5adc4550..b2f6eb0d40 100644 --- a/engines/scumm/players/player_nes.cpp +++ b/engines/scumm/players/player_nes.cpp @@ -1029,11 +1029,7 @@ top: } _mchan[x].volume += _mchan[x].voldelta; - - if (_mchan[x].volume < 0) - _mchan[x].volume = 0; - if (_mchan[x].volume > MAXVOLUME) - _mchan[x].volume = MAXVOLUME; + _mchan[x].volume = CLIP(_mchan[x].volume, 0, MAXVOLUME); APU_writeChannel(x, 0, (_mchan[x].volume >> 3) | _mchan[x].envflags); } diff --git a/engines/scumm/players/player_towns.cpp b/engines/scumm/players/player_towns.cpp index 16080205c0..a1add906bb 100644 --- a/engines/scumm/players/player_towns.cpp +++ b/engines/scumm/players/player_towns.cpp @@ -236,6 +236,8 @@ void Player_Towns_v1::setMusicVolume(int vol) { void Player_Towns_v1::startSound(int sound) { uint8 *ptr = _vm->getResourceAddress(rtSound, sound); + assert(ptr); + if (_vm->_game.version != 3) ptr += 2; @@ -620,6 +622,7 @@ int Player_Towns_v2::getSoundStatus(int sound) const { void Player_Towns_v2::startSound(int sound) { uint8 *ptr = _vm->getResourceAddress(rtSound, sound); + assert(ptr); if (READ_BE_UINT32(ptr) == MKTAG('T','O','W','S')) { _soundOverride[sound].type = 7; diff --git a/engines/scumm/saveload.cpp b/engines/scumm/saveload.cpp index d04b3bb5ad..84dd16efa6 100644 --- a/engines/scumm/saveload.cpp +++ b/engines/scumm/saveload.cpp @@ -706,7 +706,9 @@ bool ScummEngine::querySaveMetaInfos(const char *target, int slot, int heversion if (hdr.ver > VER(52)) { if (Graphics::checkThumbnailHeader(*in)) { - thumbnail = Graphics::loadThumbnail(*in); + if (!Graphics::loadThumbnail(*in, thumbnail)) { + return false; + } } if (hdr.ver > VER(57)) { diff --git a/engines/scumm/script_v2.cpp b/engines/scumm/script_v2.cpp index 822e32d9c4..208b1f4aef 100644 --- a/engines/scumm/script_v2.cpp +++ b/engines/scumm/script_v2.cpp @@ -1188,7 +1188,7 @@ void ScummEngine_v2::o2_startScript() { } } - // WORKAROUND bug #4556: Purple Tentacle can appear in the lab, after being + // WORKAROUND bug #4556: Purple Tentacle can appear in the lab, after being // chased out and end up stuck in the room. This bug is triggered if the player // enters the lab within 45 minutes of first entering the mansion and has chased Purple Tentacle // out. Eventually the cutscene with Purple Tentacle chasing Sandy in the lab @@ -1211,10 +1211,9 @@ void ScummEngine_v2::o2_startScript() { } void ScummEngine_v2::stopScriptCommon(int script) { - // WORKAROUND bug #4112: If you enter the lab while Dr. Fred has the powered turned off // to repair the Zom-B-Matic, the script will be stopped and the power will never turn - // back on. This fix forces the power on, when the player enters the lab, + // back on. This fix forces the power on, when the player enters the lab, // if the script which turned it off is running if (_game.id == GID_MANIAC && _roomResource == 4 && isScriptRunning(MM_SCRIPT(138))) { @@ -1320,7 +1319,7 @@ void ScummEngine_v2::o2_putActorInRoom() { // Var[245] is set to have the Disguise on in most situations // // We don't touch the variable in the following situations - // If the Caponian is being put into the space ship room, or the current room is the + // If the Caponian is being put into the space ship room, or the current room is the // space ship and the Caponian is being put into the backroom of the telephone company (you didnt show your fan club card) if (_game.id == GID_ZAK && _game.version <= 2 && act == 7) { // Is script-96 cutscene done diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp index fc64df4a1a..2f6ea489d5 100644 --- a/engines/scumm/scumm.cpp +++ b/engines/scumm/scumm.cpp @@ -2435,6 +2435,9 @@ void ScummEngine::scummLoop_handleSaveLoad() { if (success && _saveTemporaryState && VAR_GAME_LOADED != 0xFF && _game.version <= 7) VAR(VAR_GAME_LOADED) = 201; + + if (!_saveTemporaryState) + _lastSaveTime = _system->getMillis(); } else { success = loadState(_saveLoadSlot, _saveTemporaryState, filename); if (!success) @@ -2458,7 +2461,6 @@ void ScummEngine::scummLoop_handleSaveLoad() { clearClickedStatus(); _saveLoadFlag = 0; - _lastSaveTime = _system->getMillis(); } } diff --git a/engines/sherlock/detection.cpp b/engines/sherlock/detection.cpp index 9184fd8e88..e72700fbf2 100644 --- a/engines/sherlock/detection.cpp +++ b/engines/sherlock/detection.cpp @@ -200,6 +200,8 @@ bool SherlockMetaEngine::hasFeature(MetaEngineFeature f) const { (f == kSupportsDeleteSave) || (f == kSavesSupportMetaInfo) || (f == kSavesSupportThumbnail) || + (f == kSavesSupportCreationDate) || + (f == kSavesSupportPlayTime) || (f == kSimpleSavesNames); } @@ -233,7 +235,10 @@ SaveStateDescriptor SherlockMetaEngine::querySaveMetaInfos(const char *target, i if (f) { Sherlock::SherlockSavegameHeader header; - Sherlock::SaveManager::readSavegameHeader(f, header); + if (!Sherlock::SaveManager::readSavegameHeader(f, header, false)) { + delete f; + return SaveStateDescriptor(); + } delete f; // Create the return descriptor diff --git a/engines/sherlock/image_file.cpp b/engines/sherlock/image_file.cpp index c53e537bb8..112655648a 100644 --- a/engines/sherlock/image_file.cpp +++ b/engines/sherlock/image_file.cpp @@ -703,7 +703,7 @@ void ImageFile3DO::load3DOCelRoomData(Common::SeekableReadStream &stream) { error("load3DOCelRoomData: expected cel data, not enough bytes"); // read data into memory - byte *celDataPtr = new byte[celDataSize]; + byte *celDataPtr = new byte[celDataSize]; stream.read(celDataPtr, celDataSize); streamLeft -= celDataSize; @@ -936,15 +936,15 @@ void ImageFile3DO::loadFont(Common::SeekableReadStream &stream) { stream.read(bitsTablePtr, bitsTableSize); // Now extract all characters - uint16 curChar = 0; - const byte *curBitsLinePtr = bitsTablePtr; - const byte *curBitsPtr = NULL; - byte curBitsLeft = 0; - uint32 curCharHeightLeft = 0; - uint32 curCharWidthLeft = 0; - byte curBits = 0; - byte curBitsReversed = 0; - byte curPosX = 0; + uint16 curChar = 0; + const byte *curBitsLinePtr = bitsTablePtr; + const byte *curBitsPtr = NULL; + byte curBitsLeft = 0; + uint32 curCharHeightLeft = 0; + uint32 curCharWidthLeft = 0; + byte curBits = 0; + byte curBitsReversed = 0; + byte curPosX = 0; assert(bitsTableSize >= (header_maxChar * header_fontHeight * header_bytesPerLine)); // Security diff --git a/engines/sherlock/music.cpp b/engines/sherlock/music.cpp index cef7157034..20b811ea98 100644 --- a/engines/sherlock/music.cpp +++ b/engines/sherlock/music.cpp @@ -182,8 +182,8 @@ bool MidiParser_SH::loadMusic(byte *musData, uint32 musDataSize) { _musData = musData; _musDataSize = musDataSize; - byte *headerPtr = _musData + 12; // skip over the already checked SPACE header - byte *pos = headerPtr; + byte *headerPtr = _musData + 12; // skip over the already checked SPACE header + byte *pos = headerPtr; uint16 headerSize = READ_LE_UINT16(headerPtr); assert(headerSize == 0x7F); // Security check diff --git a/engines/sherlock/objects.h b/engines/sherlock/objects.h index 25ccddedbb..6f1148e956 100644 --- a/engines/sherlock/objects.h +++ b/engines/sherlock/objects.h @@ -470,7 +470,7 @@ struct SceneImage { int _filesize; // File size SceneImage(); -} ; +}; } // End of namespace Sherlock diff --git a/engines/sherlock/resources.cpp b/engines/sherlock/resources.cpp index 9ed6951fbe..ec7d60a1a2 100644 --- a/engines/sherlock/resources.cpp +++ b/engines/sherlock/resources.cpp @@ -57,23 +57,24 @@ void Cache::load(const Common::String &name, Common::SeekableReadStream &stream) int32 signature = stream.readUint32BE(); stream.seek(0); - - // Check whether the file is compressed - if (signature == MKTAG('L', 'Z', 'V', 26)) { - // Allocate a new cache entry - _resources[name] = CacheEntry(); - CacheEntry &cacheEntry = _resources[name]; + // Allocate a new cache entry + _resources[name] = CacheEntry(); + CacheEntry &cacheEntry = _resources[name]; + // Check whether the file is compressed + if (signature == MKTAG('L', 'Z', 'V', 26)) { // It's compressed, so decompress the file and store its data in the cache entry Common::SeekableReadStream *decompressed = _vm->_res->decompress(stream); cacheEntry.resize(decompressed->size()); decompressed->read(&cacheEntry[0], decompressed->size()); delete decompressed; - + } else { + // It's not, so read the raw data of the file into the cache entry + cacheEntry.resize(stream.size()); + stream.read(&cacheEntry[0], stream.size()); } - } Common::SeekableReadStream *Cache::get(const Common::String &filename) const { diff --git a/engines/sherlock/saveload.cpp b/engines/sherlock/saveload.cpp index 44b5e103d2..ca27f57a97 100644 --- a/engines/sherlock/saveload.cpp +++ b/engines/sherlock/saveload.cpp @@ -93,7 +93,6 @@ SaveStateList SaveManager::getSavegameList(const Common::String &target) { SherlockSavegameHeader header; filenames = saveFileMan->listSavefiles(pattern); - sort(filenames.begin(), filenames.end()); // Sort to get the files in numerical order SaveStateList saveList; for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) { @@ -104,24 +103,20 @@ SaveStateList SaveManager::getSavegameList(const Common::String &target) { Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(*file); if (in) { - if (!readSavegameHeader(in, header)) - continue; + if (readSavegameHeader(in, header)) + saveList.push_back(SaveStateDescriptor(slot, header._saveName)); - saveList.push_back(SaveStateDescriptor(slot, header._saveName)); - - header._thumbnail->free(); - delete header._thumbnail; delete in; } } } + Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator()); return saveList; } -bool SaveManager::readSavegameHeader(Common::InSaveFile *in, SherlockSavegameHeader &header) { +WARN_UNUSED_RESULT bool SaveManager::readSavegameHeader(Common::InSaveFile *in, SherlockSavegameHeader &header, bool skipThumbnail) { char saveIdentBuffer[SAVEGAME_STR_SIZE + 1]; - header._thumbnail = nullptr; // Validate the header Id in->read(saveIdentBuffer, SAVEGAME_STR_SIZE + 1); @@ -138,9 +133,9 @@ bool SaveManager::readSavegameHeader(Common::InSaveFile *in, SherlockSavegameHea while ((ch = (char)in->readByte()) != '\0') header._saveName += ch; // Get the thumbnail - header._thumbnail = Graphics::loadThumbnail(*in); - if (!header._thumbnail) + if (!Graphics::loadThumbnail(*in, header._thumbnail, skipThumbnail)) { return false; + } // Read in save date/time header._year = in->readSint16LE(); @@ -212,11 +207,6 @@ void SaveManager::loadGame(int slot) { if (!readSavegameHeader(saveFile, header)) error("Invalid savegame"); - if (header._thumbnail) { - header._thumbnail->free(); - delete header._thumbnail; - } - // Synchronize the savegame data Serializer s(saveFile, nullptr); s.setVersion(header._version); diff --git a/engines/sherlock/saveload.h b/engines/sherlock/saveload.h index 59b0b26d6e..6348b0f668 100644 --- a/engines/sherlock/saveload.h +++ b/engines/sherlock/saveload.h @@ -105,7 +105,7 @@ public: /** * Read in the header information for a savegame */ - static bool readSavegameHeader(Common::InSaveFile *in, SherlockSavegameHeader &header); + WARN_UNUSED_RESULT static bool readSavegameHeader(Common::InSaveFile *in, SherlockSavegameHeader &header, bool skipThumbnail = true); /** * Return the index of the button the mouse is over, if any diff --git a/engines/sherlock/scalpel/scalpel.cpp b/engines/sherlock/scalpel/scalpel.cpp index 2aa6ae8902..fbe025c0b7 100644 --- a/engines/sherlock/scalpel/scalpel.cpp +++ b/engines/sherlock/scalpel/scalpel.cpp @@ -258,8 +258,8 @@ void ScalpelEngine::setupGraphics() { // First try for a 640x400 mode g_system->beginGFXTransaction(); - initCommonGFX(); - g_system->initSize(640, 400, &pixelFormatRGB565); + initCommonGFX(); + g_system->initSize(640, 400, &pixelFormatRGB565); OSystem::TransactionError gfxError = g_system->endGFXTransaction(); if (gfxError == OSystem::kTransactionSuccess) { diff --git a/engines/sherlock/sherlock.cpp b/engines/sherlock/sherlock.cpp index c6b38f78d7..fcc092f017 100644 --- a/engines/sherlock/sherlock.cpp +++ b/engines/sherlock/sherlock.cpp @@ -137,9 +137,9 @@ Common::Error SherlockEngine::run() { _saves->loadGame(_loadGameSlot); _loadGameSlot = -1; } else { - do + do { showOpening(); - while (!shouldQuit() && !_interactiveFl); + } while (!shouldQuit() && !_interactiveFl); } while (!shouldQuit()) { diff --git a/engines/sherlock/tattoo/tattoo_people.cpp b/engines/sherlock/tattoo/tattoo_people.cpp index fc3c2e6574..458cc1a9c2 100644 --- a/engines/sherlock/tattoo/tattoo_people.cpp +++ b/engines/sherlock/tattoo/tattoo_people.cpp @@ -39,7 +39,7 @@ struct AdjustWalk { int _xAdjust; int _flipXAdjust; int _yAdjust; -} ; +}; static const AdjustWalk ADJUST_WALKS[NUM_ADJUSTED_WALKS] = { { "TUPRIGHT", -7, -19, 6 }, diff --git a/engines/sky/detection.cpp b/engines/sky/detection.cpp index b5425f9532..642e4d31a7 100644 --- a/engines/sky/detection.cpp +++ b/engines/sky/detection.cpp @@ -76,10 +76,10 @@ public: virtual const char *getOriginalCopyright() const; virtual bool hasFeature(MetaEngineFeature f) const; - virtual GameList getSupportedGames() const; + PlainGameList getSupportedGames() const override; virtual const ExtraGuiOptions getExtraGuiOptions(const Common::String &target) const; - virtual GameDescriptor findGame(const char *gameid) const; - virtual GameList detectGames(const Common::FSList &fslist) const; + PlainGameDescriptor findGame(const char *gameid) const override; + DetectedGames detectGames(const Common::FSList &fslist) const override; virtual Common::Error createInstance(OSystem *syst, Engine **engine) const; @@ -110,8 +110,8 @@ bool Sky::SkyEngine::hasFeature(EngineFeature f) const { (f == kSupportsSavingDuringRuntime); } -GameList SkyMetaEngine::getSupportedGames() const { - GameList games; +PlainGameList SkyMetaEngine::getSupportedGames() const { + PlainGameList games; games.push_back(skySetting); return games; } @@ -135,14 +135,14 @@ const ExtraGuiOptions SkyMetaEngine::getExtraGuiOptions(const Common::String &ta return options; } -GameDescriptor SkyMetaEngine::findGame(const char *gameid) const { +PlainGameDescriptor SkyMetaEngine::findGame(const char *gameid) const { if (0 == scumm_stricmp(gameid, skySetting.gameId)) return skySetting; - return GameDescriptor(); + return PlainGameDescriptor::empty(); } -GameList SkyMetaEngine::detectGames(const Common::FSList &fslist) const { - GameList detectedGames; +DetectedGames SkyMetaEngine::detectGames(const Common::FSList &fslist) const { + DetectedGames detectedGames; bool hasSkyDsk = false; bool hasSkyDnr = false; int dinnerTableEntries = -1; @@ -173,18 +173,25 @@ GameList SkyMetaEngine::detectGames(const Common::FSList &fslist) const { // Match found, add to list of candidates, then abort inner loop. // The game detector uses US English by default. We want British // English to match the recorded voices better. - GameDescriptor dg(skySetting.gameId, skySetting.description, Common::UNK_LANG, Common::kPlatformUnknown); const SkyVersion *sv = skyVersions; while (sv->dinnerTableEntries) { if (dinnerTableEntries == sv->dinnerTableEntries && (sv->dataDiskSize == dataDiskSize || sv->dataDiskSize == -1)) { - dg.updateDesc(Common::String::format("v0.0%d %s", sv->version, sv->extraDesc).c_str()); - dg.setGUIOptions(sv->guioptions); break; } ++sv; } - detectedGames.push_back(dg); + + if (sv->dinnerTableEntries) { + Common::String extra = Common::String::format("v0.0%d %s", sv->version, sv->extraDesc); + + DetectedGame game = DetectedGame(skySetting.gameId, skySetting.description, Common::UNK_LANG, Common::kPlatformUnknown, extra); + game.setGUIOptions(sv->guioptions); + + detectedGames.push_back(game); + } else { + detectedGames.push_back(DetectedGame(skySetting.gameId, skySetting.description)); + } } return detectedGames; diff --git a/engines/sludge/backdrop.cpp b/engines/sludge/backdrop.cpp index c1042c7f05..0e41230f36 100644 --- a/engines/sludge/backdrop.cpp +++ b/engines/sludge/backdrop.cpp @@ -422,6 +422,7 @@ void GraphicsManager::saveLightMap(Common::WriteStream *stream) { stream->writeByte(0); } stream->writeByte(_lightMapMode); + stream->writeByte(_fadeMode); } bool GraphicsManager::loadLightMap(int ssgVersion, Common::SeekableReadStream *stream) { @@ -434,6 +435,8 @@ bool GraphicsManager::loadLightMap(int ssgVersion, Common::SeekableReadStream *s _lightMapMode = stream->readByte() % 3; } + _fadeMode = stream->readByte(); + return true; } @@ -504,6 +507,27 @@ void GraphicsManager::saveHSI(Common::WriteStream *stream) { Image::writePNG(*stream, _backdropSurface); } +void GraphicsManager::saveBackdrop(Common::WriteStream *stream) { + stream->writeUint16BE(_cameraX); + stream->writeUint16BE(_cameraY); + stream->writeFloatLE(_cameraZoom); + stream->writeByte(_brightnessLevel); + saveHSI(stream); +} + +void GraphicsManager::loadBackdrop(int ssgVersion, Common::SeekableReadStream *stream) { + _cameraX = stream->readUint16BE(); + _cameraY = stream->readUint16BE(); + if (ssgVersion >= VERSION(2, 0)) { + _cameraZoom = stream->readFloatLE(); + } else { + _cameraZoom = 1.0; + } + + _brightnessLevel = stream->readByte(); + + loadHSI(stream, 0, 0, true); +} bool GraphicsManager::getRGBIntoStack(uint x, uint y, StackHandler *sH) { if (x >= _sceneWidth || y >= _sceneHeight) { @@ -516,14 +540,14 @@ bool GraphicsManager::getRGBIntoStack(uint x, uint y, StackHandler *sH) { byte *target = (byte *)_renderSurface.getBasePtr(x, y); - setVariable(newValue, SVT_INT, target[1]); + newValue.setVariable(SVT_INT, target[1]); if (!addVarToStackQuick(newValue, sH->first)) return false; sH->last = sH->first; - setVariable(newValue, SVT_INT, target[2]); + newValue.setVariable(SVT_INT, target[2]); if (!addVarToStackQuick(newValue, sH->first)) return false; - setVariable(newValue, SVT_INT, target[3]); + newValue.setVariable(SVT_INT, target[3]); if (!addVarToStackQuick(newValue, sH->first)) return false; return true; diff --git a/engines/sludge/bg_effects.cpp b/engines/sludge/bg_effects.cpp index d0c8068a27..3f40fc81e1 100644 --- a/engines/sludge/bg_effects.cpp +++ b/engines/sludge/bg_effects.cpp @@ -166,7 +166,7 @@ bool blur_createSettings(int numParams, VariableStack *&stack) { error = "Third and subsequent parameters in setBackgroundEffect should be arrays"; break; } else { - int w = stackSize(justToCheckSizes->thisVar.varData.theStack); + int w = justToCheckSizes->thisVar.varData.theStack->getStackSize(); if (a) { if (w != width) { error = "Arrays in setBackgroundEffect must be the same size"; @@ -196,7 +196,7 @@ bool blur_createSettings(int numParams, VariableStack *&stack) { for (int x = 0; x < width; x++) { int arraySlot = x + (y * width); // s_matrixEffectData[arraySlot] = (rand() % 4); - if (!getValueType(s_matrixEffectData[arraySlot], SVT_INT, eachNumber->thisVar)) { + if (!eachNumber->thisVar.getValueType(s_matrixEffectData[arraySlot], SVT_INT)) { error = ""; break; } @@ -205,10 +205,10 @@ bool blur_createSettings(int numParams, VariableStack *&stack) { trimStack(stack); } } - if (error.empty() && !getValueType(s_matrixEffectDivide, SVT_INT, stack->thisVar)) + if (error.empty() && !stack->thisVar.getValueType(s_matrixEffectDivide, SVT_INT)) error = ""; trimStack(stack); - if (error.empty() && !getValueType(s_matrixEffectBase, SVT_INT, stack->thisVar)) + if (error.empty() && !stack->thisVar.getValueType(s_matrixEffectBase, SVT_INT)) error = ""; trimStack(stack); if (error.empty()) { diff --git a/engines/sludge/builtin.cpp b/engines/sludge/builtin.cpp index 7385d4d861..1030643788 100644 --- a/engines/sludge/builtin.cpp +++ b/engines/sludge/builtin.cpp @@ -32,6 +32,7 @@ #include "sludge/floor.h" #include "sludge/fonttext.h" #include "sludge/freeze.h" +#include "sludge/function.h" #include "sludge/graphics.h" #include "sludge/language.h" #include "sludge/loadsave.h" @@ -54,51 +55,16 @@ namespace Sludge { -int speechMode = 0; -SpritePalette pastePalette; - Variable *launchResult = NULL; -extern int lastFramesPerSecond, thumbWidth, thumbHeight; extern bool allowAnyFilename; -extern bool captureAllKeys; extern VariableStack *noStack; extern StatusStuff *nowStatus; -extern ScreenRegion *overRegion; extern int numBIFNames, numUserFunc; extern Common::String *allUserFunc; extern Common::String *allBIFNames; -extern byte brightnessLevel; -extern byte fadeMode; -extern uint16 saveEncoding; - -int paramNum[] = { -1, 0, 1, 1, -1, -1, 1, 3, 4, 1, 0, 0, 8, -1, // SAY->MOVEMOUSE - -1, 0, 0, -1, -1, 1, 1, 1, 1, 4, 1, 1, 2, 1,// FOCUS->REMOVEREGION - 2, 2, 0, 0, 2, // ANIMATE->SETSCALE - -1, 2, 1, 0, 0, 0, 1, 0, 3, // new/push/pop stack, status stuff - 2, 0, 0, 3, 1, 0, 2, // delFromStack->completeTimers - -1, -1, -1, 2, 2, 0, 3, 1, // anim, costume, pO, setC, wait, sS, substring, stringLength - 0, 1, 1, 0, 2, // dark, save, load, quit, rename - 1, 3, 3, 1, 2, 1, 1, 3, 1, 0, 0, 2, 1, // stackSize, pasteString, startMusic, defvol, vol, stopmus, stopsound, setfont, alignStatus, show x 2, pos'Status, setFloor - -1, -1, 1, 1, 2, 1, 1, 1, -1, -1, -1, 1, 1, // force, jump, peekstart, peekend, enqueue, getSavedGames, inFont, loopSound, removeChar, stopCharacter - 1, 0, 3, 3, 1, 2, 1, 2, 2, // launch, howFrozen, pastecol, litcol, checksaved, float, cancelfunc, walkspeed, delAll - 2, 3, 1, 2, 2, 0, 0, 1, 2, 3, 1, -1, // extras, mixoverlay, pastebloke, getMScreenX/Y, setSound(Default/-)Volume, looppoints, speechMode, setLightMap - -1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, // think, getCharacterDirection, is(char/region/moving), deleteGame, renameGame, hardScroll, stringWidth, speechSpeed, normalCharacter - 2, 1, 2, 1, 3, 1, 1, 2, 1, // fetchEvent, setBrightness, spin, fontSpace, burnString, captureAll, cacheSound, setSpinSpeed, transitionMode - 1, 0, 0, 1, 0, 2, 1, 1, 1, // movie(Start/Abort/Playing), updateDisplay, getSoundCache, savedata, loaddata, savemode, freeSound - 3, 0, 3, 3, 2, 1, 1, // setParallax, clearParallax, setBlankColour, setBurnColour, getPixelColour, makeFastArray, getCharacterScale - 0, 2, 0, // getLanguage, launchWith, getFramesPerSecond - 3, 2, 2, 0, 0, 1, // readThumbnail, setThumbnailSize, hasFlag, snapshot, clearSnapshot, anyFilename - 2, 1, // regGet, fatal - 4, 3, -1, 0, // chr AA, max AA, setBackgroundEffect, doBackgroundEffect - 2, // setCharacterAngleOffset - 2, 5, // setCharacterTransparency, setCharacterColourise - 1, // zoomCamera - 1, 0, 0 // playMovie, stopMovie, pauseMovie - }; - bool failSecurityCheck(const Common::String &fn) { if (fn.empty()) return true; @@ -121,11 +87,12 @@ bool failSecurityCheck(const Common::String &fn) { return false; } -LoadedFunction *saverFunc; +extern LoadedFunction *saverFunc; typedef BuiltReturn (*builtInSludgeFunc)(int numParams, LoadedFunction *fun); struct builtInFunctionData { builtInSludgeFunc func; + int paramNum; }; #define builtIn(a) static BuiltReturn builtIn_ ## a (int numParams, LoadedFunction *fun) @@ -140,15 +107,15 @@ static BuiltReturn sayCore(int numParams, LoadedFunction *fun, bool sayIt) { switch (numParams) { case 3: - if (!getValueType(fileNum, SVT_FILE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(fileNum, SVT_FILE)) return BR_ERROR; trimStack(fun->stack); // fall through case 2: - newText = getTextFromAnyVar(fun->stack->thisVar); + newText = fun->stack->thisVar.getTextFromAnyVar(); trimStack(fun->stack); - if (!getValueType(objT, SVT_OBJTYPE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(objT, SVT_OBJTYPE)) return BR_ERROR; trimStack(fun->stack); p = g_sludge->_speechMan->wrapSpeech(newText, objT, fileNum, sayIt); @@ -192,13 +159,13 @@ builtIn(unfreeze) { builtIn(howFrozen) { UNUSEDALL - setVariable(fun->reg, SVT_INT, g_sludge->_gfxMan->howFrozen()); + fun->reg.setVariable(SVT_INT, g_sludge->_gfxMan->howFrozen()); return BR_CONTINUE; } builtIn(setCursor) { UNUSEDALL - PersonaAnimation *aa = getAnimationFromVar(fun->stack->thisVar); + PersonaAnimation *aa = fun->stack->thisVar.getAnimationFromVar(); g_sludge->_cursorMan->pickAnimCursor(aa); trimStack(fun->stack); return BR_CONTINUE; @@ -206,39 +173,39 @@ builtIn(setCursor) { builtIn(getMouseX) { UNUSEDALL - setVariable(fun->reg, SVT_INT, g_sludge->_evtMan->mouseX() + g_sludge->_gfxMan->getCamX()); + fun->reg.setVariable(SVT_INT, g_sludge->_evtMan->mouseX() + g_sludge->_gfxMan->getCamX()); return BR_CONTINUE; } builtIn(getMouseY) { UNUSEDALL - setVariable(fun->reg, SVT_INT, g_sludge->_evtMan->mouseY() + g_sludge->_gfxMan->getCamY()); + fun->reg.setVariable(SVT_INT, g_sludge->_evtMan->mouseY() + g_sludge->_gfxMan->getCamY()); return BR_CONTINUE; } builtIn(getMouseScreenX) { UNUSEDALL - setVariable(fun->reg, SVT_INT, g_sludge->_evtMan->mouseX() * g_sludge->_gfxMan->getCamZoom()); + fun->reg.setVariable(SVT_INT, g_sludge->_evtMan->mouseX() * g_sludge->_gfxMan->getCamZoom()); return BR_CONTINUE; } builtIn(getMouseScreenY) { UNUSEDALL - setVariable(fun->reg, SVT_INT, g_sludge->_evtMan->mouseY() * g_sludge->_gfxMan->getCamZoom()); + fun->reg.setVariable(SVT_INT, g_sludge->_evtMan->mouseY() * g_sludge->_gfxMan->getCamZoom()); return BR_CONTINUE; } builtIn(getStatusText) { UNUSEDALL - makeTextVar(fun->reg, statusBarText()); + fun->reg.makeTextVar(statusBarText()); return BR_CONTINUE; } builtIn(getMatchingFiles) { UNUSEDALL - Common::String newText = getTextFromAnyVar(fun->stack->thisVar); + Common::String newText = fun->stack->thisVar.getTextFromAnyVar(); trimStack(fun->stack); - unlinkVar(fun->reg); + fun->reg.unlinkVar(); // Return value fun->reg.varType = SVT_STACK; @@ -248,7 +215,7 @@ builtIn(getMatchingFiles) { fun->reg.varData.theStack->first = NULL; fun->reg.varData.theStack->last = NULL; fun->reg.varData.theStack->timesUsed = 1; - if (!getSavedGamesStack(fun->reg.varData.theStack, newText)) + if (!fun->reg.varData.theStack->getSavedGamesStack(newText)) return BR_ERROR; return BR_CONTINUE; } @@ -260,7 +227,7 @@ builtIn(saveGame) { fatal("Can't save game state while the engine is frozen"); } - g_sludge->loadNow = getTextFromAnyVar(fun->stack->thisVar); + g_sludge->loadNow = fun->stack->thisVar.getTextFromAnyVar(); trimStack(fun->stack); Common::String aaaaa = encodeFilename(g_sludge->loadNow); @@ -270,14 +237,14 @@ builtIn(saveGame) { g_sludge->loadNow = ":" + aaaaa; - setVariable(fun->reg, SVT_INT, 0); + fun->reg.setVariable(SVT_INT, 0); saverFunc = fun; return BR_KEEP_AND_PAUSE; } builtIn(fileExists) { UNUSEDALL - g_sludge->loadNow = getTextFromAnyVar(fun->stack->thisVar); + g_sludge->loadNow = fun->stack->thisVar.getTextFromAnyVar(); trimStack(fun->stack); Common::String aaaaa = encodeFilename(g_sludge->loadNow); g_sludge->loadNow.clear(); @@ -300,13 +267,13 @@ builtIn(fileExists) { } // Return value - setVariable(fun->reg, SVT_INT, exist); + fun->reg.setVariable(SVT_INT, exist); return BR_CONTINUE; } builtIn(loadGame) { UNUSEDALL - Common::String aaaaa = getTextFromAnyVar(fun->stack->thisVar); + Common::String aaaaa = fun->stack->thisVar.getTextFromAnyVar(); trimStack(fun->stack); g_sludge->loadNow.clear(); g_sludge->loadNow = encodeFilename(aaaaa); @@ -340,16 +307,16 @@ builtIn(blankScreen) { builtIn(blankArea) { UNUSEDALL int x1, y1, x2, y2; - if (!getValueType(y2, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(y2, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(x2, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(x2, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(y1, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(y1, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(x1, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(x1, SVT_INT)) return BR_ERROR; trimStack(fun->stack); g_sludge->_gfxMan->blankScreen(x1, y1, x2, y2); @@ -365,13 +332,13 @@ builtIn(darkBackground) { builtIn(addOverlay) { UNUSEDALL int fileNumber, xPos, yPos; - if (!getValueType(yPos, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(yPos, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(xPos, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(xPos, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(fileNumber, SVT_FILE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(fileNumber, SVT_FILE)) return BR_ERROR; trimStack(fun->stack); g_sludge->_gfxMan->loadBackDrop(fileNumber, xPos, yPos); @@ -381,13 +348,13 @@ builtIn(addOverlay) { builtIn(mixOverlay) { UNUSEDALL int fileNumber, xPos, yPos; - if (!getValueType(yPos, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(yPos, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(xPos, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(xPos, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(fileNumber, SVT_FILE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(fileNumber, SVT_FILE)) return BR_ERROR; trimStack(fun->stack); g_sludge->_gfxMan->mixBackDrop(fileNumber, xPos, yPos); @@ -397,13 +364,13 @@ builtIn(mixOverlay) { builtIn(pasteImage) { UNUSEDALL int x, y; - if (!getValueType(y, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(y, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(x, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(x, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - PersonaAnimation *pp = getAnimationFromVar(fun->stack->thisVar); + PersonaAnimation *pp = fun->stack->thisVar.getAnimationFromVar(); trimStack(fun->stack); if (pp == NULL) return BR_CONTINUE; @@ -418,10 +385,10 @@ builtIn(pasteImage) { builtIn(setSceneDimensions) { UNUSEDALL int x, y; - if (!getValueType(y, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(y, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(x, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(x, SVT_INT)) return BR_ERROR; trimStack(fun->stack); if (g_sludge->_gfxMan->killResizeBackdrop(x, y)) { @@ -435,10 +402,10 @@ builtIn(setSceneDimensions) { builtIn(aimCamera) { UNUSEDALL int cameraX, cameraY; - if (!getValueType(cameraY, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(cameraY, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(cameraX, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(cameraX, SVT_INT)) return BR_ERROR; trimStack(fun->stack); @@ -450,7 +417,7 @@ builtIn(aimCamera) { builtIn(zoomCamera) { UNUSEDALL int z; - if (!getValueType(z, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(z, SVT_INT)) return BR_ERROR; trimStack(fun->stack); @@ -476,7 +443,7 @@ builtIn(pickOne) { // Return value while (numParams--) { if (i == numParams) - copyVariable(fun->stack->thisVar, fun->reg); + fun->reg.copyFrom(fun->stack->thisVar); trimStack(fun->stack); } return BR_CONTINUE; @@ -489,13 +456,13 @@ builtIn(substring) { //debugOut ("BUILTIN: substring\n"); - if (!getValueType(length, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(length, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(start, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(start, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - wholeString = getTextFromAnyVar(fun->stack->thisVar); + wholeString = fun->stack->thisVar.getTextFromAnyVar(); trimStack(fun->stack); UTF8Converter convert; @@ -517,21 +484,21 @@ builtIn(substring) { Common::String newString(wholeString.begin() + startoffset, wholeString.begin() + endoffset); - makeTextVar(fun->reg, newString); + fun->reg.makeTextVar(newString); return BR_CONTINUE; } builtIn(stringLength) { UNUSEDALL - Common::String newText = getTextFromAnyVar(fun->stack->thisVar); + Common::String newText = fun->stack->thisVar.getTextFromAnyVar(); trimStack(fun->stack); - setVariable(fun->reg, SVT_INT, g_sludge->_txtMan->stringLength(newText)); + fun->reg.setVariable(SVT_INT, g_sludge->_txtMan->stringLength(newText)); return BR_CONTINUE; } builtIn(newStack) { UNUSEDALL - unlinkVar(fun->reg); + fun->reg.unlinkVar(); // Return value fun->reg.varType = SVT_STACK; @@ -560,13 +527,13 @@ builtIn(stackSize) { switch (fun->stack->thisVar.varType) { case SVT_STACK: // Return value - setVariable(fun->reg, SVT_INT, stackSize(fun->stack->thisVar.varData.theStack)); + fun->reg.setVariable(SVT_INT, fun->stack->thisVar.varData.theStack->getStackSize()); trimStack(fun->stack); return BR_CONTINUE; case SVT_FASTARRAY: // Return value - setVariable(fun->reg, SVT_INT, fun->stack->thisVar.varData.fastArray->size); + fun->reg.setVariable(SVT_INT, fun->stack->thisVar.varData.fastArray->size); trimStack(fun->stack); return BR_CONTINUE; @@ -584,7 +551,7 @@ builtIn(copyStack) { return BR_ERROR; } // Return value - if (!copyStack(fun->stack->thisVar, fun->reg)) + if (!fun->reg.copyStack(fun->stack->thisVar)) return BR_ERROR; trimStack(fun->stack); return BR_CONTINUE; @@ -639,10 +606,11 @@ builtIn(deleteFromStack) { } // Return value - setVariable(fun->reg, SVT_INT, deleteVarFromStack(fun->stack->thisVar, fun->stack->next->thisVar.varData.theStack->first, false)); + fun->reg.setVariable(SVT_INT, deleteVarFromStack(fun->stack->thisVar, fun->stack->next->thisVar.varData.theStack->first, false)); // Horrible hacking because 'last' value might now be wrong! - fun->stack->next->thisVar.varData.theStack->last = stackFindLast(fun->stack->next->thisVar.varData.theStack->first); + VariableStack *nextFirstStack = fun->stack->next->thisVar.varData.theStack->first; + fun->stack->next->thisVar.varData.theStack->last = (nextFirstStack == NULL) ? NULL : nextFirstStack->stackFindLast(); trimStack(fun->stack); trimStack(fun->stack); @@ -657,10 +625,11 @@ builtIn(deleteAllFromStack) { } // Return value - setVariable(fun->reg, SVT_INT, deleteVarFromStack(fun->stack->thisVar, fun->stack->next->thisVar.varData.theStack->first, true)); + fun->reg.setVariable(SVT_INT, deleteVarFromStack(fun->stack->thisVar, fun->stack->next->thisVar.varData.theStack->first, true)); // Horrible hacking because 'last' value might now be wrong! - fun->stack->next->thisVar.varData.theStack->last = stackFindLast(fun->stack->next->thisVar.varData.theStack->first); + VariableStack *nextFirstStack = fun->stack->next->thisVar.varData.theStack->first; + fun->stack->next->thisVar.varData.theStack->last = (nextFirstStack == NULL) ? NULL : nextFirstStack->stackFindLast(); trimStack(fun->stack); trimStack(fun->stack); @@ -679,7 +648,7 @@ builtIn(popFromStack) { } // Return value - copyVariable(fun->stack->thisVar.varData.theStack->first->thisVar, fun->reg); + fun->reg.copyFrom(fun->stack->thisVar.varData.theStack->first->thisVar); trimStack(fun->stack->thisVar.varData.theStack->first); trimStack(fun->stack); return BR_CONTINUE; @@ -697,7 +666,7 @@ builtIn(peekStart) { } // Return value - copyVariable(fun->stack->thisVar.varData.theStack->first->thisVar, fun->reg); + fun->reg.copyFrom(fun->stack->thisVar.varData.theStack->first->thisVar); trimStack(fun->stack); return BR_CONTINUE; } @@ -714,7 +683,7 @@ builtIn(peekEnd) { } // Return value - copyVariable(fun->stack->thisVar.varData.theStack->last->thisVar, fun->reg); + fun->reg.copyFrom(fun->stack->thisVar.varData.theStack->last->thisVar); trimStack(fun->stack); return BR_CONTINUE; } @@ -723,24 +692,24 @@ builtIn(random) { UNUSEDALL int num; - if (!getValueType(num, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(num, SVT_INT)) return BR_ERROR; trimStack(fun->stack); if (num <= 0) num = 1; - setVariable(fun->reg, SVT_INT, 0 /*rand() % num*/); //TODO:false value + fun->reg.setVariable(SVT_INT, 0 /*rand() % num*/); //TODO:false value return BR_CONTINUE; } static bool getRGBParams(int &red, int &green, int &blue, LoadedFunction *fun) { - if (!getValueType(blue, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(blue, SVT_INT)) return false; trimStack(fun->stack); - if (!getValueType(green, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(green, SVT_INT)) return false; trimStack(fun->stack); - if (!getValueType(red, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(red, SVT_INT)) return false; trimStack(fun->stack); return true; @@ -775,7 +744,7 @@ builtIn(setPasteColour) { if (!getRGBParams(red, green, blue, fun)) return BR_ERROR; - setFontColour(pastePalette, (byte)red, (byte)green, (byte)blue); + g_sludge->_txtMan->setPasterColor((byte)red, (byte)green, (byte)blue); return BR_CONTINUE; } @@ -787,7 +756,7 @@ builtIn(setBlankColour) { return BR_ERROR; g_sludge->_gfxMan->setBlankColor(red, green, blue); - setVariable(fun->reg, SVT_INT, 1); + fun->reg.setVariable(SVT_INT, 1); return BR_CONTINUE; } @@ -799,21 +768,21 @@ builtIn(setBurnColour) { return BR_ERROR; g_sludge->_gfxMan->setBurnColor(red, green, blue); - setVariable(fun->reg, SVT_INT, 1); + fun->reg.setVariable(SVT_INT, 1); return BR_CONTINUE; } builtIn(setFont) { UNUSEDALL int fileNumber, newHeight; - if (!getValueType(newHeight, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(newHeight, SVT_INT)) return BR_ERROR; // newDebug (" Height:", newHeight); trimStack(fun->stack); - Common::String newText = getTextFromAnyVar(fun->stack->thisVar); + Common::String newText = fun->stack->thisVar.getTextFromAnyVar(); // newDebug (" Character supported:", newText); trimStack(fun->stack); - if (!getValueType(fileNumber, SVT_FILE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(fileNumber, SVT_FILE)) return BR_ERROR; // newDebug (" File:", fileNumber); trimStack(fun->stack); @@ -825,28 +794,28 @@ builtIn(setFont) { builtIn(inFont) { UNUSEDALL - Common::String newText = getTextFromAnyVar(fun->stack->thisVar); + Common::String newText = fun->stack->thisVar.getTextFromAnyVar(); trimStack(fun->stack); // Return value - setVariable(fun->reg, SVT_INT, g_sludge->_txtMan->isInFont(newText)); + fun->reg.setVariable(SVT_INT, g_sludge->_txtMan->isInFont(newText)); return BR_CONTINUE; } builtIn(pasteString) { UNUSEDALL - Common::String newText = getTextFromAnyVar(fun->stack->thisVar); + Common::String newText = fun->stack->thisVar.getTextFromAnyVar(); trimStack(fun->stack); int y, x; - if (!getValueType(y, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(y, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(x, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(x, SVT_INT)) return BR_ERROR; trimStack(fun->stack); if (x == IN_THE_CENTRE) x = g_sludge->_gfxMan->getCenterX(g_sludge->_txtMan->stringWidth(newText)); - g_sludge->_txtMan->pasteStringToBackdrop(newText, x, y, pastePalette); + g_sludge->_txtMan->pasteStringToBackdrop(newText, x, y); return BR_CONTINUE; } @@ -858,11 +827,11 @@ builtIn(anim) { } // First store the frame numbers and take 'em off the stack - PersonaAnimation *ba = createPersonaAnim(numParams - 1, fun->stack); + PersonaAnimation *ba = new PersonaAnimation(numParams - 1, fun->stack); // Only remaining paramter is the file number int fileNumber; - if (!getValueType(fileNumber, SVT_FILE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(fileNumber, SVT_FILE)) return BR_ERROR; trimStack(fun->stack); @@ -870,10 +839,10 @@ builtIn(anim) { LoadedSpriteBank *sprBanky = g_sludge->_gfxMan->loadBankForAnim(fileNumber); if (!sprBanky) return BR_ERROR; // File not found, fatal done already - setBankFile(ba, sprBanky); + ba->theSprites = sprBanky; // Return value - newAnimationVariable(fun->reg, ba); + fun->reg.makeAnimationVariable(ba); return BR_CONTINUE; } @@ -893,18 +862,18 @@ builtIn(costume) { if (!checkNew(newPersona->animation)) return BR_ERROR; for (iii = numParams - 1; iii >= 0; iii--) { - newPersona->animation[iii] = getAnimationFromVar(fun->stack->thisVar); + newPersona->animation[iii] = fun->stack->thisVar.getAnimationFromVar(); trimStack(fun->stack); } // Return value - newCostumeVariable(fun->reg, newPersona); + fun->reg.makeCostumeVariable(newPersona); return BR_CONTINUE; } builtIn(launch) { UNUSEDALL - Common::String newTextA = getTextFromAnyVar(fun->stack->thisVar); + Common::String newTextA = fun->stack->thisVar.getTextFromAnyVar(); Common::String newText = encodeFilename(newTextA); @@ -922,7 +891,7 @@ builtIn(launch) { if (g_sludge->launchMe.empty()) return BR_ERROR; } - setVariable(fun->reg, SVT_INT, 1); + fun->reg.setVariable(SVT_INT, 1); launchResult = &fun->reg; return BR_KEEP_AND_PAUSE; @@ -931,7 +900,7 @@ builtIn(launch) { builtIn(pause) { UNUSEDALL int theTime; - if (!getValueType(theTime, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(theTime, SVT_INT)) return BR_ERROR; trimStack(fun->stack); if (theTime > 0) { @@ -951,10 +920,10 @@ builtIn(completeTimers) { builtIn(callEvent) { UNUSEDALL int obj1, obj2; - if (!getValueType(obj2, SVT_OBJTYPE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(obj2, SVT_OBJTYPE)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(obj1, SVT_OBJTYPE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(obj1, SVT_OBJTYPE)) return BR_ERROR; trimStack(fun->stack); @@ -962,10 +931,10 @@ builtIn(callEvent) { // Return value if (fNum) { - setVariable(fun->reg, SVT_FUNC, fNum); + fun->reg.setVariable(SVT_FUNC, fNum); return BR_CALLAFUNC; } - setVariable(fun->reg, SVT_INT, 0); + fun->reg.setVariable(SVT_INT, 0); return BR_CONTINUE; } @@ -987,13 +956,13 @@ builtIn(_rem_movieStart) { builtIn(_rem_movieAbort) { UNUSEDALL - setVariable(fun->reg, SVT_INT, 0); + fun->reg.setVariable(SVT_INT, 0); return BR_CONTINUE; } builtIn(_rem_moviePlaying) { UNUSEDALL - setVariable(fun->reg, SVT_INT, 0); + fun->reg.setVariable(SVT_INT, 0); return BR_CONTINUE; } @@ -1004,13 +973,13 @@ builtIn(playMovie) { if (movieIsPlaying) return BR_PAUSE; - if (!getValueType(fileNumber, SVT_FILE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(fileNumber, SVT_FILE)) return BR_ERROR; trimStack(fun->stack); r = playMovie(fileNumber); - setVariable(fun->reg, SVT_INT, r); + fun->reg.setVariable(SVT_INT, r); if (r && (!fun->next)) { restartFunction(fun); @@ -1024,7 +993,7 @@ builtIn(stopMovie) { stopMovie(); - setVariable(fun->reg, SVT_INT, 0); + fun->reg.setVariable(SVT_INT, 0); return BR_CONTINUE; } @@ -1033,7 +1002,7 @@ builtIn(pauseMovie) { pauseMovie(); - setVariable(fun->reg, SVT_INT, 0); + fun->reg.setVariable(SVT_INT, 0); return BR_CONTINUE; } @@ -1043,13 +1012,13 @@ builtIn(pauseMovie) { builtIn(startMusic) { UNUSEDALL int fromTrack, musChan, fileNumber; - if (!getValueType(fromTrack, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(fromTrack, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(musChan, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(musChan, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(fileNumber, SVT_FILE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(fileNumber, SVT_FILE)) return BR_ERROR; trimStack(fun->stack); if (!g_sludge->_soundMan->playMOD(fileNumber, musChan, fromTrack)) @@ -1060,7 +1029,7 @@ builtIn(startMusic) { builtIn(stopMusic) { UNUSEDALL int v; - if (!getValueType(v, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(v, SVT_INT)) return BR_ERROR; trimStack(fun->stack); g_sludge->_soundMan->stopMOD(v); @@ -1070,10 +1039,10 @@ builtIn(stopMusic) { builtIn(setMusicVolume) { UNUSEDALL int musChan, v; - if (!getValueType(v, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(v, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(musChan, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(musChan, SVT_INT)) return BR_ERROR; trimStack(fun->stack); g_sludge->_soundMan->setMusicVolume(musChan, v); @@ -1083,7 +1052,7 @@ builtIn(setMusicVolume) { builtIn(setDefaultMusicVolume) { UNUSEDALL int v; - if (!getValueType(v, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(v, SVT_INT)) return BR_ERROR; trimStack(fun->stack); g_sludge->_soundMan->setDefaultMusicVolume(v); @@ -1093,7 +1062,7 @@ builtIn(setDefaultMusicVolume) { builtIn(playSound) { UNUSEDALL int fileNumber; - if (!getValueType(fileNumber, SVT_FILE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(fileNumber, SVT_FILE)) return BR_ERROR; trimStack(fun->stack); if (!g_sludge->_soundMan->startSound(fileNumber, false)) @@ -1109,7 +1078,7 @@ builtIn(loopSound) { return BR_ERROR; } else if (numParams < 2) { - if (!getValueType(fileNumber, SVT_FILE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(fileNumber, SVT_FILE)) return BR_ERROR; trimStack(fun->stack); if (!g_sludge->_soundMan->startSound(fileNumber, true)) @@ -1124,12 +1093,12 @@ builtIn(loopSound) { // Should we loop? if (fun->stack->thisVar.varType != SVT_FILE) { - getValueType(doLoop, SVT_INT, fun->stack->thisVar); + fun->stack->thisVar.getValueType(doLoop, SVT_INT); trimStack(fun->stack); numParams--; } while (numParams) { - if (!getValueType(fileNumber, SVT_FILE, fun->stack->thisVar)) { + if (!fun->stack->thisVar.getValueType(fileNumber, SVT_FILE)) { fatal("Illegal parameter given built-in function loopSound()."); return BR_ERROR; } @@ -1162,7 +1131,7 @@ builtIn(loopSound) { builtIn(stopSound) { UNUSEDALL int v; - if (!getValueType(v, SVT_FILE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(v, SVT_FILE)) return BR_ERROR; trimStack(fun->stack); g_sludge->_soundMan->huntKillSound(v); @@ -1172,7 +1141,7 @@ builtIn(stopSound) { builtIn(setDefaultSoundVolume) { UNUSEDALL int v; - if (!getValueType(v, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(v, SVT_INT)) return BR_ERROR; trimStack(fun->stack); g_sludge->_soundMan->setDefaultSoundVolume(v); @@ -1182,10 +1151,10 @@ builtIn(setDefaultSoundVolume) { builtIn(setSoundVolume) { UNUSEDALL int musChan, v; - if (!getValueType(v, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(v, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(musChan, SVT_FILE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(musChan, SVT_FILE)) return BR_ERROR; trimStack(fun->stack); g_sludge->_soundMan->setSoundVolume(musChan, v); @@ -1195,13 +1164,13 @@ builtIn(setSoundVolume) { builtIn(setSoundLoopPoints) { UNUSEDALL int musChan, theEnd, theStart; - if (!getValueType(theEnd, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(theEnd, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(theStart, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(theStart, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(musChan, SVT_FILE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(musChan, SVT_FILE)) return BR_ERROR; trimStack(fun->stack); g_sludge->_soundMan->setSoundLoop(musChan, theStart, theEnd); @@ -1215,20 +1184,20 @@ builtIn(setFloor) { UNUSEDALL if (fun->stack->thisVar.varType == SVT_FILE) { int v; - getValueType(v, SVT_FILE, fun->stack->thisVar); + fun->stack->thisVar.getValueType(v, SVT_FILE); trimStack(fun->stack); - if (!setFloor(v)) + if (!g_sludge->_floorMan->setFloor(v)) return BR_ERROR; } else { trimStack(fun->stack); - setFloorNull(); + g_sludge->_floorMan->setFloorNull(); } return BR_CONTINUE; } builtIn(showFloor) { UNUSEDALL - drawFloor(); + g_sludge->_floorMan->drawFloor(); return BR_CONTINUE; } @@ -1236,7 +1205,7 @@ builtIn(setZBuffer) { UNUSEDALL if (fun->stack->thisVar.varType == SVT_FILE) { int v; - getValueType(v, SVT_FILE, fun->stack->thisVar); + fun->stack->thisVar.getValueType(v, SVT_FILE); trimStack(fun->stack); if (!g_sludge->_gfxMan->setZBuffer(v)) return BR_ERROR; @@ -1251,7 +1220,7 @@ builtIn(setLightMap) { UNUSEDALL switch (numParams) { case 2: - if (!getValueType(g_sludge->_gfxMan->_lightMapMode, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(g_sludge->_gfxMan->_lightMapMode, SVT_INT)) return BR_ERROR; trimStack(fun->stack); g_sludge->_gfxMan->_lightMapMode %= LIGHTMAPMODE_NUM; @@ -1260,15 +1229,15 @@ builtIn(setLightMap) { case 1: if (fun->stack->thisVar.varType == SVT_FILE) { int v; - getValueType(v, SVT_FILE, fun->stack->thisVar); + fun->stack->thisVar.getValueType(v, SVT_FILE); trimStack(fun->stack); if (!g_sludge->_gfxMan->loadLightMap(v)) return BR_ERROR; - setVariable(fun->reg, SVT_INT, 1); + fun->reg.setVariable(SVT_INT, 1); } else { trimStack(fun->stack); g_sludge->_gfxMan->killLightMap(); - setVariable(fun->reg, SVT_INT, 0); + fun->reg.setVariable(SVT_INT, 0); } break; @@ -1284,13 +1253,15 @@ builtIn(setLightMap) { builtIn(setSpeechMode) { UNUSEDALL - if (!getValueType(speechMode, SVT_INT, fun->stack->thisVar)) + int speechMode; + if (!fun->stack->thisVar.getValueType(speechMode, SVT_INT)) return BR_ERROR; trimStack(fun->stack); if (speechMode < 0 || speechMode > 2) { fatal("Valid parameters are be SPEECHANDTEXT, SPEECHONLY or TEXTONLY"); return BR_ERROR; } + g_sludge->_speechMan->setSpeechMode(speechMode); return BR_CONTINUE; } @@ -1298,9 +1269,9 @@ builtIn(somethingSpeaking) { UNUSEDALL int i = g_sludge->_speechMan->isThereAnySpeechGoingOn(); if (i == -1) { - setVariable(fun->reg, SVT_INT, 0); + fun->reg.setVariable(SVT_INT, 0); } else { - setVariable(fun->reg, SVT_OBJTYPE, i); + fun->reg.setVariable(SVT_OBJTYPE, i); } return BR_CONTINUE; } @@ -1313,21 +1284,21 @@ builtIn(skipSpeech) { builtIn(getOverObject) { UNUSEDALL - if (overRegion) + if (g_sludge->_regionMan->getOverRegion()) // Return value - setVariable(fun->reg, SVT_OBJTYPE, overRegion->thisType->objectNum); + fun->reg.setVariable(SVT_OBJTYPE, g_sludge->_regionMan->getOverRegion()->thisType->objectNum); else // Return value - setVariable(fun->reg, SVT_INT, 0); + fun->reg.setVariable(SVT_INT, 0); return BR_CONTINUE; } builtIn(rename) { UNUSEDALL - Common::String newText = getTextFromAnyVar(fun->stack->thisVar); + Common::String newText = fun->stack->thisVar.getTextFromAnyVar(); int objT; trimStack(fun->stack); - if (!getValueType(objT, SVT_OBJTYPE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(objT, SVT_OBJTYPE)) return BR_ERROR; trimStack(fun->stack); ObjectType *o = g_sludge->_objMan->findObjectType(objT); @@ -1339,19 +1310,19 @@ builtIn(rename) { builtIn(getObjectX) { UNUSEDALL int objectNumber; - if (!getValueType(objectNumber, SVT_OBJTYPE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(objectNumber, SVT_OBJTYPE)) return BR_ERROR; trimStack(fun->stack); - OnScreenPerson *pers = findPerson(objectNumber); + OnScreenPerson *pers = g_sludge->_peopleMan->findPerson(objectNumber); if (pers) { - setVariable(fun->reg, SVT_INT, pers->x); + fun->reg.setVariable(SVT_INT, pers->x); } else { - ScreenRegion *la = getRegionForObject(objectNumber); + ScreenRegion *la = g_sludge->_regionMan->getRegionForObject(objectNumber); if (la) { - setVariable(fun->reg, SVT_INT, la->sX); + fun->reg.setVariable(SVT_INT, la->sX); } else { - setVariable(fun->reg, SVT_INT, 0); + fun->reg.setVariable(SVT_INT, 0); } } return BR_CONTINUE; @@ -1360,19 +1331,19 @@ builtIn(getObjectX) { builtIn(getObjectY) { UNUSEDALL int objectNumber; - if (!getValueType(objectNumber, SVT_OBJTYPE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(objectNumber, SVT_OBJTYPE)) return BR_ERROR; trimStack(fun->stack); - OnScreenPerson *pers = findPerson(objectNumber); + OnScreenPerson *pers = g_sludge->_peopleMan->findPerson(objectNumber); if (pers) { - setVariable(fun->reg, SVT_INT, pers->y); + fun->reg.setVariable(SVT_INT, pers->y); } else { - ScreenRegion *la = getRegionForObject(objectNumber); + ScreenRegion *la = g_sludge->_regionMan->getRegionForObject(objectNumber); if (la) { - setVariable(fun->reg, SVT_INT, la->sY); + fun->reg.setVariable(SVT_INT, la->sY); } else { - setVariable(fun->reg, SVT_INT, 0); + fun->reg.setVariable(SVT_INT, 0); } } return BR_CONTINUE; @@ -1381,31 +1352,31 @@ builtIn(getObjectY) { builtIn(addScreenRegion) { UNUSEDALL int sX, sY, x1, y1, x2, y2, di, objectNumber; - if (!getValueType(di, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(di, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(sY, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(sY, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(sX, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(sX, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(y2, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(y2, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(x2, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(x2, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(y1, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(y1, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(x1, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(x1, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(objectNumber, SVT_OBJTYPE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(objectNumber, SVT_OBJTYPE)) return BR_ERROR; trimStack(fun->stack); - if (addScreenRegion(x1, y1, x2, y2, sX, sY, di, objectNumber)) + if (g_sludge->_regionMan->addScreenRegion(x1, y1, x2, y2, sX, sY, di, objectNumber)) return BR_CONTINUE; return BR_ERROR; @@ -1414,22 +1385,22 @@ builtIn(addScreenRegion) { builtIn(removeScreenRegion) { UNUSEDALL int objectNumber; - if (!getValueType(objectNumber, SVT_OBJTYPE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(objectNumber, SVT_OBJTYPE)) return BR_ERROR; trimStack(fun->stack); - removeScreenRegion(objectNumber); + g_sludge->_regionMan->removeScreenRegion(objectNumber); return BR_CONTINUE; } builtIn(showBoxes) { UNUSEDALL - showBoxes(); + g_sludge->_regionMan->showBoxes(); return BR_CONTINUE; } builtIn(removeAllScreenRegions) { UNUSEDALL - killAllRegions(); + g_sludge->_regionMan->kill(); return BR_CONTINUE; } @@ -1438,21 +1409,21 @@ builtIn(addCharacter) { Persona *p; int x, y, objectNumber; - p = getCostumeFromVar(fun->stack->thisVar); + p = fun->stack->thisVar.getCostumeFromVar(); if (p == NULL) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(y, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(y, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(x, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(x, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(objectNumber, SVT_OBJTYPE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(objectNumber, SVT_OBJTYPE)) return BR_ERROR; trimStack(fun->stack); - if (addPerson(x, y, objectNumber, p)) + if (g_sludge->_peopleMan->addPerson(x, y, objectNumber, p)) return BR_CONTINUE; return BR_ERROR; } @@ -1460,109 +1431,109 @@ builtIn(addCharacter) { builtIn(hideCharacter) { UNUSEDALL int objectNumber; - if (!getValueType(objectNumber, SVT_OBJTYPE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(objectNumber, SVT_OBJTYPE)) return BR_ERROR; trimStack(fun->stack); - setShown(false, objectNumber); + g_sludge->_peopleMan->setShown(false, objectNumber); return BR_CONTINUE; } builtIn(showCharacter) { UNUSEDALL int objectNumber; - if (!getValueType(objectNumber, SVT_OBJTYPE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(objectNumber, SVT_OBJTYPE)) return BR_ERROR; trimStack(fun->stack); - setShown(true, objectNumber); + g_sludge->_peopleMan->setShown(true, objectNumber); return BR_CONTINUE; } builtIn(removeAllCharacters) { UNUSEDALL killSpeechTimers(); - killMostPeople(); + g_sludge->_peopleMan->killMostPeople(); return BR_CONTINUE; } builtIn(setCharacterDrawMode) { UNUSEDALL int obj, di; - if (!getValueType(di, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(di, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(obj, SVT_OBJTYPE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(obj, SVT_OBJTYPE)) return BR_ERROR; trimStack(fun->stack); - setDrawMode(di, obj); + g_sludge->_peopleMan->setDrawMode(di, obj); return BR_CONTINUE; } builtIn(setCharacterTransparency) { UNUSEDALL int obj, x; - if (!getValueType(x, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(x, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(obj, SVT_OBJTYPE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(obj, SVT_OBJTYPE)) return BR_ERROR; trimStack(fun->stack); - setPersonTransparency(obj, x); + g_sludge->_peopleMan->setPersonTransparency(obj, x); return BR_CONTINUE; } builtIn(setCharacterColourise) { UNUSEDALL int obj, r, g, b, mix; - if (!getValueType(mix, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(mix, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(b, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(b, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(g, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(g, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(r, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(r, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(obj, SVT_OBJTYPE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(obj, SVT_OBJTYPE)) return BR_ERROR; trimStack(fun->stack); - setPersonColourise(obj, r, g, b, mix); + g_sludge->_peopleMan->setPersonColourise(obj, r, g, b, mix); return BR_CONTINUE; } builtIn(setScale) { UNUSEDALL int val1, val2; - if (!getValueType(val2, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(val2, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(val1, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(val1, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - setScale((int16)val1, (int16)val2); + g_sludge->_peopleMan->setScale((int16)val1, (int16)val2); return BR_CONTINUE; } builtIn(stopCharacter) { UNUSEDALL int obj; - if (!getValueType(obj, SVT_OBJTYPE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(obj, SVT_OBJTYPE)) return BR_ERROR; trimStack(fun->stack); // Return value - setVariable(fun->reg, SVT_INT, stopPerson(obj)); + fun->reg.setVariable(SVT_INT, g_sludge->_peopleMan->stopPerson(obj)); return BR_CONTINUE; } builtIn(pasteCharacter) { UNUSEDALL int obj; - if (!getValueType(obj, SVT_OBJTYPE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(obj, SVT_OBJTYPE)) return BR_ERROR; trimStack(fun->stack); - OnScreenPerson *thisPerson = findPerson(obj); + OnScreenPerson *thisPerson = g_sludge->_peopleMan->findPerson(obj); if (thisPerson) { PersonaAnimation *myAnim; myAnim = thisPerson->myAnim; @@ -1574,9 +1545,9 @@ builtIn(pasteCharacter) { int fNum = myAnim->frames[thisPerson->frameNum].frameNum; g_sludge->_gfxMan->fixScaleSprite(thisPerson->x, thisPerson->y, myAnim->theSprites->bank.sprites[ABS(fNum)], myAnim->theSprites->bank.myPalette, thisPerson, 0, 0, fNum < 0); - setVariable(fun->reg, SVT_INT, 1); + fun->reg.setVariable(SVT_INT, 1); } else { - setVariable(fun->reg, SVT_INT, 0); + fun->reg.setVariable(SVT_INT, 0); } return BR_CONTINUE; } @@ -1584,91 +1555,91 @@ builtIn(pasteCharacter) { builtIn(animate) { UNUSEDALL int obj; - PersonaAnimation *pp = getAnimationFromVar(fun->stack->thisVar); + PersonaAnimation *pp = fun->stack->thisVar.getAnimationFromVar(); if (pp == NULL) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(obj, SVT_OBJTYPE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(obj, SVT_OBJTYPE)) return BR_ERROR; trimStack(fun->stack); - animatePerson(obj, pp); - setVariable(fun->reg, SVT_INT, timeForAnim(pp)); + g_sludge->_peopleMan->animatePerson(obj, pp); + fun->reg.setVariable(SVT_INT, pp->getTotalTime()); return BR_CONTINUE; } builtIn(setCostume) { UNUSEDALL int obj; - Persona *pp = getCostumeFromVar(fun->stack->thisVar); + Persona *pp = fun->stack->thisVar.getCostumeFromVar(); if (pp == NULL) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(obj, SVT_OBJTYPE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(obj, SVT_OBJTYPE)) return BR_ERROR; trimStack(fun->stack); - animatePerson(obj, pp); + g_sludge->_peopleMan->animatePerson(obj, pp); return BR_CONTINUE; } builtIn(floatCharacter) { UNUSEDALL int obj, di; - if (!getValueType(di, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(di, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(obj, SVT_OBJTYPE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(obj, SVT_OBJTYPE)) return BR_ERROR; trimStack(fun->stack); - setVariable(fun->reg, SVT_INT, floatCharacter(di, obj)); + fun->reg.setVariable(SVT_INT, g_sludge->_peopleMan->floatCharacter(di, obj)); return BR_CONTINUE; } builtIn(setCharacterWalkSpeed) { UNUSEDALL int obj, di; - if (!getValueType(di, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(di, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(obj, SVT_OBJTYPE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(obj, SVT_OBJTYPE)) return BR_ERROR; trimStack(fun->stack); - setVariable(fun->reg, SVT_INT, setCharacterWalkSpeed(di, obj)); + fun->reg.setVariable(SVT_INT, g_sludge->_peopleMan->setCharacterWalkSpeed(di, obj)); return BR_CONTINUE; } builtIn(turnCharacter) { UNUSEDALL int obj, di; - if (!getValueType(di, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(di, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(obj, SVT_OBJTYPE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(obj, SVT_OBJTYPE)) return BR_ERROR; trimStack(fun->stack); - setVariable(fun->reg, SVT_INT, turnPersonToFace(obj, di)); + fun->reg.setVariable(SVT_INT, g_sludge->_peopleMan->turnPersonToFace(obj, di)); return BR_CONTINUE; } builtIn(setCharacterExtra) { UNUSEDALL int obj, di; - if (!getValueType(di, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(di, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(obj, SVT_OBJTYPE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(obj, SVT_OBJTYPE)) return BR_ERROR; trimStack(fun->stack); - setVariable(fun->reg, SVT_INT, setPersonExtra(obj, di)); + fun->reg.setVariable(SVT_INT, g_sludge->_peopleMan->setPersonExtra(obj, di)); return BR_CONTINUE; } builtIn(removeCharacter) { UNUSEDALL int objectNumber; - if (!getValueType(objectNumber, SVT_OBJTYPE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(objectNumber, SVT_OBJTYPE)) return BR_ERROR; trimStack(fun->stack); - removeOneCharacter(objectNumber); + g_sludge->_peopleMan->removeOneCharacter(objectNumber); return BR_CONTINUE; } @@ -1677,23 +1648,23 @@ static BuiltReturn moveChr(int numParams, LoadedFunction *fun, bool force, bool case 3: { int x, y, objectNumber; - if (!getValueType(y, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(y, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(x, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(x, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(objectNumber, SVT_OBJTYPE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(objectNumber, SVT_OBJTYPE)) return BR_ERROR; trimStack(fun->stack); if (force) { - if (forceWalkingPerson(x, y, objectNumber, fun, -1)) + if (g_sludge->_peopleMan->forceWalkingPerson(x, y, objectNumber, fun, -1)) return BR_PAUSE; } else if (immediate) { - jumpPerson(x, y, objectNumber); + g_sludge->_peopleMan->jumpPerson(x, y, objectNumber); } else { - if (makeWalkingPerson(x, y, objectNumber, fun, -1)) + if (g_sludge->_peopleMan->makeWalkingPerson(x, y, objectNumber, fun, -1)) return BR_PAUSE; } return BR_CONTINUE; @@ -1703,23 +1674,23 @@ static BuiltReturn moveChr(int numParams, LoadedFunction *fun, bool force, bool int toObj, objectNumber; ScreenRegion*reggie; - if (!getValueType(toObj, SVT_OBJTYPE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(toObj, SVT_OBJTYPE)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(objectNumber, SVT_OBJTYPE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(objectNumber, SVT_OBJTYPE)) return BR_ERROR; trimStack(fun->stack); - reggie = getRegionForObject(toObj); + reggie = g_sludge->_regionMan->getRegionForObject(toObj); if (reggie == NULL) return BR_CONTINUE; if (force) { - if (forceWalkingPerson(reggie->sX, reggie->sY, objectNumber, fun, reggie->di)) + if (g_sludge->_peopleMan->forceWalkingPerson(reggie->sX, reggie->sY, objectNumber, fun, reggie->di)) return BR_PAUSE; } else if (immediate) { - jumpPerson(reggie->sX, reggie->sY, objectNumber); + g_sludge->_peopleMan->jumpPerson(reggie->sX, reggie->sY, objectNumber); } else { - if (makeWalkingPerson(reggie->sX, reggie->sY, objectNumber, fun, reggie->di)) + if (g_sludge->_peopleMan->makeWalkingPerson(reggie->sX, reggie->sY, objectNumber, fun, reggie->di)) return BR_PAUSE; } return BR_CONTINUE; @@ -1766,7 +1737,7 @@ builtIn(addStatus) { builtIn(statusText) { UNUSEDALL - Common::String newText = getTextFromAnyVar(fun->stack->thisVar); + Common::String newText = fun->stack->thisVar.getTextFromAnyVar(); trimStack(fun->stack); setStatusBar(newText); return BR_CONTINUE; @@ -1775,7 +1746,7 @@ builtIn(statusText) { builtIn(lightStatus) { UNUSEDALL int val; - if (!getValueType(val, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(val, SVT_INT)) return BR_ERROR; trimStack(fun->stack); setLitStatus(val); @@ -1785,10 +1756,10 @@ builtIn(lightStatus) { builtIn(positionStatus) { UNUSEDALL int x, y; - if (!getValueType(y, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(y, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(x, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(x, SVT_INT)) return BR_ERROR; trimStack(fun->stack); positionStatus(x, y); @@ -1798,7 +1769,7 @@ builtIn(positionStatus) { builtIn(alignStatus) { UNUSEDALL int val; - if (!getValueType(val, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(val, SVT_INT)) return BR_ERROR; trimStack(fun->stack); nowStatus->alignStatus = (int16)val; @@ -1812,7 +1783,7 @@ static bool getFuncNumForCallback(int numParams, LoadedFunction *fun, int &funct break; case 1: - if (!getValueType(functionNum, SVT_FUNC, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(functionNum, SVT_FUNC)) return false; trimStack(fun->stack); break; @@ -1922,18 +1893,18 @@ builtIn(cancelSub) { builtIn(stringWidth) { UNUSEDALL - Common::String theText = getTextFromAnyVar(fun->stack->thisVar); + Common::String theText = fun->stack->thisVar.getTextFromAnyVar(); trimStack(fun->stack); // Return value - setVariable(fun->reg, SVT_INT, g_sludge->_txtMan->stringWidth(theText)); + fun->reg.setVariable(SVT_INT, g_sludge->_txtMan->stringWidth(theText)); return BR_CONTINUE; } builtIn(hardScroll) { UNUSEDALL int v; - if (!getValueType(v, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(v, SVT_INT)) return BR_ERROR; trimStack(fun->stack); g_sludge->_gfxMan->hardScroll(v); @@ -1943,80 +1914,76 @@ builtIn(hardScroll) { builtIn(isScreenRegion) { UNUSEDALL int objectNumber; - if (!getValueType(objectNumber, SVT_OBJTYPE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(objectNumber, SVT_OBJTYPE)) return BR_ERROR; trimStack(fun->stack); - setVariable(fun->reg, SVT_INT, getRegionForObject(objectNumber) != NULL); + fun->reg.setVariable(SVT_INT, g_sludge->_regionMan->getRegionForObject(objectNumber) != NULL); return BR_CONTINUE; } builtIn(setSpeechSpeed) { UNUSEDALL int number; - if (!getValueType(number, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(number, SVT_INT)) return BR_ERROR; trimStack(fun->stack); g_sludge->_speechMan->setSpeechSpeed(number * 0.01); - setVariable(fun->reg, SVT_INT, 1); + fun->reg.setVariable(SVT_INT, 1); return BR_CONTINUE; } builtIn(setFontSpacing) { UNUSEDALL int fontSpaceI; - if (!getValueType(fontSpaceI, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(fontSpaceI, SVT_INT)) return BR_ERROR; g_sludge->_txtMan->setFontSpace(fontSpaceI); trimStack(fun->stack); - setVariable(fun->reg, SVT_INT, 1); + fun->reg.setVariable(SVT_INT, 1); return BR_CONTINUE; } builtIn(transitionLevel) { UNUSEDALL - int number; - if (!getValueType(number, SVT_INT, fun->stack->thisVar)) + int brightnessLevel; + if (!fun->stack->thisVar.getValueType(brightnessLevel, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (number < 0) - brightnessLevel = 0; - else if (number > 255) - brightnessLevel = 255; - else - brightnessLevel = number; + g_sludge->_gfxMan->setBrightnessLevel(brightnessLevel); - setVariable(fun->reg, SVT_INT, 1); + fun->reg.setVariable(SVT_INT, 1); return BR_CONTINUE; } builtIn(captureAllKeys) { UNUSEDALL - captureAllKeys = getBoolean(fun->stack->thisVar); + // This built-in function doesn't have any effect any more, we capture all keys by default + bool captureAllKeysDeprecated = fun->stack->thisVar.getBoolean(); trimStack(fun->stack); - setVariable(fun->reg, SVT_INT, captureAllKeys); + fun->reg.setVariable(SVT_INT, captureAllKeysDeprecated); return BR_CONTINUE; } builtIn(spinCharacter) { UNUSEDALL int number, objectNumber; - if (!getValueType(number, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(number, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(objectNumber, SVT_OBJTYPE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(objectNumber, SVT_OBJTYPE)) return BR_ERROR; trimStack(fun->stack); - OnScreenPerson *thisPerson = findPerson(objectNumber); + OnScreenPerson *thisPerson = g_sludge->_peopleMan->findPerson(objectNumber); if (thisPerson) { thisPerson->wantAngle = number; thisPerson->spinning = true; thisPerson->continueAfterWalking = fun; - setVariable(fun->reg, SVT_INT, 1); + fun->reg.setVariable(SVT_INT, 1); return BR_PAUSE; } else { - setVariable(fun->reg, SVT_INT, 0); + fun->reg.setVariable(SVT_INT, 0); return BR_CONTINUE; } } @@ -2024,14 +1991,14 @@ builtIn(spinCharacter) { builtIn(getCharacterDirection) { UNUSEDALL int objectNumber; - if (!getValueType(objectNumber, SVT_OBJTYPE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(objectNumber, SVT_OBJTYPE)) return BR_ERROR; trimStack(fun->stack); - OnScreenPerson *thisPerson = findPerson(objectNumber); + OnScreenPerson *thisPerson = g_sludge->_peopleMan->findPerson(objectNumber); if (thisPerson) { - setVariable(fun->reg, SVT_INT, thisPerson->direction); + fun->reg.setVariable(SVT_INT, thisPerson->direction); } else { - setVariable(fun->reg, SVT_INT, 0); + fun->reg.setVariable(SVT_INT, 0); } return BR_CONTINUE; } @@ -2039,26 +2006,26 @@ builtIn(getCharacterDirection) { builtIn(isCharacter) { UNUSEDALL int objectNumber; - if (!getValueType(objectNumber, SVT_OBJTYPE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(objectNumber, SVT_OBJTYPE)) return BR_ERROR; trimStack(fun->stack); - OnScreenPerson *thisPerson = findPerson(objectNumber); - setVariable(fun->reg, SVT_INT, thisPerson != NULL); + OnScreenPerson *thisPerson = g_sludge->_peopleMan->findPerson(objectNumber); + fun->reg.setVariable(SVT_INT, thisPerson != NULL); return BR_CONTINUE; } builtIn(normalCharacter) { UNUSEDALL int objectNumber; - if (!getValueType(objectNumber, SVT_OBJTYPE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(objectNumber, SVT_OBJTYPE)) return BR_ERROR; trimStack(fun->stack); - OnScreenPerson *thisPerson = findPerson(objectNumber); + OnScreenPerson *thisPerson = g_sludge->_peopleMan->findPerson(objectNumber); if (thisPerson) { thisPerson->myAnim = thisPerson->myPersona->animation[thisPerson->direction]; - setVariable(fun->reg, SVT_INT, 1); + fun->reg.setVariable(SVT_INT, 1); } else { - setVariable(fun->reg, SVT_INT, 0); + fun->reg.setVariable(SVT_INT, 0); } return BR_CONTINUE; } @@ -2066,14 +2033,14 @@ builtIn(normalCharacter) { builtIn(isMoving) { UNUSEDALL int objectNumber; - if (!getValueType(objectNumber, SVT_OBJTYPE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(objectNumber, SVT_OBJTYPE)) return BR_ERROR; trimStack(fun->stack); - OnScreenPerson *thisPerson = findPerson(objectNumber); + OnScreenPerson *thisPerson = g_sludge->_peopleMan->findPerson(objectNumber); if (thisPerson) { - setVariable(fun->reg, SVT_INT, thisPerson->walking); + fun->reg.setVariable(SVT_INT, thisPerson->walking); } else { - setVariable(fun->reg, SVT_INT, 0); + fun->reg.setVariable(SVT_INT, 0); } return BR_CONTINUE; } @@ -2081,10 +2048,10 @@ builtIn(isMoving) { builtIn(fetchEvent) { UNUSEDALL int obj1, obj2; - if (!getValueType(obj2, SVT_OBJTYPE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(obj2, SVT_OBJTYPE)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(obj1, SVT_OBJTYPE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(obj1, SVT_OBJTYPE)) return BR_ERROR; trimStack(fun->stack); @@ -2092,9 +2059,9 @@ builtIn(fetchEvent) { // Return value if (fNum) { - setVariable(fun->reg, SVT_FUNC, fNum); + fun->reg.setVariable(SVT_FUNC, fNum); } else { - setVariable(fun->reg, SVT_INT, 0); + fun->reg.setVariable(SVT_INT, 0); } return BR_CONTINUE; } @@ -2102,13 +2069,13 @@ builtIn(fetchEvent) { builtIn(deleteFile) { UNUSEDALL - Common::String namNormal = getTextFromAnyVar(fun->stack->thisVar); + Common::String namNormal = fun->stack->thisVar.getTextFromAnyVar(); trimStack(fun->stack); Common::String nam = encodeFilename(namNormal); namNormal.clear(); if (failSecurityCheck(nam)) return BR_ERROR; - setVariable(fun->reg, SVT_INT, remove(nam.c_str())); + fun->reg.setVariable(SVT_INT, remove(nam.c_str())); return BR_CONTINUE; } @@ -2118,20 +2085,20 @@ builtIn(renameFile) { Common::String temp; temp.clear(); - temp = getTextFromAnyVar(fun->stack->thisVar); + temp = fun->stack->thisVar.getTextFromAnyVar(); Common::String newnam = encodeFilename(temp); trimStack(fun->stack); if (failSecurityCheck(newnam)) return BR_ERROR; temp.clear(); - temp = getTextFromAnyVar(fun->stack->thisVar); + temp = fun->stack->thisVar.getTextFromAnyVar(); Common::String nam = encodeFilename(temp); trimStack(fun->stack); if (failSecurityCheck(nam)) return BR_ERROR; - setVariable(fun->reg, SVT_INT, rename(nam.c_str(), newnam.c_str())); + fun->reg.setVariable(SVT_INT, rename(nam.c_str(), newnam.c_str())); return BR_CONTINUE; } @@ -2139,7 +2106,7 @@ builtIn(renameFile) { builtIn(cacheSound) { UNUSEDALL int fileNumber; - if (!getValueType(fileNumber, SVT_FILE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(fileNumber, SVT_FILE)) return BR_ERROR; trimStack(fun->stack); if (g_sludge->_soundMan->cacheSound(fileNumber) == -1) @@ -2149,38 +2116,38 @@ builtIn(cacheSound) { builtIn(burnString) { UNUSEDALL - Common::String newText = getTextFromAnyVar(fun->stack->thisVar); + Common::String newText = fun->stack->thisVar.getTextFromAnyVar(); trimStack(fun->stack); int y, x; - if (!getValueType(y, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(y, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(x, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(x, SVT_INT)) return BR_ERROR; trimStack(fun->stack); if (x == IN_THE_CENTRE) x = g_sludge->_gfxMan->getCenterX(g_sludge->_txtMan->stringWidth(newText)); - g_sludge->_txtMan->burnStringToBackdrop(newText, x, y, pastePalette); + g_sludge->_txtMan->burnStringToBackdrop(newText, x, y); return BR_CONTINUE; } builtIn(setCharacterSpinSpeed) { UNUSEDALL int speed, who; - if (!getValueType(speed, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(speed, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(who, SVT_OBJTYPE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(who, SVT_OBJTYPE)) return BR_ERROR; trimStack(fun->stack); - OnScreenPerson *thisPerson = findPerson(who); + OnScreenPerson *thisPerson = g_sludge->_peopleMan->findPerson(who); if (thisPerson) { thisPerson->spinSpeed = speed; - setVariable(fun->reg, SVT_INT, 1); + fun->reg.setVariable(SVT_INT, 1); } else { - setVariable(fun->reg, SVT_INT, 0); + fun->reg.setVariable(SVT_INT, 0); } return BR_CONTINUE; } @@ -2188,20 +2155,20 @@ builtIn(setCharacterSpinSpeed) { builtIn(setCharacterAngleOffset) { UNUSEDALL int angle, who; - if (!getValueType(angle, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(angle, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(who, SVT_OBJTYPE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(who, SVT_OBJTYPE)) return BR_ERROR; trimStack(fun->stack); - OnScreenPerson *thisPerson = findPerson(who); + OnScreenPerson *thisPerson = g_sludge->_peopleMan->findPerson(who); if (thisPerson) { thisPerson->angleOffset = angle; - setVariable(fun->reg, SVT_INT, 1); + fun->reg.setVariable(SVT_INT, 1); } else { - setVariable(fun->reg, SVT_INT, 0); + fun->reg.setVariable(SVT_INT, 0); } return BR_CONTINUE; } @@ -2209,11 +2176,11 @@ builtIn(setCharacterAngleOffset) { builtIn(transitionMode) { UNUSEDALL int n; - if (!getValueType(n, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(n, SVT_INT)) return BR_ERROR; - fadeMode = n; + g_sludge->_gfxMan->setFadeMode(n); trimStack(fun->stack); - setVariable(fun->reg, SVT_INT, 1); + fun->reg.setVariable(SVT_INT, 1); return BR_CONTINUE; } @@ -2221,7 +2188,7 @@ builtIn(transitionMode) { builtIn(_rem_updateDisplay) { UNUSEDALL trimStack(fun->stack); - setVariable(fun->reg, SVT_INT, true); + fun->reg.setVariable(SVT_INT, true); return BR_CONTINUE; } @@ -2242,7 +2209,7 @@ builtIn(getSoundCache) { builtIn(saveCustomData) { UNUSEDALL // saveCustomData (thisStack, fileName); - Common::String fileNameB = getTextFromAnyVar(fun->stack->thisVar); + Common::String fileNameB = fun->stack->thisVar.getTextFromAnyVar(); Common::String fileName = encodeFilename(fileNameB); @@ -2254,7 +2221,7 @@ builtIn(saveCustomData) { fatal("First parameter isn't a stack"); return BR_ERROR; } - if (!stackToFile(fileName, fun->stack->thisVar)) + if (!CustomSaveHelper::stackToFile(fileName, fun->stack->thisVar)) return BR_ERROR; trimStack(fun->stack); return BR_CONTINUE; @@ -2263,7 +2230,7 @@ builtIn(saveCustomData) { builtIn(loadCustomData) { UNUSEDALL - Common::String newTextA = getTextFromAnyVar(fun->stack->thisVar); + Common::String newTextA = fun->stack->thisVar.getTextFromAnyVar(); Common::String newText = encodeFilename(newTextA); @@ -2271,7 +2238,7 @@ builtIn(loadCustomData) { return BR_ERROR; trimStack(fun->stack); - unlinkVar(fun->reg); + fun->reg.unlinkVar(); fun->reg.varType = SVT_STACK; fun->reg.varData.theStack = new StackHandler; if (!checkNew(fun->reg.varData.theStack)) @@ -2279,7 +2246,7 @@ builtIn(loadCustomData) { fun->reg.varData.theStack->first = NULL; fun->reg.varData.theStack->last = NULL; fun->reg.varData.theStack->timesUsed = 1; - if (!fileToStack(newText, fun->reg.varData.theStack)) + if (!CustomSaveHelper::fileToStack(newText, fun->reg.varData.theStack)) return BR_ERROR; return BR_CONTINUE; } @@ -2287,18 +2254,18 @@ builtIn(loadCustomData) { builtIn(setCustomEncoding) { UNUSEDALL int n; - if (!getValueType(n, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(n, SVT_INT)) return BR_ERROR; - saveEncoding = n; + CustomSaveHelper::_saveEncoding = n; trimStack(fun->stack); - setVariable(fun->reg, SVT_INT, 1); + fun->reg.setVariable(SVT_INT, 1); return BR_CONTINUE; } builtIn(freeSound) { UNUSEDALL int v; - if (!getValueType(v, SVT_FILE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(v, SVT_FILE)) return BR_ERROR; trimStack(fun->stack); g_sludge->_soundMan->huntKillFreeSound(v); @@ -2312,19 +2279,19 @@ builtIn(parallaxAdd) { return BR_ERROR; } else { int wrapX, wrapY, v; - if (!getValueType(wrapY, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(wrapY, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(wrapX, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(wrapX, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(v, SVT_FILE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(v, SVT_FILE)) return BR_ERROR; trimStack(fun->stack); if (!g_sludge->_gfxMan->loadParallax(v, wrapX, wrapY)) return BR_ERROR; - setVariable(fun->reg, SVT_INT, 1); + fun->reg.setVariable(SVT_INT, 1); } return BR_CONTINUE; } @@ -2332,21 +2299,21 @@ builtIn(parallaxAdd) { builtIn(parallaxClear) { UNUSEDALL g_sludge->_gfxMan->killParallax(); - setVariable(fun->reg, SVT_INT, 1); + fun->reg.setVariable(SVT_INT, 1); return BR_CONTINUE; } builtIn(getPixelColour) { UNUSEDALL int x, y; - if (!getValueType(y, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(y, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(x, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(x, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - unlinkVar(fun->reg); + fun->reg.unlinkVar(); fun->reg.varType = SVT_STACK; fun->reg.varData.theStack = new StackHandler; if (!checkNew(fun->reg.varData.theStack)) @@ -2364,7 +2331,7 @@ builtIn(makeFastArray) { UNUSEDALL switch (fun->stack->thisVar.varType) { case SVT_STACK: { - bool success = makeFastArrayFromStack(fun->reg, fun->stack->thisVar.varData.theStack); + bool success = fun->reg.makeFastArrayFromStack(fun->stack->thisVar.varData.theStack); trimStack(fun->stack); return success ? BR_CONTINUE : BR_ERROR; } @@ -2373,7 +2340,7 @@ builtIn(makeFastArray) { case SVT_INT: { int i = fun->stack->thisVar.varData.intValue; trimStack(fun->stack); - return makeFastArraySize(fun->reg, i) ? BR_CONTINUE : BR_ERROR; + return fun->reg.makeFastArraySize(i) ? BR_CONTINUE : BR_ERROR; } break; @@ -2387,22 +2354,22 @@ builtIn(makeFastArray) { builtIn(getCharacterScale) { UNUSEDALL int objectNumber; - if (!getValueType(objectNumber, SVT_OBJTYPE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(objectNumber, SVT_OBJTYPE)) return BR_ERROR; trimStack(fun->stack); - OnScreenPerson *pers = findPerson(objectNumber); + OnScreenPerson *pers = g_sludge->_peopleMan->findPerson(objectNumber); if (pers) { - setVariable(fun->reg, SVT_INT, pers->scale * 100); + fun->reg.setVariable(SVT_INT, pers->scale * 100); } else { - setVariable(fun->reg, SVT_INT, 0); + fun->reg.setVariable(SVT_INT, 0); } return BR_CONTINUE; } builtIn(getLanguageID) { UNUSEDALL - setVariable(fun->reg, SVT_INT, g_sludge->getLanguageID()); + fun->reg.setVariable(SVT_INT, g_sludge->getLanguageID()); return BR_CONTINUE; } @@ -2413,7 +2380,7 @@ builtIn(_rem_launchWith) { trimStack(fun->stack); // To support some windows only games - Common::String filename = getTextFromAnyVar(fun->stack->thisVar); + Common::String filename = fun->stack->thisVar.getTextFromAnyVar(); trimStack(fun->stack); if (filename.hasSuffix(".exe")) { @@ -2432,28 +2399,28 @@ builtIn(_rem_launchWith) { } g_sludge->launchNext.clear(); - setVariable(fun->reg, SVT_INT, false); + fun->reg.setVariable(SVT_INT, false); return BR_CONTINUE; } builtIn(getFramesPerSecond) { UNUSEDALL - setVariable(fun->reg, SVT_INT, lastFramesPerSecond); + fun->reg.setVariable(SVT_INT, g_sludge->_timer.getLastFps()); return BR_CONTINUE; } builtIn(showThumbnail) { UNUSEDALL int x, y; - if (!getValueType(y, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(y, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(x, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(x, SVT_INT)) return BR_ERROR; trimStack(fun->stack); // Encode the name!Encode the name! - Common::String aaaaa = getTextFromAnyVar(fun->stack->thisVar); + Common::String aaaaa = fun->stack->thisVar.getTextFromAnyVar(); trimStack(fun->stack); Common::String file = encodeFilename(aaaaa); g_sludge->_gfxMan->showThumbnail(file, x, y); @@ -2462,13 +2429,14 @@ builtIn(showThumbnail) { builtIn(setThumbnailSize) { UNUSEDALL - if (!getValueType(thumbHeight, SVT_INT, fun->stack->thisVar)) + int thumbHeight, thumbWidth; + if (!fun->stack->thisVar.getValueType(thumbHeight, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(thumbWidth, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(thumbWidth, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!g_sludge->_gfxMan->checkSizeValide(thumbWidth, thumbHeight)) { + if (!g_sludge->_gfxMan->setThumbnailSize(thumbWidth, thumbHeight)) { Common::String buff = Common::String::format("%i x %i", thumbWidth, thumbWidth); fatal("Invalid thumbnail size", buff); return BR_ERROR; @@ -2479,16 +2447,16 @@ builtIn(setThumbnailSize) { builtIn(hasFlag) { UNUSEDALL int objNum, flagIndex; - if (!getValueType(flagIndex, SVT_INT, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(flagIndex, SVT_INT)) return BR_ERROR; trimStack(fun->stack); - if (!getValueType(objNum, SVT_OBJTYPE, fun->stack->thisVar)) + if (!fun->stack->thisVar.getValueType(objNum, SVT_OBJTYPE)) return BR_ERROR; trimStack(fun->stack); ObjectType *objT = g_sludge->_objMan->findObjectType(objNum); if (!objT) return BR_ERROR; - setVariable(fun->reg, SVT_INT, objT->flags & (1 << flagIndex)); + fun->reg.setVariable(SVT_INT, objT->flags & (1 << flagIndex)); return BR_CONTINUE; } @@ -2506,9 +2474,9 @@ builtIn(snapshotClear) { builtIn(bodgeFilenames) { UNUSEDALL bool lastValue = allowAnyFilename; - allowAnyFilename = getBoolean(fun->stack->thisVar); + allowAnyFilename = fun->stack->thisVar.getBoolean(); trimStack(fun->stack); - setVariable(fun->reg, SVT_INT, lastValue); + fun->reg.setVariable(SVT_INT, lastValue); return BR_CONTINUE; } @@ -2517,14 +2485,14 @@ builtIn(_rem_registryGetString) { UNUSEDALL trimStack(fun->stack); trimStack(fun->stack); - setVariable(fun->reg, SVT_INT, 0); + fun->reg.setVariable(SVT_INT, 0); return BR_CONTINUE; } builtIn(quitWithFatalError) { UNUSEDALL - Common::String mess = getTextFromAnyVar(fun->stack->thisVar); + Common::String mess = fun->stack->thisVar.getTextFromAnyVar(); trimStack(fun->stack); fatal(mess); return BR_ERROR; @@ -2555,14 +2523,14 @@ builtIn(_rem_setMaximumAA) { builtIn(setBackgroundEffect) { UNUSEDALL bool done = blur_createSettings(numParams, fun->stack); - setVariable(fun->reg, SVT_INT, done ? 1 : 0); + fun->reg.setVariable(SVT_INT, done ? 1 : 0); return BR_CONTINUE; } builtIn(doBackgroundEffect) { UNUSEDALL bool done = blurScreen(); - setVariable(fun->reg, SVT_INT, done ? 1 : 0); + fun->reg.setVariable(SVT_INT, done ? 1 : 0); return BR_CONTINUE; } @@ -2584,9 +2552,9 @@ BuiltReturn callBuiltIn(int whichFunc, int numParams, LoadedFunction *fun) { } if (whichFunc < NUM_FUNCS) { - if (paramNum[whichFunc] != -1) { - if (paramNum[whichFunc] != numParams) { - Common::String buff = Common::String::format("Built in function must have %i parameter%s", paramNum[whichFunc], (paramNum[whichFunc] == 1) ? "" : "s"); + if (builtInFunctionArray[whichFunc].paramNum != -1) { + if (builtInFunctionArray[whichFunc].paramNum != numParams) { + Common::String buff = Common::String::format("Built in function must have %i parameter%s", builtInFunctionArray[whichFunc].paramNum, (builtInFunctionArray[whichFunc].paramNum == 1) ? "" : "s"); Common::String msg = buff; fatal(msg); return BR_ERROR; diff --git a/engines/sludge/builtin.h b/engines/sludge/builtin.h index b2274c22e9..b7fa8b86ef 100644 --- a/engines/sludge/builtin.h +++ b/engines/sludge/builtin.h @@ -23,6 +23,8 @@ #ifndef SLUDGE_BUILTIN_H #define SLUDGE_BUILTIN_H +#include "sludge/allfiles.h" + namespace Sludge { struct LoadedFunction; diff --git a/engines/sludge/csludge.h b/engines/sludge/csludge.h index 435a220a2d..cf0d6aaf43 100644 --- a/engines/sludge/csludge.h +++ b/engines/sludge/csludge.h @@ -25,7 +25,7 @@ namespace Sludge { -enum sludgeCommand { +enum SludgeCommand { SLU_UNKNOWN, SLU_RETURN, SLU_BRANCH, diff --git a/engines/sludge/cursors.cpp b/engines/sludge/cursors.cpp index 0c7745e9ff..0ec46e2f17 100644 --- a/engines/sludge/cursors.cpp +++ b/engines/sludge/cursors.cpp @@ -44,18 +44,24 @@ CursorManager::~CursorManager() { } void CursorManager::init() { - _mouseCursorAnim = makeNullAnim(); + _mouseCursorAnim = new PersonaAnimation(); _mouseCursorFrameNum = 0; _mouseCursorCountUp = 0; } void CursorManager::kill() { - deleteAnim(_mouseCursorAnim); + if (_mouseCursorAnim) { + delete _mouseCursorAnim; + _mouseCursorAnim = nullptr; + } _mouseCursorAnim = nullptr; } void CursorManager::pickAnimCursor(PersonaAnimation *pp) { - deleteAnim(_mouseCursorAnim); + if (_mouseCursorAnim) { + delete _mouseCursorAnim; + _mouseCursorAnim = nullptr; + } _mouseCursorAnim = pp; _mouseCursorFrameNum = 0; _mouseCursorCountUp = 0; @@ -107,18 +113,21 @@ void CursorManager::pasteCursor(int x, int y, PersonaAnimation *c) { void CursorManager::freeze(FrozenStuffStruct *frozenStuff) { frozenStuff->mouseCursorAnim = _mouseCursorAnim; frozenStuff->mouseCursorFrameNum = _mouseCursorFrameNum; - _mouseCursorAnim = makeNullAnim(); + _mouseCursorAnim = new PersonaAnimation(); _mouseCursorFrameNum = 0; } void CursorManager::resotre(FrozenStuffStruct *frozenStuff) { - deleteAnim(_mouseCursorAnim); + if (_mouseCursorAnim) { + delete _mouseCursorAnim; + _mouseCursorAnim = nullptr; + } _mouseCursorAnim = frozenStuff->mouseCursorAnim; _mouseCursorFrameNum = frozenStuff->mouseCursorFrameNum; } void CursorManager::saveCursor(Common::WriteStream *stream) { - saveAnim(_mouseCursorAnim, stream); + _mouseCursorAnim->save(stream); stream->writeUint16BE(_mouseCursorFrameNum); } @@ -126,7 +135,7 @@ bool CursorManager::loadCursor(Common::SeekableReadStream *stream) { _mouseCursorAnim = new PersonaAnimation; if (!checkNew(_mouseCursorAnim)) return false; - if (!loadAnim(_mouseCursorAnim, stream)) + if (!_mouseCursorAnim->load(stream)) return false; _mouseCursorFrameNum = stream->readUint16BE(); return true; diff --git a/engines/sludge/cursors.h b/engines/sludge/cursors.h index 4229900a94..f3c71c2560 100644 --- a/engines/sludge/cursors.h +++ b/engines/sludge/cursors.h @@ -53,7 +53,7 @@ public: private: SludgeEngine *_vm; - PersonaAnimation *_mouseCursorAnim; + PersonaAnimation *_mouseCursorAnim; int _mouseCursorFrameNum; int _mouseCursorCountUp; }; diff --git a/engines/sludge/detection.cpp b/engines/sludge/detection.cpp index a530a5c796..8c5c0ac13d 100644 --- a/engines/sludge/detection.cpp +++ b/engines/sludge/detection.cpp @@ -86,11 +86,11 @@ public: virtual const char *getName() const { return "Sludge Engine"; } - + virtual const char *getOriginalCopyright() const { return "Copyright (C) 2000-2014 Hungry Software and contributors"; } - + virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const { const Sludge::SludgeGameDescription *gd = (const Sludge::SludgeGameDescription *)desc; if (gd) { @@ -100,10 +100,10 @@ public: } // for fall back detection - virtual const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const; + ADDetectedGame fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const override; }; -const ADGameDescription *SludgeMetaEngine::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const { +ADDetectedGame SludgeMetaEngine::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const { // reset fallback description s_fallbackDesc.desc.gameId = "sludge"; s_fallbackDesc.desc.extra = ""; @@ -147,9 +147,19 @@ const ADGameDescription *SludgeMetaEngine::fallbackDetect(const FileMap &allFile s_fallbackFileNameBuffer[50] = '\0'; s_fallbackDesc.desc.filesDescriptions[0].fileName = s_fallbackFileNameBuffer; - return (const ADGameDescription *)&s_fallbackDesc; + ADDetectedGame game; + game.desc = &s_fallbackDesc.desc; + + FileProperties tmp; + if (getFileProperties(file->getParent(), allFiles, s_fallbackDesc.desc, fileName, tmp)) { + game.hasUnknownFiles = true; + game.matchedFiles[fileName] = tmp; + } + + return game; } - return 0; + + return ADDetectedGame(); } #if PLUGIN_ENABLED_DYNAMIC(SLUDGE) diff --git a/engines/sludge/event.cpp b/engines/sludge/event.cpp index d5c453bdc1..098720b4c6 100644 --- a/engines/sludge/event.cpp +++ b/engines/sludge/event.cpp @@ -25,6 +25,7 @@ #include "sludge/event.h" #include "sludge/freeze.h" +#include "sludge/function.h" #include "sludge/graphics.h" #include "sludge/newfatal.h" #include "sludge/region.h" @@ -36,8 +37,6 @@ namespace Sludge { extern Variable *launchResult; extern VariableStack *noStack; -extern ScreenRegion *overRegion; -extern ScreenRegion *lastRegion; EventManager::EventManager(SludgeEngine *vm) { _vm = vm; @@ -152,14 +151,14 @@ void EventManager::checkInput() { bool EventManager::handleInput() { static int l = 0; - if (!g_sludge->launchMe.empty()) { + if (!_vm->launchMe.empty()) { if (l) { // Still paused because of spawned thingy... } else { l = 1; - setVariable(*launchResult, SVT_INT, 0/*launch(launchMe) > 31*/); //TODO:false value - g_sludge->launchMe.clear(); + launchResult->setVariable(SVT_INT, 0/*launch(launchMe) > 31*/); //TODO:false value + _vm->launchMe.clear(); launchResult = nullptr; } return true; @@ -167,8 +166,8 @@ bool EventManager::handleInput() { l = 0; } - if (!overRegion) - getOverRegion(); + if (!_vm->_regionMan->getOverRegion()) + _vm->_regionMan->updateOverRegion(); if (_input.justMoved) { if (_currentEvents->func[kMoveMouse]) { @@ -178,16 +177,16 @@ bool EventManager::handleInput() { } _input.justMoved = false; - if (lastRegion != overRegion && _currentEvents->func[kFocus]) { + if (_vm-> _regionMan->isRegionChanged()&& _currentEvents->func[kFocus]) { VariableStack *tempStack = new VariableStack; if (!checkNew(tempStack)) return false; - initVarNew(tempStack->thisVar); + ScreenRegion *overRegion = _vm->_regionMan->getOverRegion(); if (overRegion) { - setVariable(tempStack->thisVar, SVT_OBJTYPE, overRegion->thisType->objectNum); + tempStack->thisVar.setVariable(SVT_OBJTYPE, overRegion->thisType->objectNum); } else { - setVariable(tempStack->thisVar, SVT_INT, 0); + tempStack->thisVar.setVariable(SVT_INT, 0); } tempStack->next = nullptr; if (!startNewFunctionNum(_currentEvents->func[kFocus], 1, nullptr, tempStack)) @@ -321,8 +320,7 @@ bool EventManager::handleInput() { VariableStack *tempStack = new VariableStack; if (!checkNew(tempStack)) return false; - initVarNew(tempStack->thisVar); - makeTextVar(tempStack->thisVar, tempString); + tempStack->thisVar.makeTextVar(tempString); tempStack->next = nullptr; if (!startNewFunctionNum(_currentEvents->func[kSpace], 1, nullptr, tempStack)) return false; @@ -333,7 +331,7 @@ bool EventManager::handleInput() { _input.rightRelease = false; _input.leftRelease = false; _input.keyPressed = 0; - lastRegion = overRegion; + _vm->_regionMan->updateLastRegion(); return true; } diff --git a/engines/sludge/event.h b/engines/sludge/event.h index 015e9ea1cb..c07f7b87ad 100644 --- a/engines/sludge/event.h +++ b/engines/sludge/event.h @@ -69,7 +69,7 @@ public: int &mouseY() { return _input.mouseY; } // Events - void setEventFunction(EventFunctions event, int funcNum) { _currentEvents->func[event] = funcNum; } ; + void setEventFunction(EventFunctions event, int funcNum) { _currentEvents->func[event] = funcNum; }; void loadHandlers(Common::SeekableReadStream *stream); void saveHandlers(Common::WriteStream *stream); bool freeze(FrozenStuffStruct *frozenStuff); diff --git a/engines/sludge/fileset.cpp b/engines/sludge/fileset.cpp index fcdec32335..c9c3e7a43b 100644 --- a/engines/sludge/fileset.cpp +++ b/engines/sludge/fileset.cpp @@ -48,12 +48,14 @@ void ResourceManager::init() { _startOfSubIndex = 0; _startOfObjectIndex = 0; _startIndex = 0; + _allResourceNames.clear(); } void ResourceManager::kill() { if (_bigDataFile) { delete _bigDataFile; _bigDataFile = nullptr; } + _allResourceNames.clear(); } bool ResourceManager::openSubSlice(int num) { @@ -216,6 +218,31 @@ void ResourceManager::finishAccess() { _sliceBusy = false; } +void ResourceManager::readResourceNames(Common::SeekableReadStream *readStream) { + int numResourceNames = readStream->readUint16BE(); + debugC(2, kSludgeDebugDataLoad, "numResourceNames %i", numResourceNames); + _allResourceNames.reserve(numResourceNames); + + for (int fn = 0; fn < numResourceNames; fn++) { + _allResourceNames[fn].clear(); + _allResourceNames[fn] = readString(readStream); + debugC(2, kSludgeDebugDataLoad, "Resource %i: %s", fn, _allResourceNames[fn].c_str()); + } +} + +const Common::String ResourceManager::resourceNameFromNum(int i) { + if (i == -1) + return ""; + + if (_allResourceNames.empty()) + return "RESOURCE"; + + if (i < (int)_allResourceNames.size()) + return _allResourceNames[i]; + + return "Unknown resource"; +} + void ResourceManager::setData(Common::File *fp) { _bigDataFile = fp; _startIndex = fp->pos(); diff --git a/engines/sludge/fileset.h b/engines/sludge/fileset.h index 83200ceeb8..fb6a696f47 100644 --- a/engines/sludge/fileset.h +++ b/engines/sludge/fileset.h @@ -44,15 +44,23 @@ public: bool openObjectSlice(int num); Common::String getNumberedString(int value); + // Access control flag bool startAccess(); void finishAccess(); + // Resource names + void readResourceNames(Common::SeekableReadStream *readStream); + const Common::String resourceNameFromNum(int i); + bool hasResourceNames() { return !_allResourceNames.empty(); } + private: bool _sliceBusy; Common::File *_bigDataFile; uint32 _startOfDataIndex, _startOfTextIndex, _startOfSubIndex, _startOfObjectIndex; int32 _startIndex; + Common::Array<Common::String> _allResourceNames; + private: static uint32 _cp1250ToUTF32[128]; Common::String convertString(const Common::String &s); diff --git a/engines/sludge/floor.cpp b/engines/sludge/floor.cpp index 71aa75cbe7..c51fcc4309 100644 --- a/engines/sludge/floor.cpp +++ b/engines/sludge/floor.cpp @@ -28,23 +28,33 @@ #include "sludge/graphics.h" #include "sludge/moreio.h" #include "sludge/newfatal.h" +#include "sludge/people.h" #include "sludge/sludge.h" +#define ANGLEFIX (180.0 / 3.14157) + namespace Sludge { -Floor *currentFloor = NULL; +FloorManager::FloorManager(SludgeEngine *vm) { + _vm = vm; + _currentFloor = nullptr; +} -bool pointInFloorPolygon(FloorPolygon &floorPoly, int x, int y) { +FloorManager::~FloorManager() { + kill(); +} + +bool FloorManager::pointInFloorPolygon(FloorPolygon &floorPoly, int x, int y) { int i = 0, j, c = 0; float xp_i, yp_i; float xp_j, yp_j; for (j = floorPoly.numVertices - 1; i < floorPoly.numVertices; j = i++) { - xp_i = currentFloor->vertex[floorPoly.vertexID[i]].x; - yp_i = currentFloor->vertex[floorPoly.vertexID[i]].y; - xp_j = currentFloor->vertex[floorPoly.vertexID[j]].x; - yp_j = currentFloor->vertex[floorPoly.vertexID[j]].y; + xp_i = _currentFloor->vertex[floorPoly.vertexID[i]].x; + yp_i = _currentFloor->vertex[floorPoly.vertexID[i]].y; + xp_j = _currentFloor->vertex[floorPoly.vertexID[j]].x; + yp_j = _currentFloor->vertex[floorPoly.vertexID[j]].y; if ((((yp_i <= y) && (y < yp_j)) || ((yp_j <= y) && (y < yp_i))) && (x < (xp_j - xp_i) * (y - yp_i) / (yp_j - yp_i) + xp_i)) { c = !c; @@ -53,7 +63,7 @@ bool pointInFloorPolygon(FloorPolygon &floorPoly, int x, int y) { return c; } -bool getMatchingCorners(FloorPolygon &a, FloorPolygon &b, int &cornerA, int &cornerB) { +bool FloorManager::getMatchingCorners(FloorPolygon &a, FloorPolygon &b, int &cornerA, int &cornerB) { int sharedVertices = 0; int i, j; @@ -73,7 +83,7 @@ bool getMatchingCorners(FloorPolygon &a, FloorPolygon &b, int &cornerA, int &cor return false; } -bool polysShareSide(FloorPolygon &a, FloorPolygon &b) { +bool FloorManager::polysShareSide(FloorPolygon &a, FloorPolygon &b) { int sharedVertices = 0; int i, j; @@ -89,46 +99,45 @@ bool polysShareSide(FloorPolygon &a, FloorPolygon &b) { return false; } -void noFloor() { - currentFloor->numPolygons = 0; - currentFloor->polygon = NULL; - currentFloor->vertex = NULL; - currentFloor->matrix = NULL; -} - -bool initFloor() { - currentFloor = new Floor; - if (!checkNew(currentFloor)) +bool FloorManager::init() { + _currentFloor = new Floor; + if (!checkNew(_currentFloor)) return false; - noFloor(); + _currentFloor->numPolygons = 0; + _currentFloor->polygon = nullptr; + _currentFloor->vertex = nullptr; + _currentFloor->matrix = nullptr; return true; } -void killFloor() { - if (currentFloor) { - for (int i = 0; i < currentFloor->numPolygons; i++) { - delete []currentFloor->polygon[i].vertexID; - delete []currentFloor->matrix[i]; +void FloorManager::setFloorNull() { + if (_currentFloor) { + for (int i = 0; i < _currentFloor->numPolygons; i++) { + delete[] _currentFloor->polygon[i].vertexID; + delete[] _currentFloor->matrix[i]; } - delete []currentFloor->polygon; - currentFloor->polygon = NULL; - delete []currentFloor->vertex; - currentFloor->vertex = NULL; - delete []currentFloor->matrix; - currentFloor->matrix = NULL; + delete[] _currentFloor->polygon; + _currentFloor->polygon = nullptr; + delete[] _currentFloor->vertex; + _currentFloor->vertex = nullptr; + delete[] _currentFloor->matrix; + _currentFloor->matrix = nullptr; } } -void setFloorNull() { - killFloor(); - noFloor(); +void FloorManager::kill() { + setFloorNull(); + if (_currentFloor) { + delete _currentFloor; + _currentFloor = nullptr; + } } -bool setFloor(int fileNum) { +bool FloorManager::setFloor(int fileNum) { int i, j; - killFloor(); + setFloorNull(); setResourceForFatal(fileNum); @@ -137,73 +146,73 @@ bool setFloor(int fileNum) { // Find out how many polygons there are and reserve memory - currentFloor->originalNum = fileNum; - currentFloor->numPolygons = g_sludge->_resMan->getData()->readByte(); - currentFloor->polygon = new FloorPolygon[currentFloor->numPolygons]; - if (!checkNew(currentFloor->polygon)) + _currentFloor->originalNum = fileNum; + _currentFloor->numPolygons = g_sludge->_resMan->getData()->readByte(); + _currentFloor->polygon = new FloorPolygon[_currentFloor->numPolygons]; + if (!checkNew(_currentFloor->polygon)) return false; // Read in each polygon - for (i = 0; i < currentFloor->numPolygons; i++) { + for (i = 0; i < _currentFloor->numPolygons; i++) { // Find out how many vertex IDs there are and reserve memory - currentFloor->polygon[i].numVertices = g_sludge->_resMan->getData()->readByte(); - currentFloor->polygon[i].vertexID = new int[currentFloor->polygon[i].numVertices]; - if (!checkNew(currentFloor->polygon[i].vertexID)) + _currentFloor->polygon[i].numVertices = g_sludge->_resMan->getData()->readByte(); + _currentFloor->polygon[i].vertexID = new int[_currentFloor->polygon[i].numVertices]; + if (!checkNew(_currentFloor->polygon[i].vertexID)) return false; // Read in each vertex ID - for (j = 0; j < currentFloor->polygon[i].numVertices; j++) { - currentFloor->polygon[i].vertexID[j] = g_sludge->_resMan->getData()->readUint16BE(); + for (j = 0; j < _currentFloor->polygon[i].numVertices; j++) { + _currentFloor->polygon[i].vertexID[j] = g_sludge->_resMan->getData()->readUint16BE(); } } // Find out how many vertices there are and reserve memory i = g_sludge->_resMan->getData()->readUint16BE(); - currentFloor->vertex = new Common::Point[i]; - if (!checkNew(currentFloor->vertex)) + _currentFloor->vertex = new Common::Point[i]; + if (!checkNew(_currentFloor->vertex)) return false; for (j = 0; j < i; j++) { - currentFloor->vertex[j].x = g_sludge->_resMan->getData()->readUint16BE(); - currentFloor->vertex[j].y = g_sludge->_resMan->getData()->readUint16BE(); + _currentFloor->vertex[j].x = g_sludge->_resMan->getData()->readUint16BE(); + _currentFloor->vertex[j].y = g_sludge->_resMan->getData()->readUint16BE(); } g_sludge->_resMan->finishAccess(); // Now build the movement martix - currentFloor->matrix = new int *[currentFloor->numPolygons]; - int **distanceMatrix = new int *[currentFloor->numPolygons]; + _currentFloor->matrix = new int *[_currentFloor->numPolygons]; + int **distanceMatrix = new int *[_currentFloor->numPolygons]; - if (!checkNew(currentFloor->matrix)) + if (!checkNew(_currentFloor->matrix)) return false; - for (i = 0; i < currentFloor->numPolygons; i++) { - currentFloor->matrix[i] = new int[currentFloor->numPolygons]; - distanceMatrix[i] = new int[currentFloor->numPolygons]; - if (!checkNew(currentFloor->matrix[i])) + for (i = 0; i < _currentFloor->numPolygons; i++) { + _currentFloor->matrix[i] = new int[_currentFloor->numPolygons]; + distanceMatrix[i] = new int[_currentFloor->numPolygons]; + if (!checkNew(_currentFloor->matrix[i])) return false; - for (j = 0; j < currentFloor->numPolygons; j++) { - currentFloor->matrix[i][j] = -1; + for (j = 0; j < _currentFloor->numPolygons; j++) { + _currentFloor->matrix[i][j] = -1; distanceMatrix[i][j] = 10000; } } - for (i = 0; i < currentFloor->numPolygons; i++) { - for (j = 0; j < currentFloor->numPolygons; j++) { + for (i = 0; i < _currentFloor->numPolygons; i++) { + for (j = 0; j < _currentFloor->numPolygons; j++) { if (i != j) { - if (polysShareSide(currentFloor->polygon[i], currentFloor->polygon[j])) { - currentFloor->matrix[i][j] = j; + if (polysShareSide(_currentFloor->polygon[i], _currentFloor->polygon[j])) { + _currentFloor->matrix[i][j] = j; distanceMatrix[i][j] = 1; } } else { - currentFloor->matrix[i][j] = -2; + _currentFloor->matrix[i][j] = -2; distanceMatrix[i][j] = 0; } } @@ -214,17 +223,16 @@ bool setFloor(int fileNum) { do { lookForDistance++; -// debugMatrix (); madeChange = false; - for (i = 0; i < currentFloor->numPolygons; i++) { - for (j = 0; j < currentFloor->numPolygons; j++) { - if (currentFloor->matrix[i][j] == -1) { + for (i = 0; i < _currentFloor->numPolygons; i++) { + for (j = 0; j < _currentFloor->numPolygons; j++) { + if (_currentFloor->matrix[i][j] == -1) { // OK, so we don't know how to get from i to j... - for (int d = 0; d < currentFloor->numPolygons; d++) { + for (int d = 0; d < _currentFloor->numPolygons; d++) { if (d != i && d != j) { - if (currentFloor->matrix[i][d] == d && currentFloor->matrix[d][j] >= 0 && distanceMatrix[d][j] <= lookForDistance) { - currentFloor->matrix[i][j] = d; + if (_currentFloor->matrix[i][d] == d && _currentFloor->matrix[d][j] >= 0 && distanceMatrix[d][j] <= lookForDistance) { + _currentFloor->matrix[i][j] = d; distanceMatrix[i][j] = lookForDistance + 1; madeChange = true; } @@ -235,44 +243,44 @@ bool setFloor(int fileNum) { } } while (madeChange); - for (i = 0; i < currentFloor->numPolygons; i++) { + for (i = 0; i < _currentFloor->numPolygons; i++) { delete[] distanceMatrix[i]; } delete []distanceMatrix; - distanceMatrix = NULL; + distanceMatrix = nullptr; setResourceForFatal(-1); return true; } -void drawFloor() { +void FloorManager::drawFloor() { int i, j, nV; - for (i = 0; i < currentFloor->numPolygons; i++) { - nV = currentFloor->polygon[i].numVertices; + for (i = 0; i < _currentFloor->numPolygons; i++) { + nV = _currentFloor->polygon[i].numVertices; if (nV > 1) { for (j = 1; j < nV; j++) { - g_sludge->_gfxMan->drawLine(currentFloor->vertex[currentFloor->polygon[i].vertexID[j - 1]].x, currentFloor->vertex[currentFloor->polygon[i].vertexID[j - 1]].y, - currentFloor->vertex[currentFloor->polygon[i].vertexID[j]].x, currentFloor->vertex[currentFloor->polygon[i].vertexID[j]].y); + g_sludge->_gfxMan->drawLine(_currentFloor->vertex[_currentFloor->polygon[i].vertexID[j - 1]].x, _currentFloor->vertex[_currentFloor->polygon[i].vertexID[j - 1]].y, + _currentFloor->vertex[_currentFloor->polygon[i].vertexID[j]].x, _currentFloor->vertex[_currentFloor->polygon[i].vertexID[j]].y); } - g_sludge->_gfxMan->drawLine(currentFloor->vertex[currentFloor->polygon[i].vertexID[0]].x, currentFloor->vertex[currentFloor->polygon[i].vertexID[0]].y, - currentFloor->vertex[currentFloor->polygon[i].vertexID[nV - 1]].x, currentFloor->vertex[currentFloor->polygon[i].vertexID[nV - 1]].y); + g_sludge->_gfxMan->drawLine(_currentFloor->vertex[_currentFloor->polygon[i].vertexID[0]].x, _currentFloor->vertex[_currentFloor->polygon[i].vertexID[0]].y, + _currentFloor->vertex[_currentFloor->polygon[i].vertexID[nV - 1]].x, _currentFloor->vertex[_currentFloor->polygon[i].vertexID[nV - 1]].y); } } } -int inFloor(int x, int y) { +int FloorManager::inFloor(int x, int y) { int i, r = -1; - for (i = 0; i < currentFloor->numPolygons; i++) - if (pointInFloorPolygon(currentFloor->polygon[i], x, y)) + for (i = 0; i < _currentFloor->numPolygons; i++) + if (pointInFloorPolygon(_currentFloor->polygon[i], x, y)) r = i; return r; } -bool closestPointOnLine(int &closestX, int &closestY, int x1, int y1, int x2, int y2, int xP, int yP) { +bool FloorManager::closestPointOnLine(int &closestX, int &closestY, int x1, int y1, int x2, int y2, int xP, int yP) { int xDiff = x2 - x1; int yDiff = y2 - y1; @@ -293,4 +301,126 @@ bool closestPointOnLine(int &closestX, int &closestY, int x1, int y1, int x2, in return false; } +bool FloorManager::handleClosestPoint(int &setX, int &setY, int &setPoly) { + int gotX = 320, gotY = 200, gotPoly = -1, i, j, xTest1, yTest1, xTest2, yTest2, closestX, closestY, oldJ, currentDistance = 0xFFFFF, thisDistance; + + for (i = 0; i < _currentFloor->numPolygons; i++) { + oldJ = _currentFloor->polygon[i].numVertices - 1; + for (j = 0; j < _currentFloor->polygon[i].numVertices; j++) { + xTest1 = _currentFloor->vertex[_currentFloor->polygon[i].vertexID[j]].x; + yTest1 = _currentFloor->vertex[_currentFloor->polygon[i].vertexID[j]].y; + xTest2 = _currentFloor->vertex[_currentFloor->polygon[i].vertexID[oldJ]].x; + yTest2 = _currentFloor->vertex[_currentFloor->polygon[i].vertexID[oldJ]].y; + closestPointOnLine(closestX, closestY, xTest1, yTest1, xTest2, yTest2, setX, setY); + xTest1 = setX - closestX; + yTest1 = setY - closestY; + thisDistance = xTest1 * xTest1 + yTest1 * yTest1; + + if (thisDistance < currentDistance) { + currentDistance = thisDistance; + gotX = closestX; + gotY = closestY; + gotPoly = i; + } + oldJ = j; + } + } + + if (gotPoly == -1) + return false; + setX = gotX; + setY = gotY; + setPoly = gotPoly; + return true; +} + +bool FloorManager::doBorderStuff(OnScreenPerson *moveMe) { + if (moveMe->inPoly == moveMe->walkToPoly) { + moveMe->inPoly = -1; + moveMe->thisStepX = moveMe->walkToX; + moveMe->thisStepY = moveMe->walkToY; + } else { + // The section in which we need to be next... + int newPoly = _currentFloor->matrix[moveMe->inPoly][moveMe->walkToPoly]; + if (newPoly == -1) + return false; + + // Grab the index of the second matching corner... + int ID, ID2; + if (!getMatchingCorners(_currentFloor->polygon[moveMe->inPoly], _currentFloor->polygon[newPoly], ID, ID2)) + return fatal("Not a valid floor plan!"); + + // Remember that we're walking to the new polygon... + moveMe->inPoly = newPoly; + + // Calculate the destination position on the coincidantal line... + int x1 = moveMe->x, y1 = moveMe->y; + int x2 = moveMe->walkToX, y2 = moveMe->walkToY; + int x3 = _currentFloor->vertex[ID].x, y3 = _currentFloor->vertex[ID].y; + int x4 = _currentFloor->vertex[ID2].x, y4 = _currentFloor->vertex[ID2].y; + + int xAB = x1 - x2; + int yAB = y1 - y2; + int xCD = x4 - x3; + int yCD = y4 - y3; + + double m = (yAB * (x3 - x1) - xAB * (y3 - y1)); + m /= ((xAB * yCD) - (yAB * xCD)); + + if (m > 0 && m < 1) { + moveMe->thisStepX = x3 + m * xCD; + moveMe->thisStepY = y3 + m * yCD; + } else { + int dx13 = x1 - x3, dx14 = x1 - x4, dx23 = x2 - x3, dx24 = x2 - x4; + int dy13 = y1 - y3, dy14 = y1 - y4, dy23 = y2 - y3, dy24 = y2 - y4; + + dx13 *= dx13; + dx14 *= dx14; + dx23 *= dx23; + dx24 *= dx24; + dy13 *= dy13; + dy14 *= dy14; + dy23 *= dy23; + dy24 *= dy24; + + if (sqrt((double)dx13 + dy13) + sqrt((double)dx23 + dy23) < sqrt((double)dx14 + dy14) + sqrt((double)dx24 + dy24)) { + moveMe->thisStepX = x3; + moveMe->thisStepY = y3; + } else { + moveMe->thisStepX = x4; + moveMe->thisStepY = y4; + } + } + } + + float yDiff = moveMe->thisStepY - moveMe->y; + float xDiff = moveMe->x - moveMe->thisStepX; + if (xDiff || yDiff) { + moveMe->wantAngle = 180 + ANGLEFIX * atan2(xDiff, yDiff * 2); + moveMe->spinning = true; + } + + moveMe->makeTalker(); + return true; +} + +void FloorManager::save(Common::WriteStream *stream) { + if (_currentFloor->numPolygons) { + stream->writeByte(1); + stream->writeUint16BE(_currentFloor->originalNum); + } else { + stream->writeByte(0); + } +} + +bool FloorManager::load(Common::SeekableReadStream *stream) { + if (stream->readByte()) { + if (!setFloor(stream->readUint16BE())) + return false; + } else { + setFloorNull(); + } + return true; +} + } // End of namespace Sludge diff --git a/engines/sludge/floor.h b/engines/sludge/floor.h index 4db7e22deb..22c8b12f30 100644 --- a/engines/sludge/floor.h +++ b/engines/sludge/floor.h @@ -26,6 +26,9 @@ namespace Sludge { +class SludgeEngine; +struct OnScreenPerson; + struct FloorPolygon { int numVertices; int *vertexID; @@ -39,13 +42,37 @@ struct Floor { int **matrix; }; -bool initFloor(); -void setFloorNull(); -bool setFloor(int fileNum); -void drawFloor(); -int inFloor(int x, int y); -bool getMatchingCorners(FloorPolygon &, FloorPolygon &, int &, int &); -bool closestPointOnLine(int &closestX, int &closestY, int x1, int y1, int x2, int y2, int xP, int yP); +class FloorManager { +public: + FloorManager(SludgeEngine *vm); + ~FloorManager(); + + bool init(); + void kill(); + + void setFloorNull(); + bool setFloor(int fileNum); + void drawFloor(); + int inFloor(int x, int y); + bool isFloorNoPolygon() { return !_currentFloor || _currentFloor->numPolygons == 0; } + + // For Person collision detection + bool handleClosestPoint(int &setX, int &setY, int &setPoly); + bool doBorderStuff(OnScreenPerson *moveMe); + + // Save & load + void save(Common::WriteStream *stream); + bool load(Common::SeekableReadStream *stream); + +private: + Floor *_currentFloor; + SludgeEngine *_vm; + + bool getMatchingCorners(FloorPolygon &, FloorPolygon &, int &, int &); + bool closestPointOnLine(int &closestX, int &closestY, int x1, int y1, int x2, int y2, int xP, int yP); + bool pointInFloorPolygon(FloorPolygon &floorPoly, int x, int y); + bool polysShareSide(FloorPolygon &a, FloorPolygon &b); +}; } // End of namespace Sludge diff --git a/engines/sludge/fonttext.cpp b/engines/sludge/fonttext.cpp index 0f63c6e24f..da380f4f0b 100644 --- a/engines/sludge/fonttext.cpp +++ b/engines/sludge/fonttext.cpp @@ -48,11 +48,13 @@ void TextManager::init() { _loadedFontNum = 0; _fontSpace = -1; + _pastePalette.init(); _fontTable.clear(); } void TextManager::kill() { GraphicsManager::forgetSpriteBank(_theFont); + _pastePalette.kill(); } bool TextManager::isInFont(const Common::String &theText) { @@ -110,7 +112,7 @@ void TextManager::pasteString(const Common::String &theText, int xOff, int y, Sp } } -void TextManager::pasteStringToBackdrop(const Common::String &theText, int xOff, int y, SpritePalette &thePal) { +void TextManager::pasteStringToBackdrop(const Common::String &theText, int xOff, int y) { if (_fontTable.empty()) return; @@ -120,12 +122,12 @@ void TextManager::pasteStringToBackdrop(const Common::String &theText, int xOff, for (uint32 i = 0; i < str32.size(); ++i) { uint32 c = str32[i]; Sprite *mySprite = &_theFont.sprites[fontInTable(c)]; - g_sludge->_gfxMan->pasteSpriteToBackDrop(xOff, y, *mySprite, thePal); + g_sludge->_gfxMan->pasteSpriteToBackDrop(xOff, y, *mySprite, _pastePalette); xOff += mySprite->surface.w + _fontSpace; } } -void TextManager::burnStringToBackdrop(const Common::String &theText, int xOff, int y, SpritePalette &thePal) { +void TextManager::burnStringToBackdrop(const Common::String &theText, int xOff, int y) { if (_fontTable.empty()) return; @@ -135,17 +137,11 @@ void TextManager::burnStringToBackdrop(const Common::String &theText, int xOff, for (uint i = 0; i < str32.size(); ++i) { uint32 c = str32[i]; Sprite *mySprite = &_theFont.sprites[fontInTable(c)]; - g_sludge->_gfxMan->burnSpriteToBackDrop(xOff, y, *mySprite, thePal); + g_sludge->_gfxMan->burnSpriteToBackDrop(xOff, y, *mySprite, _pastePalette); xOff += mySprite->surface.w + _fontSpace; } } -void setFontColour(SpritePalette &sP, byte r, byte g, byte b) { - sP.originalRed = r; - sP.originalGreen = g; - sP.originalBlue = b; -} - bool TextManager::loadFont(int filenum, const Common::String &charOrder, int h) { _fontOrder.setUTF8String(charOrder); diff --git a/engines/sludge/fonttext.h b/engines/sludge/fonttext.h index 26b12d9f11..7018c75213 100644 --- a/engines/sludge/fonttext.h +++ b/engines/sludge/fonttext.h @@ -46,12 +46,14 @@ public: bool loadFont(int filenum, const Common::String &charOrder, int); void pasteString(const Common::String &theText, int, int, SpritePalette &); - void pasteStringToBackdrop(const Common::String &theText, int xOff, int y, SpritePalette &thePal); - void burnStringToBackdrop(const Common::String &theText, int xOff, int y, SpritePalette &thePal); + void pasteStringToBackdrop(const Common::String &theText, int xOff, int y); + void burnStringToBackdrop(const Common::String &theText, int xOff, int y); bool isInFont(const Common::String &theText); + // setter & getter void setFontSpace(int fontSpace) { _fontSpace = fontSpace; } int getFontHeight() const { return _fontHeight; } + void setPasterColor(byte r, byte g, byte b) { _pastePalette.setColor(r, g, b); } // load & save void saveFont(Common::WriteStream *stream); @@ -62,6 +64,7 @@ private: int _fontHeight, _numFontColours, _loadedFontNum; UTF8Converter _fontOrder; int16 _fontSpace; + SpritePalette _pastePalette; Common::HashMap<uint32, uint32> _fontTable; @@ -69,8 +72,6 @@ private: }; -void setFontColour(SpritePalette &sP, byte r, byte g, byte b); - } // End of namespace Sludge #endif diff --git a/engines/sludge/freeze.cpp b/engines/sludge/freeze.cpp index e90f2700d8..9f8c85e828 100644 --- a/engines/sludge/freeze.cpp +++ b/engines/sludge/freeze.cpp @@ -41,10 +41,6 @@ namespace Sludge { -extern OnScreenPerson *allPeople; -extern ScreenRegion *allScreenRegions; -extern ScreenRegion *overRegion; - void GraphicsManager::freezeGraphics() { int w = _winWidth; @@ -87,18 +83,14 @@ bool GraphicsManager::freeze() { _backdropSurface.copyFrom(_freezeSurface); _backdropExists = true; - newFreezer->allPeople = allPeople; - allPeople = NULL; + _vm->_peopleMan->freeze(newFreezer); - StatusStuff *newStatusStuff = new StatusStuff ; + StatusStuff *newStatusStuff = new StatusStuff; if (!checkNew(newStatusStuff)) return false; newFreezer->frozenStatus = copyStatusBarStuff(newStatusStuff); - newFreezer->allScreenRegions = allScreenRegions; - allScreenRegions = NULL; - overRegion = NULL; - + _vm->_regionMan->freeze(newFreezer); _vm->_cursorMan->freeze(newFreezer); _vm->_speechMan->freeze(newFreezer); _vm->_evtMan->freeze(newFreezer); @@ -136,11 +128,8 @@ void GraphicsManager::unfreeze(bool killImage) { _vm->_evtMan->mouseX() = (int)(_vm->_evtMan->mouseX() / _cameraZoom); _vm->_evtMan->mouseY() = (int)(_vm->_evtMan->mouseY() / _cameraZoom); - killAllPeople(); - allPeople = _frozenStuff->allPeople; - - killAllRegions(); - allScreenRegions = _frozenStuff->allScreenRegions; + g_sludge->_peopleMan->resotre(_frozenStuff); + g_sludge->_regionMan->resotre(_frozenStuff); killLightMap(); @@ -171,7 +160,6 @@ void GraphicsManager::unfreeze(bool killImage) { _vm->_speechMan->restore(_frozenStuff); _frozenStuff = _frozenStuff->next; - overRegion = NULL; // free current frozen screen struct if (killMe->backdropSurface.getPixels()) diff --git a/engines/sludge/freeze.h b/engines/sludge/freeze.h index 830c2d8a72..4a7740887f 100644 --- a/engines/sludge/freeze.h +++ b/engines/sludge/freeze.h @@ -30,14 +30,18 @@ struct OnScreenPerson; struct PersonaAnimation; struct ScreenRegion; struct SpeechStruct; -struct StatusStuff ; +struct StatusStuff; struct EventHandlers; +struct ScreenRegion; + +typedef Common::List<ScreenRegion *> ScreenRegionList; +typedef Common::List<OnScreenPerson *> OnScreenPersonList; class Parallax; struct FrozenStuffStruct { - OnScreenPerson *allPeople; - ScreenRegion *allScreenRegions; + OnScreenPersonList *allPeople; + ScreenRegionList *allScreenRegions; Graphics::Surface backdropSurface; Graphics::Surface lightMapSurface; Graphics::Surface *zBufferSprites; diff --git a/engines/sludge/function.cpp b/engines/sludge/function.cpp new file mode 100644 index 0000000000..553e59d76c --- /dev/null +++ b/engines/sludge/function.cpp @@ -0,0 +1,788 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "sludge/builtin.h" +#include "sludge/function.h" +#include "sludge/loadsave.h" +#include "sludge/newfatal.h" +#include "sludge/people.h" +#include "sludge/sludge.h" +#include "sludge/sound.h" +#include "sludge/speech.h" + +namespace Sludge { + +int numBIFNames = 0; +Common::String *allBIFNames = NULL; +int numUserFunc = 0; +Common::String *allUserFunc = NULL; + +LoadedFunction *saverFunc; +LoadedFunction *allRunningFunctions = NULL; +VariableStack *noStack = NULL; +Variable *globalVars = NULL; + +const char *sludgeText[] = { "?????", "RETURN", "BRANCH", "BR_ZERO", + "SET_GLOBAL", "SET_LOCAL", "LOAD_GLOBAL", "LOAD_LOCAL", "PLUS", "MINUS", + "MULT", "DIVIDE", "AND", "OR", "EQUALS", "NOT_EQ", "MODULUS", + "LOAD_VALUE", "LOAD_BUILT", "LOAD_FUNC", "CALLIT", "LOAD_STRING", + "LOAD_FILE", "LOAD_OBJTYPE", "NOT", "LOAD_NULL", "STACK_PUSH", + "LESSTHAN", "MORETHAN", "NEGATIVE", "U", "LESS_EQUAL", "MORE_EQUAL", + "INC_LOCAL", "DEC_LOCAL", "INC_GLOBAL", "DEC_GLOBAL", "INDEXSET", + "INDEXGET", "INC_INDEX", "DEC_INDEX", "QUICK_PUSH" }; + +void pauseFunction(LoadedFunction *fun) { + LoadedFunction **huntAndDestroy = &allRunningFunctions; + while (*huntAndDestroy) { + if (fun == *huntAndDestroy) { + (*huntAndDestroy) = (*huntAndDestroy)->next; + fun->next = NULL; + } else { + huntAndDestroy = &(*huntAndDestroy)->next; + } + } +} + +void restartFunction(LoadedFunction *fun) { + fun->next = allRunningFunctions; + allRunningFunctions = fun; +} + +void killSpeechTimers() { + LoadedFunction *thisFunction = allRunningFunctions; + + while (thisFunction) { + if (thisFunction->freezerLevel == 0 && thisFunction->isSpeech + && thisFunction->timeLeft) { + thisFunction->timeLeft = 0; + thisFunction->isSpeech = false; + } + thisFunction = thisFunction->next; + } + + g_sludge->_speechMan->kill(); +} + +void completeTimers() { + LoadedFunction *thisFunction = allRunningFunctions; + + while (thisFunction) { + if (thisFunction->freezerLevel == 0) + thisFunction->timeLeft = 0; + thisFunction = thisFunction->next; + } +} + +void finishFunction(LoadedFunction *fun) { + int a; + + pauseFunction(fun); + if (fun->stack) + fatal(ERROR_NON_EMPTY_STACK); + delete[] fun->compiledLines; + for (a = 0; a < fun->numLocals; a++) + fun->localVars[a].unlinkVar(); + delete[] fun->localVars; + fun->reg.unlinkVar(); + delete fun; + fun = NULL; +} + +void abortFunction(LoadedFunction *fun) { + int a; + + pauseFunction(fun); + while (fun->stack) + trimStack(fun->stack); + delete []fun->compiledLines; + for (a = 0; a < fun->numLocals; a++) + fun->localVars[a].unlinkVar(); + delete []fun->localVars; + fun->reg.unlinkVar(); + if (fun->calledBy) + abortFunction(fun->calledBy); + delete fun; + fun = NULL; +} + +int cancelAFunction(int funcNum, LoadedFunction *myself, bool &killedMyself) { + int n = 0; + killedMyself = false; + + LoadedFunction *fun = allRunningFunctions; + while (fun) { + if (fun->originalNumber == funcNum) { + fun->cancelMe = true; + n++; + if (fun == myself) + killedMyself = true; + } + fun = fun->next; + } + return n; +} + +void freezeSubs() { + LoadedFunction *thisFunction = allRunningFunctions; + + while (thisFunction) { + if (thisFunction->unfreezable) { + //msgBox ("SLUDGE debugging bollocks!", "Trying to freeze an unfreezable function!"); + } else { + thisFunction->freezerLevel++; + } + thisFunction = thisFunction->next; + } +} + +void unfreezeSubs() { + LoadedFunction *thisFunction = allRunningFunctions; + + while (thisFunction) { + if (thisFunction->freezerLevel) + thisFunction->freezerLevel--; + thisFunction = thisFunction->next; + } +} + +bool continueFunction(LoadedFunction *fun) { + bool keepLooping = true; + bool advanceNow; + uint param; + SludgeCommand com; + + if (fun->cancelMe) { + abortFunction(fun); + return true; + } + + while (keepLooping) { + advanceNow = true; + debugC(1, kSludgeDebugStackMachine, "Executing command line %i : ", fun->runThisLine); + param = fun->compiledLines[fun->runThisLine].param; + com = fun->compiledLines[fun->runThisLine].theCommand; + + if (numBIFNames) { + setFatalInfo((fun->originalNumber < numUserFunc) ? allUserFunc[fun->originalNumber] : "Unknown user function", (com < numSludgeCommands) ? sludgeText[com] : ERROR_UNKNOWN_MCODE); + } + + switch (com) { + case SLU_RETURN: + if (fun->calledBy) { + LoadedFunction *returnTo = fun->calledBy; + if (fun->returnSomething) + returnTo->reg.copyFrom(fun->reg); + finishFunction(fun); + fun = returnTo; + restartFunction(fun); + } else { + finishFunction(fun); + advanceNow = false; // So we don't do anything else with "fun" + keepLooping = false; // So we drop out of the loop + } + break; + + case SLU_CALLIT: + switch (fun->reg.varType) { + case SVT_FUNC: + pauseFunction(fun); + if (numBIFNames) + setFatalInfo( + (fun->originalNumber < numUserFunc) ? + allUserFunc[fun->originalNumber] : + "Unknown user function", + (fun->reg.varData.intValue < numUserFunc) ? + allUserFunc[fun->reg.varData.intValue] : + "Unknown user function"); + + if (!startNewFunctionNum(fun->reg.varData.intValue, param, fun, + fun->stack)) + return false; + fun = allRunningFunctions; + advanceNow = false; // So we don't do anything else with "fun" + break; + + case SVT_BUILT: { + debugC(1, kSludgeDebugStackMachine, "Built-in init value: %i", + fun->reg.varData.intValue); + BuiltReturn br = callBuiltIn(fun->reg.varData.intValue, param, + fun); + + switch (br) { + case BR_ERROR: + return fatal( + "Unknown error. This shouldn't happen. Please notify the SLUDGE developers."); + + case BR_PAUSE: + pauseFunction(fun); + // fall through + + case BR_KEEP_AND_PAUSE: + keepLooping = false; + break; + + case BR_ALREADY_GONE: + keepLooping = false; + advanceNow = false; + break; + + case BR_CALLAFUNC: { + int i = fun->reg.varData.intValue; + fun->reg.setVariable(SVT_INT, 1); + pauseFunction(fun); + if (numBIFNames) + setFatalInfo( + (fun->originalNumber < numUserFunc) ? + allUserFunc[fun->originalNumber] : + "Unknown user function", + (i < numUserFunc) ? + allUserFunc[i] : + "Unknown user function"); + if (!startNewFunctionNum(i, 0, fun, noStack, false)) + return false; + fun = allRunningFunctions; + advanceNow = false; // So we don't do anything else with "fun" + } + break; + + default: + break; + } + } + break; + + default: + return fatal(ERROR_CALL_NONFUNCTION); + } + break; + + // These all grab things and shove 'em into the register + + case SLU_LOAD_NULL: + fun->reg.setVariable(SVT_NULL, 0); + break; + + case SLU_LOAD_FILE: + fun->reg.setVariable(SVT_FILE, param); + break; + + case SLU_LOAD_VALUE: + fun->reg.setVariable(SVT_INT, param); + break; + + case SLU_LOAD_LOCAL: + if (!fun->reg.copyFrom(fun->localVars[param])) + return false; + break; + + case SLU_AND: + fun->reg.setVariable(SVT_INT, + fun->reg.getBoolean() && fun->stack->thisVar.getBoolean()); + trimStack(fun->stack); + break; + + case SLU_OR: + fun->reg.setVariable(SVT_INT, + fun->reg.getBoolean() || fun->stack->thisVar.getBoolean()); + trimStack(fun->stack); + break; + + case SLU_LOAD_FUNC: + fun->reg.setVariable(SVT_FUNC, param); + break; + + case SLU_LOAD_BUILT: + fun->reg.setVariable(SVT_BUILT, param); + break; + + case SLU_LOAD_OBJTYPE: + fun->reg.setVariable(SVT_OBJTYPE, param); + break; + + case SLU_UNREG: + break; + + case SLU_LOAD_STRING: + if (!fun->reg.loadStringToVar(param)) { + return false; + } + break; + + case SLU_INDEXGET: + case SLU_INCREMENT_INDEX: + case SLU_DECREMENT_INDEX: + switch (fun->stack->thisVar.varType) { + case SVT_NULL: + if (com == SLU_INDEXGET) { + fun->reg.setVariable(SVT_NULL, 0); + trimStack(fun->stack); + } else { + return fatal(ERROR_INCDEC_UNKNOWN); + } + break; + + case SVT_FASTARRAY: + case SVT_STACK: + if (fun->stack->thisVar.varData.theStack->first == NULL) { + return fatal(ERROR_INDEX_EMPTY); + } else { + int ii; + if (!fun->reg.getValueType(ii, SVT_INT)) + return false; + Variable *grab = + (fun->stack->thisVar.varType == SVT_FASTARRAY) ? + fun->stack->thisVar.varData.fastArray->fastArrayGetByIndex(ii) : + fun->stack->thisVar.varData.theStack->first->stackGetByIndex(ii); + + trimStack(fun->stack); + + if (!grab) { + fun->reg.setVariable(SVT_NULL, 0); + } else { + int kk; + switch (com) { + case SLU_INCREMENT_INDEX: + if (!grab->getValueType(kk, SVT_INT)) + return false; + fun->reg.setVariable(SVT_INT, kk); + grab->varData.intValue = kk + 1; + break; + + case SLU_DECREMENT_INDEX: + if (!grab->getValueType(kk, SVT_INT)) + return false; + fun->reg.setVariable(SVT_INT, kk); + grab->varData.intValue = kk - 1; + break; + + default: + if (!fun->reg.copyFrom(*grab)) + return false; + } + } + } + break; + + default: + return fatal(ERROR_INDEX_NONSTACK); + } + break; + + case SLU_INDEXSET: + switch (fun->stack->thisVar.varType) { + case SVT_STACK: + if (fun->stack->thisVar.varData.theStack->first == NULL) { + return fatal(ERROR_INDEX_EMPTY); + } else { + int ii; + if (!fun->reg.getValueType(ii, SVT_INT)) + return false; + if (!fun->stack->thisVar.varData.theStack->first->stackSetByIndex(ii, fun->stack->next->thisVar)) { + return false; + } + trimStack(fun->stack); + trimStack(fun->stack); + } + break; + + case SVT_FASTARRAY: { + int ii; + if (!fun->reg.getValueType(ii, SVT_INT)) + return false; + Variable *v = fun->stack->thisVar.varData.fastArray->fastArrayGetByIndex(ii); + if (v == NULL) + return fatal("Not within bounds of fast array."); + if (!v->copyFrom(fun->stack->next->thisVar)) + return false; + trimStack(fun->stack); + trimStack(fun->stack); + } + break; + + default: + return fatal(ERROR_INDEX_NONSTACK); + } + break; + + // What can we do with the register? Well, we can copy it into a local + // variable, a global or onto the stack... + + case SLU_INCREMENT_LOCAL: { + int ii; + if (!fun->localVars[param].getValueType(ii, SVT_INT)) + return false; + fun->reg.setVariable(SVT_INT, ii); + fun->localVars[param].setVariable(SVT_INT, ii + 1); + } + break; + + case SLU_INCREMENT_GLOBAL: { + int ii; + if (!globalVars[param].getValueType(ii, SVT_INT)) + return false; + fun->reg.setVariable(SVT_INT, ii); + globalVars[param].setVariable(SVT_INT, ii + 1); + } + break; + + case SLU_DECREMENT_LOCAL: { + int ii; + if (!fun->localVars[param].getValueType(ii, SVT_INT)) + return false; + fun->reg.setVariable(SVT_INT, ii); + fun->localVars[param].setVariable(SVT_INT, ii - 1); + } + break; + + case SLU_DECREMENT_GLOBAL: { + int ii; + if (!globalVars[param].getValueType(ii, SVT_INT)) + return false; + fun->reg.setVariable(SVT_INT, ii); + globalVars[param].setVariable(SVT_INT, ii - 1); + } + break; + + case SLU_SET_LOCAL: + if (!fun->localVars[param].copyFrom(fun->reg)) + return false; + break; + + case SLU_SET_GLOBAL: + if (!globalVars[param].copyFrom(fun->reg)) + return false; + break; + + case SLU_LOAD_GLOBAL: + if (!fun->reg.copyFrom(globalVars[param])) + return false; + break; + + case SLU_STACK_PUSH: + if (!addVarToStack(fun->reg, fun->stack)) + return false; + break; + + case SLU_QUICK_PUSH: + if (!addVarToStackQuick(fun->reg, fun->stack)) + return false; + break; + + case SLU_NOT: + fun->reg.setVariable(SVT_INT, !fun->reg.getBoolean()); + break; + + case SLU_BR_ZERO: + if (!fun->reg.getBoolean()) { + advanceNow = false; + fun->runThisLine = param; + } + break; + + case SLU_BRANCH: + advanceNow = false; + fun->runThisLine = param; + break; + + case SLU_NEGATIVE: { + int i; + if (!fun->reg.getValueType(i, SVT_INT)) + return false; + fun->reg.setVariable(SVT_INT, -i); + } + break; + + // All these things rely on there being somet' on the stack + + case SLU_MULT: + case SLU_PLUS: + case SLU_MINUS: + case SLU_MODULUS: + case SLU_DIVIDE: + case SLU_EQUALS: + case SLU_NOT_EQ: + case SLU_LESSTHAN: + case SLU_MORETHAN: + case SLU_LESS_EQUAL: + case SLU_MORE_EQUAL: + if (fun->stack) { + int firstValue, secondValue; + + switch (com) { + case SLU_PLUS: + fun->reg.addVariablesInSecond(fun->stack->thisVar); + trimStack(fun->stack); + break; + + case SLU_EQUALS: + fun->reg.compareVariablesInSecond(fun->stack->thisVar); + trimStack(fun->stack); + break; + + case SLU_NOT_EQ: + fun->reg.compareVariablesInSecond(fun->stack->thisVar); + trimStack(fun->stack); + fun->reg.varData.intValue = !fun->reg.varData.intValue; + break; + + default: + if (!fun->stack->thisVar.getValueType(firstValue, SVT_INT)) + return false; + if (!fun->reg.getValueType(secondValue, SVT_INT)) + return false; + trimStack(fun->stack); + + switch (com) { + case SLU_MULT: + fun->reg.setVariable(SVT_INT, + firstValue * secondValue); + break; + + case SLU_MINUS: + fun->reg.setVariable(SVT_INT, + firstValue - secondValue); + break; + + case SLU_MODULUS: + fun->reg.setVariable(SVT_INT, + firstValue % secondValue); + break; + + case SLU_DIVIDE: + fun->reg.setVariable(SVT_INT, + firstValue / secondValue); + break; + + case SLU_LESSTHAN: + fun->reg.setVariable(SVT_INT, + firstValue < secondValue); + break; + + case SLU_MORETHAN: + fun->reg.setVariable(SVT_INT, + firstValue > secondValue); + break; + + case SLU_LESS_EQUAL: + fun->reg.setVariable(SVT_INT, + firstValue <= secondValue); + break; + + case SLU_MORE_EQUAL: + fun->reg.setVariable(SVT_INT, + firstValue >= secondValue); + break; + + default: + break; + } + } + } else { + return fatal(ERROR_NOSTACK); + } + break; + + default: + return fatal(ERROR_UNKNOWN_CODE); + } + + if (advanceNow) + fun->runThisLine++; + + } + return true; +} + +void killAllFunctions() { + while (allRunningFunctions) + finishFunction(allRunningFunctions); +} + +bool loadFunctionCode(LoadedFunction *newFunc) { + uint numLines, numLinesRead; + + if (!g_sludge->_resMan->openSubSlice(newFunc->originalNumber)) + return false; + + debugC(3, kSludgeDebugDataLoad, "Load function code"); + + Common::SeekableReadStream *readStream = g_sludge->_resMan->getData(); + newFunc->unfreezable = readStream->readByte(); + numLines = readStream->readUint16BE(); + debugC(3, kSludgeDebugDataLoad, "numLines: %i", numLines); + newFunc->numArgs = readStream->readUint16BE(); + debugC(3, kSludgeDebugDataLoad, "numArgs: %i", newFunc->numArgs); + newFunc->numLocals = readStream->readUint16BE(); + debugC(3, kSludgeDebugDataLoad, "numLocals: %i", newFunc->numLocals); + newFunc->compiledLines = new LineOfCode[numLines]; + if (!checkNew(newFunc->compiledLines)) + return false; + + for (numLinesRead = 0; numLinesRead < numLines; numLinesRead++) { + newFunc->compiledLines[numLinesRead].theCommand = (SludgeCommand)readStream->readByte(); + newFunc->compiledLines[numLinesRead].param = readStream->readUint16BE(); + debugC(3, kSludgeDebugDataLoad, "command line %i: %i", numLinesRead, + newFunc->compiledLines[numLinesRead].theCommand); + } + g_sludge->_resMan->finishAccess(); + + // Now we need to reserve memory for the local variables + newFunc->localVars = new Variable[newFunc->numLocals]; + if (!checkNew(newFunc->localVars)) + return false; + + return true; +} + +int startNewFunctionNum(uint funcNum, uint numParamsExpected, + LoadedFunction *calledBy, VariableStack *&vStack, bool returnSommet) { + LoadedFunction *newFunc = new LoadedFunction; + checkNew(newFunc); + newFunc->originalNumber = funcNum; + + loadFunctionCode(newFunc); + + if (newFunc->numArgs != (int) numParamsExpected) + return fatal("Wrong number of parameters!"); + if (newFunc->numArgs > newFunc->numLocals) + return fatal("More arguments than local Variable space!"); + + // Now, lets copy the parameters from the calling function's stack... + + while (numParamsExpected) { + numParamsExpected--; + if (vStack == NULL) + return fatal( + "Corrupted file!The stack's empty and there were still parameters expected"); + newFunc->localVars[numParamsExpected].copyFrom(vStack->thisVar); + trimStack(vStack); + } + + newFunc->cancelMe = false; + newFunc->timeLeft = 0; + newFunc->returnSomething = returnSommet; + newFunc->calledBy = calledBy; + newFunc->stack = NULL; + newFunc->freezerLevel = 0; + newFunc->runThisLine = 0; + newFunc->isSpeech = 0; + + restartFunction(newFunc); + return 1; +} + +bool runAllFunctions() { + + LoadedFunction *thisFunction = allRunningFunctions; + LoadedFunction *nextFunction; + + while (thisFunction) { + nextFunction = thisFunction->next; + + if (!thisFunction->freezerLevel) { + if (thisFunction->timeLeft) { + if (thisFunction->timeLeft < 0) { + if (!g_sludge->_soundMan->stillPlayingSound( + g_sludge->_speechMan->getLastSpeechSound())) { + thisFunction->timeLeft = 0; + } + } else if (!--(thisFunction->timeLeft)) { + } + } else { + if (thisFunction->isSpeech) { + thisFunction->isSpeech = false; + g_sludge->_speechMan->kill(); + } + if (!continueFunction(thisFunction)) + return false; + } + } + + thisFunction = nextFunction; + } + + return true; +} + +void saveFunction(LoadedFunction *fun, Common::WriteStream *stream) { + int a; + stream->writeUint16BE(fun->originalNumber); + if (fun->calledBy) { + stream->writeByte(1); + saveFunction(fun->calledBy, stream); + } else { + stream->writeByte(0); + } + stream->writeUint32LE(fun->timeLeft); + stream->writeUint16BE(fun->runThisLine); + stream->writeByte(fun->cancelMe); + stream->writeByte(fun->returnSomething); + stream->writeByte(fun->isSpeech); + fun->reg.save(stream); + + if (fun->freezerLevel) { + fatal(ERROR_GAME_SAVE_FROZEN); + } + saveStack(fun->stack, stream); + for (a = 0; a < fun->numLocals; a++) { + fun->localVars[a].save(stream); + } +} + +LoadedFunction *loadFunction(Common::SeekableReadStream *stream) { + int a; + + // Reserve memory... + + LoadedFunction *buildFunc = new LoadedFunction; + if (!checkNew(buildFunc)) + return NULL; + + // See what it was called by and load if we need to... + + buildFunc->originalNumber = stream->readUint16BE(); + buildFunc->calledBy = NULL; + if (stream->readByte()) { + buildFunc->calledBy = loadFunction(stream); + if (!buildFunc->calledBy) + return NULL; + } + + buildFunc->timeLeft = stream->readUint32LE(); + buildFunc->runThisLine = stream->readUint16BE(); + buildFunc->freezerLevel = 0; + buildFunc->cancelMe = stream->readByte(); + buildFunc->returnSomething = stream->readByte(); + buildFunc->isSpeech = stream->readByte(); + buildFunc->reg.load(stream); + loadFunctionCode(buildFunc); + + buildFunc->stack = loadStack(stream, NULL); + + for (a = 0; a < buildFunc->numLocals; a++) { + buildFunc->localVars[a].load(stream); + } + + return buildFunc; +} + +} // End of namespace Sludge diff --git a/engines/sludge/function.h b/engines/sludge/function.h new file mode 100644 index 0000000000..005760a960 --- /dev/null +++ b/engines/sludge/function.h @@ -0,0 +1,73 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 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 SLUDGE_FUNCTION_H +#define SLUDGE_FUNCTION_H + +#include "sludge/allfiles.h" +#include "sludge/csludge.h" +#include "sludge/variable.h" + +namespace Sludge { + +struct Variable; +struct VariableStack; + +struct LineOfCode { + SludgeCommand theCommand; + int32 param; +}; + +struct LoadedFunction { + int originalNumber; + LineOfCode *compiledLines; + int numLocals, timeLeft, numArgs; + Variable *localVars; + VariableStack *stack; + Variable reg; + uint runThisLine; + LoadedFunction *calledBy; + LoadedFunction *next; + bool returnSomething, isSpeech, unfreezable, cancelMe; + byte freezerLevel; +}; + +bool runAllFunctions(); +int startNewFunctionNum(uint, uint, LoadedFunction *, VariableStack*&, bool = true); +void restartFunction(LoadedFunction *fun); +bool loadFunctionCode(LoadedFunction *newFunc); +void killAllFunctions(); + +void finishFunction(LoadedFunction *fun); +void abortFunction(LoadedFunction *fun); + +void freezeSubs(); +void unfreezeSubs(); +void completeTimers(); +void killSpeechTimers(); +int cancelAFunction(int funcNum, LoadedFunction *myself, bool &killedMyself); + +LoadedFunction *loadFunction(Common::SeekableReadStream *stream); +void saveFunction(LoadedFunction *fun, Common::WriteStream *stream); + +} // End of namespace Sludge + +#endif diff --git a/engines/sludge/functionlist.h b/engines/sludge/functionlist.h index 025f80a844..c7858a2d03 100644 --- a/engines/sludge/functionlist.h +++ b/engines/sludge/functionlist.h @@ -29,178 +29,178 @@ namespace Sludge { -#define FUNC(special,name) {builtIn_ ## name}, +#define FUNC(special,name,paramNum) {builtIn_ ## name, paramNum}, static builtInFunctionData builtInFunctionArray[] = { - FUNC(true, say) - FUNC(true, skipSpeech) - FUNC(true, statusText) - FUNC(true, pause) - FUNC(true, onLeftMouse) - FUNC(true, onRightMouse) - FUNC(true, setCursor) - FUNC(true, addOverlay) - FUNC(true, addCharacter) - FUNC(true, playSound) - FUNC(true, getMouseX) - FUNC(true, getMouseY) - FUNC(true, addScreenRegion) - FUNC(true, onMoveMouse) - FUNC(true, onFocusChange) - FUNC(true, getOverObject) - FUNC(true, blankScreen) - FUNC(true, moveCharacter) - FUNC(true, onKeyboard) - FUNC(true, getObjectX) - FUNC(true, getObjectY) - FUNC(true, random) - FUNC(true, spawnSub) - FUNC(true, blankArea) - FUNC(true, hideCharacter) - FUNC(true, showCharacter) - FUNC(true, callEvent) - FUNC(true, removeScreenRegion) - FUNC(true, animate) - FUNC(true, turnCharacter) - FUNC(true, removeAllCharacters) - FUNC(true, removeAllScreenRegions) - FUNC(true, setScale) - FUNC(true, newStack) - FUNC(true, pushToStack) - FUNC(true, popFromStack) - FUNC(true, clearStatus) - FUNC(true, addStatus) - FUNC(true, removeLastStatus) - FUNC(true, lightStatus) - FUNC(true, getStatusText) - FUNC(true, setStatusColour) - FUNC(true, deleteFromStack) - FUNC(true, freeze) - FUNC(true, unfreeze) - FUNC(true, pasteImage) - FUNC(true, copyStack) - FUNC(true, completeTimers) - FUNC(true, setCharacterDrawMode) - FUNC(true, anim) - FUNC(true, costume) - FUNC(true, pickOne) - FUNC(true, setCostume) - FUNC(true, wait) - FUNC(true, somethingSpeaking) - FUNC(true, substring) - FUNC(true, stringLength) - FUNC(true, darkBackground) - FUNC(true, saveGame) - FUNC(true, loadGame) - FUNC(true, quitGame) - FUNC(true, rename) - FUNC(true, stackSize) - FUNC(true, pasteString) - FUNC(true, startMusic) - FUNC(true, setDefaultMusicVolume) - FUNC(true, setMusicVolume) - FUNC(true, stopMusic) - FUNC(true, stopSound) - FUNC(true, setFont) - FUNC(true, alignStatus) - FUNC(true, showFloor) - FUNC(true, showBoxes) - FUNC(true, positionStatus) - FUNC(true, setFloor) - FUNC(true, forceCharacter) - FUNC(true, jumpCharacter) - FUNC(true, peekStart) - FUNC(true, peekEnd) - FUNC(true, enqueue) - FUNC(true, setZBuffer) - FUNC(true, getMatchingFiles) - FUNC(true, inFont) - FUNC(true, onLeftMouseUp) - FUNC(true, onRightMouseUp) - FUNC(true, loopSound) - FUNC(true, removeCharacter) - FUNC(true, stopCharacter) - FUNC(true, launch) - FUNC(true, howFrozen) - FUNC(true, setPasteColour) - FUNC(true, setLitStatusColour) - FUNC(true, fileExists) - FUNC(true, floatCharacter) - FUNC(true, cancelSub) - FUNC(true, setCharacterWalkSpeed) - FUNC(true, deleteAllFromStack) - FUNC(true, setCharacterExtra) - FUNC(true, mixOverlay) - FUNC(true, pasteCharacter) - FUNC(true, setSceneDimensions) - FUNC(true, aimCamera) - FUNC(true, getMouseScreenX) - FUNC(true, getMouseScreenY) - FUNC(true, setDefaultSoundVolume) - FUNC(true, setSoundVolume) - FUNC(true, setSoundLoopPoints) - FUNC(true, setSpeechMode) - FUNC(true, setLightMap) - FUNC(true, think) - FUNC(true, getCharacterDirection) - FUNC(true, isCharacter) - FUNC(true, isScreenRegion) - FUNC(true, isMoving) - FUNC(true, deleteFile) - FUNC(true, renameFile) - FUNC(true, hardScroll) - FUNC(true, stringWidth) - FUNC(true, setSpeechSpeed) - FUNC(true, normalCharacter) - FUNC(true, fetchEvent) - FUNC(true, transitionLevel) - FUNC(true, spinCharacter) - FUNC(true, setFontSpacing) - FUNC(true, burnString) - FUNC(true, captureAllKeys) - FUNC(true, cacheSound) - FUNC(true, setCharacterSpinSpeed) - FUNC(true, transitionMode) - FUNC(false, _rem_movieStart) - FUNC(false, _rem_movieAbort) - FUNC(false, _rem_moviePlaying) - FUNC(false, _rem_updateDisplay) - FUNC(true, getSoundCache) - FUNC(true, saveCustomData) - FUNC(true, loadCustomData) - FUNC(true, setCustomEncoding) - FUNC(true, freeSound) - FUNC(true, parallaxAdd) - FUNC(true, parallaxClear) - FUNC(true, setBlankColour) - FUNC(true, setBurnColour) - FUNC(true, getPixelColour) - FUNC(true, makeFastArray) - FUNC(true, getCharacterScale) - FUNC(true, getLanguageID) - FUNC(false, _rem_launchWith) - FUNC(true, getFramesPerSecond) - FUNC(true, showThumbnail) - FUNC(true, setThumbnailSize) - FUNC(true, hasFlag) - FUNC(true, snapshotGrab) - FUNC(true, snapshotClear) - FUNC(true, bodgeFilenames) - FUNC(false, _rem_registryGetString) - FUNC(true, quitWithFatalError) - FUNC(true, _rem_setCharacterAA) - FUNC(true, _rem_setMaximumAA) - FUNC(true, setBackgroundEffect) - FUNC(true, doBackgroundEffect) - FUNC(true, setCharacterAngleOffset) - FUNC(true, setCharacterTransparency) - FUNC(true, setCharacterColourise) - FUNC(true, zoomCamera) - FUNC(true, playMovie) - FUNC(true, stopMovie) - FUNC(true, pauseMovie) + FUNC(true, say, -1) + FUNC(true, skipSpeech, 0) + FUNC(true, statusText, 1) + FUNC(true, pause, 1) + FUNC(true, onLeftMouse, -1) + FUNC(true, onRightMouse, -1) + FUNC(true, setCursor, 1) + FUNC(true, addOverlay, 3) + FUNC(true, addCharacter, 4) + FUNC(true, playSound, 1) + FUNC(true, getMouseX, 0) + FUNC(true, getMouseY, 0) + FUNC(true, addScreenRegion, 8) + FUNC(true, onMoveMouse, -1) + FUNC(true, onFocusChange, -1) + FUNC(true, getOverObject, 0) + FUNC(true, blankScreen, 0) + FUNC(true, moveCharacter, -1) + FUNC(true, onKeyboard, -1) + FUNC(true, getObjectX, 1) + FUNC(true, getObjectY, 1) + FUNC(true, random, 1) + FUNC(true, spawnSub, 1) + FUNC(true, blankArea, 4) + FUNC(true, hideCharacter, 1) + FUNC(true, showCharacter, 1) + FUNC(true, callEvent, 2) + FUNC(true, removeScreenRegion, 1) + FUNC(true, animate, 2) + FUNC(true, turnCharacter, 2) + FUNC(true, removeAllCharacters, 0) + FUNC(true, removeAllScreenRegions, 0) + FUNC(true, setScale, 2) + FUNC(true, newStack, -1) + FUNC(true, pushToStack, 2) + FUNC(true, popFromStack, 1) + FUNC(true, clearStatus, 0) + FUNC(true, addStatus, 0) + FUNC(true, removeLastStatus, 0) + FUNC(true, lightStatus, 1) + FUNC(true, getStatusText, 0) + FUNC(true, setStatusColour, 3) + FUNC(true, deleteFromStack, 2) + FUNC(true, freeze, 0) + FUNC(true, unfreeze, 0) + FUNC(true, pasteImage, 3) + FUNC(true, copyStack, 1) + FUNC(true, completeTimers, 0) + FUNC(true, setCharacterDrawMode, 2) + FUNC(true, anim, -1) + FUNC(true, costume, -1) + FUNC(true, pickOne, -1) + FUNC(true, setCostume, 2) + FUNC(true, wait, 2) + FUNC(true, somethingSpeaking, 0) + FUNC(true, substring, 3) + FUNC(true, stringLength, 1) + FUNC(true, darkBackground, 0) + FUNC(true, saveGame, 1) + FUNC(true, loadGame, 1) + FUNC(true, quitGame, 0) + FUNC(true, rename, 2) + FUNC(true, stackSize, 1) + FUNC(true, pasteString, 3) + FUNC(true, startMusic, 3) + FUNC(true, setDefaultMusicVolume, 1) + FUNC(true, setMusicVolume, 2) + FUNC(true, stopMusic, 1) + FUNC(true, stopSound, 1) + FUNC(true, setFont, 3) + FUNC(true, alignStatus, 1) + FUNC(true, showFloor, 0) + FUNC(true, showBoxes, 0) + FUNC(true, positionStatus, 2) + FUNC(true, setFloor, 1) + FUNC(true, forceCharacter, -1) + FUNC(true, jumpCharacter, -1) + FUNC(true, peekStart, 1) + FUNC(true, peekEnd, 1) + FUNC(true, enqueue, 2) + FUNC(true, setZBuffer, 1) + FUNC(true, getMatchingFiles, 1) + FUNC(true, inFont, 1) + FUNC(true, onLeftMouseUp, -1) + FUNC(true, onRightMouseUp, -1) + FUNC(true, loopSound, -1) + FUNC(true, removeCharacter, 1) + FUNC(true, stopCharacter, 1) + FUNC(true, launch, 1) + FUNC(true, howFrozen, 0) + FUNC(true, setPasteColour, 3) + FUNC(true, setLitStatusColour, 3) + FUNC(true, fileExists, 1) + FUNC(true, floatCharacter, 2) + FUNC(true, cancelSub, 1) + FUNC(true, setCharacterWalkSpeed, 2) + FUNC(true, deleteAllFromStack, 2) + FUNC(true, setCharacterExtra, 2) + FUNC(true, mixOverlay, 3) + FUNC(true, pasteCharacter, 1) + FUNC(true, setSceneDimensions, 2) + FUNC(true, aimCamera, 2) + FUNC(true, getMouseScreenX, 0) + FUNC(true, getMouseScreenY, 0) + FUNC(true, setDefaultSoundVolume, 1) + FUNC(true, setSoundVolume, 2) + FUNC(true, setSoundLoopPoints, 3) + FUNC(true, setSpeechMode, 1) + FUNC(true, setLightMap, -1) + FUNC(true, think, -1) + FUNC(true, getCharacterDirection, 1) + FUNC(true, isCharacter, 1) + FUNC(true, isScreenRegion, 1) + FUNC(true, isMoving, 1) + FUNC(true, deleteFile, 1) + FUNC(true, renameFile, 2) + FUNC(true, hardScroll, 1) + FUNC(true, stringWidth, 1) + FUNC(true, setSpeechSpeed, 1) + FUNC(true, normalCharacter, 1) + FUNC(true, fetchEvent, 2) + FUNC(true, transitionLevel, 1) + FUNC(true, spinCharacter, 2) + FUNC(true, setFontSpacing, 1) + FUNC(true, burnString, 3) + FUNC(true, captureAllKeys, 1) + FUNC(true, cacheSound, 1) + FUNC(true, setCharacterSpinSpeed, 2) + FUNC(true, transitionMode, 1) + FUNC(false, _rem_movieStart, 1) + FUNC(false, _rem_movieAbort, 0) + FUNC(false, _rem_moviePlaying, 0) + FUNC(false, _rem_updateDisplay, 1) + FUNC(true, getSoundCache, 0) + FUNC(true, saveCustomData, 2) + FUNC(true, loadCustomData, 1) + FUNC(true, setCustomEncoding, 1) + FUNC(true, freeSound, 1) + FUNC(true, parallaxAdd, 3) + FUNC(true, parallaxClear, 0) + FUNC(true, setBlankColour, 3) + FUNC(true, setBurnColour, 3) + FUNC(true, getPixelColour, 2) + FUNC(true, makeFastArray, 1) + FUNC(true, getCharacterScale, 1) + FUNC(true, getLanguageID, 0) + FUNC(false, _rem_launchWith, 2) + FUNC(true, getFramesPerSecond, 0) + FUNC(true, showThumbnail, 3) + FUNC(true, setThumbnailSize, 2) + FUNC(true, hasFlag, 2) + FUNC(true, snapshotGrab, 0) + FUNC(true, snapshotClear, 0) + FUNC(true, bodgeFilenames, 1) + FUNC(false, _rem_registryGetString, 2) + FUNC(true, quitWithFatalError, 1) + FUNC(true, _rem_setCharacterAA, 4) + FUNC(true, _rem_setMaximumAA, 3) + FUNC(true, setBackgroundEffect, -1) + FUNC(true, doBackgroundEffect, 0) + FUNC(true, setCharacterAngleOffset, 2) + FUNC(true, setCharacterTransparency, 2) + FUNC(true, setCharacterColourise, 5) + FUNC(true, zoomCamera, 1) + FUNC(true, playMovie, 1) + FUNC(true, stopMovie, 0) + FUNC(true, pauseMovie, 0) }; #undef FUNC -int NUM_FUNCS = (sizeof (builtInFunctionArray) / sizeof (builtInFunctionArray[0])); +const static int NUM_FUNCS = (sizeof (builtInFunctionArray) / sizeof (builtInFunctionArray[0])); } // End of namespace Sludge diff --git a/engines/sludge/graphics.cpp b/engines/sludge/graphics.cpp index 578e6f65fe..72301b3475 100644 --- a/engines/sludge/graphics.cpp +++ b/engines/sludge/graphics.cpp @@ -83,6 +83,15 @@ void GraphicsManager::init() { _currentBurnR = 0; _currentBurnG = 0; _currentBurnB = 0; + + // Thumbnail + _thumbWidth = 0; + _thumbHeight = 0; + + // Transition + resetRandW(); + _brightnessLevel = 255; + _fadeMode = 2; } void GraphicsManager::kill() { @@ -157,6 +166,8 @@ bool GraphicsManager::initGfx() { void GraphicsManager::display() { g_system->copyRectToScreen((byte *)_renderSurface.getPixels(), _renderSurface.pitch, 0, 0, _renderSurface.w, _renderSurface.h); g_system->updateScreen(); + if (_brightnessLevel < 255) + fixBrightness(); } void GraphicsManager::clear() { diff --git a/engines/sludge/graphics.h b/engines/sludge/graphics.h index 16973a1658..8bc47cdab4 100644 --- a/engines/sludge/graphics.h +++ b/engines/sludge/graphics.h @@ -91,6 +91,8 @@ public: void drawVerticalLine(uint, uint, uint); void hardScroll(int distance); bool getRGBIntoStack(uint x, uint y, StackHandler *sH); + void saveBackdrop(Common::WriteStream *stream); // To game save + void loadBackdrop(int ssgVersion, Common::SeekableReadStream *streamn); // From game save // Lightmap int _lightMapMode; @@ -109,11 +111,6 @@ public: int getCamX() { return _cameraX; } int getCamY() { return _cameraY; } float getCamZoom() { return _cameraZoom; } - void setCamera(int camerX, int camerY, float camerZ) { - _cameraX = camerX; - _cameraY = camerY; - _cameraZoom = camerZ; - } void aimCamera(int cameraX, int cameraY); void zoomCamera(int z); @@ -167,11 +164,18 @@ public: void saveColors(Common::WriteStream *stream); void loadColors(Common::SeekableReadStream *stream); - // Thumb nail + // Thumbnail + bool setThumbnailSize(int thumbWidth, int thumbHeight); bool saveThumbnail(Common::WriteStream *stream); bool skipThumbnail(Common::SeekableReadStream *stream); void showThumbnail(const Common::String &filename, int x, int y); + // Transition + void setBrightnessLevel(int brightnessLevel); + void setFadeMode(int fadeMode) { _fadeMode = fadeMode; }; + void fixBrightness(); + void resetRandW(); + private: SludgeEngine *_vm; @@ -222,6 +226,14 @@ private: // Colors uint _currentBlankColour; byte _currentBurnR, _currentBurnG, _currentBurnB; + + // Thumbnail + int _thumbWidth; + int _thumbHeight; + + // Transition + byte _brightnessLevel; + byte _fadeMode; }; } // End of namespace Sludge diff --git a/engines/sludge/loadsave.cpp b/engines/sludge/loadsave.cpp index 4cabbc79da..453e78f3e5 100644 --- a/engines/sludge/loadsave.cpp +++ b/engines/sludge/loadsave.cpp @@ -29,6 +29,7 @@ #include "sludge/event.h" #include "sludge/floor.h" #include "sludge/fonttext.h" +#include "sludge/function.h" #include "sludge/graphics.h" #include "sludge/language.h" #include "sludge/loadsave.h" @@ -37,6 +38,7 @@ #include "sludge/objtypes.h" #include "sludge/people.h" #include "sludge/region.h" +#include "sludge/savedata.h" #include "sludge/sludge.h" #include "sludge/sludger.h" #include "sludge/sound.h" @@ -54,291 +56,31 @@ namespace Sludge { // From elsewhere //---------------------------------------------------------------------- +extern LoadedFunction *saverFunc; // In function.cpp extern LoadedFunction *allRunningFunctions; // In sludger.cpp -extern const char *typeName[]; // In variable.cpp extern int numGlobals; // In sludger.cpp extern Variable *globalVars; // In sludger.cpp -extern Floor *currentFloor; // In floor.cpp extern FILETIME fileTime; // In sludger.cpp -extern byte brightnessLevel; // " " " -extern byte fadeMode; // In transition.cpp -extern bool captureAllKeys; extern bool allowAnyFilename; -extern uint16 saveEncoding; // in savedata.cpp //---------------------------------------------------------------------- -// Globals (so we know what's saved already and what's a reference -//---------------------------------------------------------------------- - -struct stackLibrary { - StackHandler *stack; - stackLibrary *next; -}; - -int stackLibTotal = 0; -stackLibrary *stackLib = NULL; - -//---------------------------------------------------------------------- -// For saving and loading stacks... -//---------------------------------------------------------------------- -void saveStack(VariableStack *vs, Common::WriteStream *stream) { - int elements = 0; - int a; - - VariableStack *search = vs; - while (search) { - elements++; - search = search->next; - } - - stream->writeUint16BE(elements); - search = vs; - for (a = 0; a < elements; a++) { - saveVariable(&search->thisVar, stream); - search = search->next; - } -} - -VariableStack *loadStack(Common::SeekableReadStream *stream, VariableStack **last) { - int elements = stream->readUint16BE(); - int a; - VariableStack *first = NULL; - VariableStack **changeMe = &first; - - for (a = 0; a < elements; a++) { - VariableStack *nS = new VariableStack; - if (!checkNew(nS)) - return NULL; - loadVariable(&(nS->thisVar), stream); - if (last && a == elements - 1) { - *last = nS; - } - nS->next = NULL; - (*changeMe) = nS; - changeMe = &(nS->next); - } - - return first; -} - -bool saveStackRef(StackHandler *vs, Common::WriteStream *stream) { - stackLibrary *s = stackLib; - int a = 0; - while (s) { - if (s->stack == vs) { - stream->writeByte(1); - stream->writeUint16BE(stackLibTotal - a); - return true; - } - s = s->next; - a++; - } - stream->writeByte(0); - saveStack(vs->first, stream); - s = new stackLibrary; - stackLibTotal++; - if (!checkNew(s)) - return false; - s->next = stackLib; - s->stack = vs; - stackLib = s; - return true; -} - -void clearStackLib() { - stackLibrary *k; - while (stackLib) { - k = stackLib; - stackLib = stackLib->next; - delete k; - } - stackLibTotal = 0; -} - -StackHandler *getStackFromLibrary(int n) { - n = stackLibTotal - n; - while (n) { - stackLib = stackLib->next; - n--; - } - return stackLib->stack; -} - -StackHandler *loadStackRef(Common::SeekableReadStream *stream) { - StackHandler *nsh; - - if (stream->readByte()) { // It's one we've loaded already... - nsh = getStackFromLibrary(stream->readUint16BE()); - nsh->timesUsed++; - } else { - // Load the new stack - - nsh = new StackHandler; - if (!checkNew(nsh)) - return NULL; - nsh->last = NULL; - nsh->first = loadStack(stream, &nsh->last); - nsh->timesUsed = 1; - - // Add it to the library of loaded stacks - - stackLibrary *s = new stackLibrary; - if (!checkNew(s)) - return NULL; - s->stack = nsh; - s->next = stackLib; - stackLib = s; - stackLibTotal++; - } - return nsh; -} - -//---------------------------------------------------------------------- -// For saving and loading variables... +// Save everything //---------------------------------------------------------------------- -bool saveVariable(Variable *from, Common::WriteStream *stream) { - stream->writeByte(from->varType); - switch (from->varType) { - case SVT_INT: - case SVT_FUNC: - case SVT_BUILT: - case SVT_FILE: - case SVT_OBJTYPE: - stream->writeUint32LE(from->varData.intValue); - return true; - - case SVT_STRING: - writeString(from->varData.theString, stream); - return true; - - case SVT_STACK: - return saveStackRef(from->varData.theStack, stream); - - case SVT_COSTUME: - saveCostume(from->varData.costumeHandler, stream); - return false; - - case SVT_ANIM: - saveAnim(from->varData.animHandler, stream); - return false; - - case SVT_NULL: - return false; - - default: - fatal("Can't save variables of this type:", (from->varType < SVT_NUM_TYPES) ? typeName[from->varType] : "bad ID"); - } - return true; -} -bool loadVariable(Variable *to, Common::SeekableReadStream *stream) { - to->varType = (VariableType)stream->readByte(); - switch (to->varType) { - case SVT_INT: - case SVT_FUNC: - case SVT_BUILT: - case SVT_FILE: - case SVT_OBJTYPE: - to->varData.intValue = stream->readUint32LE(); - return true; - - case SVT_STRING: - to->varData.theString = createCString(readString(stream)); - return true; - - case SVT_STACK: - to->varData.theStack = loadStackRef(stream); - return true; - - case SVT_COSTUME: - to->varData.costumeHandler = new Persona; - if (!checkNew(to->varData.costumeHandler)) +bool handleSaveLoad() { + if (!g_sludge->loadNow.empty()) { + if (g_sludge->loadNow[0] == ':') { + saveGame(g_sludge->loadNow.c_str() + 1); + saverFunc->reg.setVariable(SVT_INT, 1); + } else { + if (!loadGame(g_sludge->loadNow)) return false; - loadCostume(to->varData.costumeHandler, stream); - return true; - - case SVT_ANIM: - to->varData.animHandler = new PersonaAnimation ; - if (!checkNew(to->varData.animHandler)) - return false; - loadAnim(to->varData.animHandler, stream); - return true; - - default: - break; + } + g_sludge->loadNow.clear(); } return true; } -//---------------------------------------------------------------------- -// For saving and loading functions -//---------------------------------------------------------------------- -void saveFunction(LoadedFunction *fun, Common::WriteStream *stream) { - int a; - stream->writeUint16BE(fun->originalNumber); - if (fun->calledBy) { - stream->writeByte(1); - saveFunction(fun->calledBy, stream); - } else { - stream->writeByte(0); - } - stream->writeUint32LE(fun->timeLeft); - stream->writeUint16BE(fun->runThisLine); - stream->writeByte(fun->cancelMe); - stream->writeByte(fun->returnSomething); - stream->writeByte(fun->isSpeech); - saveVariable(&(fun->reg), stream); - - if (fun->freezerLevel) { - fatal(ERROR_GAME_SAVE_FROZEN); - } - saveStack(fun->stack, stream); - for (a = 0; a < fun->numLocals; a++) { - saveVariable(&(fun->localVars[a]), stream); - } -} - -LoadedFunction *loadFunction(Common::SeekableReadStream *stream) { - int a; - - // Reserve memory... - - LoadedFunction *buildFunc = new LoadedFunction; - if (!checkNew(buildFunc)) - return NULL; - - // See what it was called by and load if we need to... - - buildFunc->originalNumber = stream->readUint16BE(); - buildFunc->calledBy = NULL; - if (stream->readByte()) { - buildFunc->calledBy = loadFunction(stream); - if (!buildFunc->calledBy) - return NULL; - } - - buildFunc->timeLeft = stream->readUint32LE(); - buildFunc->runThisLine = stream->readUint16BE(); - buildFunc->freezerLevel = 0; - buildFunc->cancelMe = stream->readByte(); - buildFunc->returnSomething = stream->readByte(); - buildFunc->isSpeech = stream->readByte(); - loadVariable(&(buildFunc->reg), stream); - loadFunctionCode(buildFunc); - - buildFunc->stack = loadStack(stream, NULL); - - for (a = 0; a < buildFunc->numLocals; a++) { - loadVariable(&(buildFunc->localVars[a]), stream); - } - - return buildFunc; -} - -//---------------------------------------------------------------------- -// Save everything -//---------------------------------------------------------------------- - bool saveGame(const Common::String &fname) { Common::OutSaveFile *fp = g_system->getSavefileManager()->openForSaving(fname); @@ -359,23 +101,18 @@ bool saveGame(const Common::String &fname) { // DON'T ADD ANYTHING NEW BEFORE THIS POINT! fp->writeByte(allowAnyFilename); - fp->writeByte(captureAllKeys); + fp->writeByte(false); // deprecated captureAllKeys fp->writeByte(true); g_sludge->_txtMan->saveFont(fp); // Save backdrop - fp->writeUint16BE(g_sludge->_gfxMan->getCamX()); - fp->writeUint16BE(g_sludge->_gfxMan->getCamY()); - fp->writeFloatLE(g_sludge->_gfxMan->getCamZoom()); - - fp->writeByte(brightnessLevel); - g_sludge->_gfxMan->saveHSI(fp); + g_sludge->_gfxMan->saveBackdrop(fp); // Save event handlers g_sludge->_evtMan->saveHandlers(fp); // Save regions - saveRegions(fp); + g_sludge->_regionMan->saveRegions(fp); g_sludge->_cursorMan->saveCursor(fp); @@ -395,28 +132,21 @@ bool saveGame(const Common::String &fname) { } for (int a = 0; a < numGlobals; a++) { - saveVariable(&globalVars[a], fp); + globalVars[a].save(fp); } - savePeople(fp); + g_sludge->_peopleMan->savePeople(fp); - if (currentFloor->numPolygons) { - fp->writeByte(1); - fp->writeUint16BE(currentFloor->originalNum); - } else { - fp->writeByte(0); - } + g_sludge->_floorMan->save(fp); g_sludge->_gfxMan->saveZBuffer(fp); g_sludge->_gfxMan->saveLightMap(fp); - fp->writeByte(fadeMode); - g_sludge->_speechMan->save(fp); saveStatusBars(fp); g_sludge->_soundMan->saveSounds(fp); - fp->writeUint16BE(saveEncoding); + fp->writeUint16BE(CustomSaveHelper::_saveEncoding); blur_saveSettings(fp); @@ -498,28 +228,18 @@ bool loadGame(const Common::String &fname) { if (ssgVersion >= VERSION(1, 4)) { allowAnyFilename = fp->readByte(); } - captureAllKeys = fp->readByte(); - fp->readByte(); // updateDisplay (part of movie playing) + fp->readByte(); // deprecated captureAllKeys + fp->readByte(); // updateDisplay (part of movie playing) g_sludge->_txtMan->loadFont(ssgVersion, fp); - killAllPeople(); - killAllRegions(); - - int camerX = fp->readUint16BE(); - int camerY = fp->readUint16BE(); - float camerZ; - if (ssgVersion >= VERSION(2, 0)) { - camerZ = fp->readFloatLE(); - } else { - camerZ = 1.0; - } + g_sludge->_regionMan->kill(); - brightnessLevel = fp->readByte(); + g_sludge->_gfxMan->loadBackdrop(ssgVersion, fp); - g_sludge->_gfxMan->loadHSI(fp, 0, 0, true); g_sludge->_evtMan->loadHandlers(fp); - loadRegions(fp); + + g_sludge->_regionMan->loadRegions(fp); if (!g_sludge->_cursorMan->loadCursor(fp)) { return false; @@ -537,17 +257,15 @@ bool loadGame(const Common::String &fname) { } for (int a = 0; a < numGlobals; a++) { - unlinkVar(globalVars[a]); - loadVariable(&globalVars[a], fp); + globalVars[a].unlinkVar(); + globalVars[a].load(fp); } - loadPeople(fp); + g_sludge->_peopleMan->loadPeople(fp); - if (fp->readByte()) { - if (!setFloor(fp->readUint16BE())) - return false; - } else - setFloorNull(); + if (!g_sludge->_floorMan->load(fp)) { + return false; + } if (!g_sludge->_gfxMan->loadZBuffer(fp)) return false; @@ -556,12 +274,11 @@ bool loadGame(const Common::String &fname) { return false; } - fadeMode = fp->readByte(); g_sludge->_speechMan->load(fp); loadStatusBars(fp); g_sludge->_soundMan->loadSounds(fp); - saveEncoding = fp->readUint16BE(); + CustomSaveHelper::_saveEncoding = fp->readUint16BE(); if (ssgVersion >= VERSION(1, 6)) { if (ssgVersion < VERSION(2, 0)) { @@ -600,8 +317,6 @@ bool loadGame(const Common::String &fname) { delete fp; - g_sludge->_gfxMan->setCamera(camerX, camerY, camerZ); - clearStackLib(); return true; } diff --git a/engines/sludge/loadsave.h b/engines/sludge/loadsave.h index 269fadb507..4077950cfb 100644 --- a/engines/sludge/loadsave.h +++ b/engines/sludge/loadsave.h @@ -24,23 +24,10 @@ namespace Sludge { -struct LoadedFunction; -struct Variable; -struct VariableStack; - +bool handleSaveLoad(); bool saveGame(const Common::String &fname); bool loadGame(const Common::String &fname); -bool saveVariable(Variable *from, Common::WriteStream *stream); -bool loadVariable(Variable *to, Common::SeekableReadStream *stream); - -VariableStack *loadStack(Common::SeekableReadStream *stream, VariableStack **last); -bool saveStackRef(StackHandler *vs, Common::WriteStream *stream); -StackHandler *loadStackRef(Common::SeekableReadStream *stream); - -LoadedFunction *loadFunction(Common::SeekableReadStream *stream); -void saveFunction(LoadedFunction *fun, Common::WriteStream *stream); - } // End of namespace Sludge #endif diff --git a/engines/sludge/main_loop.cpp b/engines/sludge/main_loop.cpp index 905d91d9c2..8f6e1f9cfb 100644 --- a/engines/sludge/main_loop.cpp +++ b/engines/sludge/main_loop.cpp @@ -28,8 +28,10 @@ #include "sludge/backdrop.h" #include "sludge/event.h" #include "sludge/floor.h" +#include "sludge/function.h" #include "sludge/graphics.h" #include "sludge/language.h" +#include "sludge/loadsave.h" #include "sludge/newfatal.h" #include "sludge/objtypes.h" #include "sludge/people.h" @@ -39,15 +41,12 @@ #include "sludge/sludge.h" #include "sludge/sludger.h" #include "sludge/speech.h" -#include "sludge/transition.h" #include "sludge/timing.h" namespace Sludge { extern VariableStack *noStack; -int dialogValue = 0; - int main_loop(Common::String filename) { if (!initSludge(filename)) { @@ -63,9 +62,10 @@ int main_loop(Common::String filename) { while (!g_sludge->_evtMan->quit()) { g_sludge->_evtMan->checkInput(); - walkAllPeople(); + g_sludge->_peopleMan->walkAllPeople(); if (g_sludge->_evtMan->handleInput()) { - runSludge(); + runAllFunctions(); + handleSaveLoad(); } sludgeDisplay(); g_sludge->_soundMan->handleSoundLists(); diff --git a/engines/sludge/module.mk b/engines/sludge/module.mk index a083ec4a95..d904e6c4c3 100644 --- a/engines/sludge/module.mk +++ b/engines/sludge/module.mk @@ -12,6 +12,7 @@ MODULE_OBJS := \ floor.o \ freeze.o \ fonttext.o \ + function.o \ graphics.o \ hsi.o \ imgloader.o \ diff --git a/engines/sludge/moreio.cpp b/engines/sludge/moreio.cpp index 1512574207..ee9ab8e0f0 100644 --- a/engines/sludge/moreio.cpp +++ b/engines/sludge/moreio.cpp @@ -167,4 +167,15 @@ Common::String decodeFilename(const Common::String &nameIn) { return newName; } +char *createCString(const Common::String &s) { + uint n = s.size() + 1; + char *res = new char[n]; + if (!checkNew(res)) { + fatal("createCString : Unable to copy String"); + return NULL; + } + memcpy(res, s.c_str(), n); + return res; +} + } // End of namespace Sludge diff --git a/engines/sludge/moreio.h b/engines/sludge/moreio.h index 09235ae8a6..237a918626 100644 --- a/engines/sludge/moreio.h +++ b/engines/sludge/moreio.h @@ -31,6 +31,8 @@ void writeString(Common::String s, Common::WriteStream *stream); Common::String encodeFilename(const Common::String &nameIn); Common::String decodeFilename(const Common::String &nameIn); +char *createCString(const Common::String &s); + } // End of namespace Sludge #endif diff --git a/engines/sludge/movie.cpp b/engines/sludge/movie.cpp index 271728da75..2c43c3cceb 100644 --- a/engines/sludge/movie.cpp +++ b/engines/sludge/movie.cpp @@ -911,7 +911,8 @@ int playMovie(int fileNumber) { glBindFramebuffer(GL_FRAMEBUFFER, old_fbo); movieIsPlaying = nothing; - for (int i = 0; i < 10; i++) Wait_Frame(); + for (int i = 0; i < 10; i++) + Wait_Frame(); huntKillFreeSound(fileNumber); if (vpx_codec_destroy(&codec)) diff --git a/engines/sludge/newfatal.cpp b/engines/sludge/newfatal.cpp index edd4a88073..a5069ae306 100644 --- a/engines/sludge/newfatal.cpp +++ b/engines/sludge/newfatal.cpp @@ -24,34 +24,17 @@ #include "sludge/allfiles.h" #include "sludge/errors.h" +#include "sludge/fileset.h" +#include "sludge/newfatal.h" #include "sludge/sludge.h" #include "sludge/sound.h" #include "sludge/version.h" -namespace Sludge { - -const char emergencyMemoryMessage[] = "Out of memory displaying error message!"; - -extern int numResourceNames /* = 0*/; -extern Common::String *allResourceNames /*= ""*/; - -int resourceForFatal = -1; - -const Common::String resourceNameFromNum(int i) { - if (i == -1) - return NULL; - if (numResourceNames == 0) - return "RESOURCE"; - if (i < numResourceNames) - return allResourceNames[i]; - return "Unknown resource"; +namespace Common { +DECLARE_SINGLETON(Sludge::FatalMsgManager); } -bool hasFatal() { - if (!g_sludge->fatalMessage.empty()) - return true; - return false; -} +namespace Sludge { int inFatal(const Common::String &str) { g_sludge->_soundMan->killSoundStuff(); @@ -59,35 +42,55 @@ int inFatal(const Common::String &str) { return true; } -int checkNew(const void *mem) { - if (mem == NULL) { - inFatal(ERROR_OUT_OF_MEMORY); - return 0; - } - return 1; +FatalMsgManager::FatalMsgManager() { + reset(); } -void setFatalInfo(const Common::String &userFunc, const Common::String &BIF) { - g_sludge->fatalInfo = "Currently in this sub: " + userFunc + "\nCalling: " + BIF; - debugC(0, kSludgeDebugFatal, "%s", g_sludge->fatalInfo.c_str()); +FatalMsgManager::~FatalMsgManager() { } -void setResourceForFatal(int n) { - resourceForFatal = n; +void FatalMsgManager::reset() { + _fatalMessage = ""; + _fatalInfo = "Initialisation error! Something went wrong before we even got started!"; + _resourceForFatal = -1; } -int fatal(const Common::String &str1) { - if (numResourceNames && resourceForFatal != -1) { - Common::String r = resourceNameFromNum(resourceForFatal); - Common::String newStr = g_sludge->fatalInfo + "\nResource: " + r + "\n\n" + str1; +bool FatalMsgManager::hasFatal() { + if (!_fatalMessage.empty()) + return true; + return false; +} + +void FatalMsgManager::setFatalInfo(const Common::String &userFunc, const Common::String &BIF) { + _fatalInfo = "Currently in this sub: " + userFunc + "\nCalling: " + BIF; + debugC(0, kSludgeDebugFatal, "%s", _fatalInfo.c_str()); +} + +void FatalMsgManager::setResourceForFatal(int n) { + _resourceForFatal = n; +} + +int FatalMsgManager::fatal(const Common::String &str1) { + ResourceManager *resMan = g_sludge->_resMan; + if (resMan->hasResourceNames() && _resourceForFatal != -1) { + Common::String r = resMan->resourceNameFromNum(_resourceForFatal); + Common::String newStr = _fatalInfo + "\nResource: " + r + "\n\n" + str1; inFatal(newStr); } else { - Common::String newStr = g_sludge->fatalInfo + "\n\n" + str1; + Common::String newStr = _fatalInfo + "\n\n" + str1; inFatal(newStr); } return 0; } +int checkNew(const void *mem) { + if (mem == NULL) { + inFatal(ERROR_OUT_OF_MEMORY); + return 0; + } + return 1; +} + int fatal(const Common::String &str1, const Common::String &str2) { Common::String newStr = str1 + " " + str2; fatal(newStr); diff --git a/engines/sludge/newfatal.h b/engines/sludge/newfatal.h index fc91110758..81ca4b7616 100644 --- a/engines/sludge/newfatal.h +++ b/engines/sludge/newfatal.h @@ -23,19 +23,49 @@ #define SLUDGE_NEWFATAL_H #include "common/str.h" +#include "common/singleton.h" #include "sludge/errors.h" namespace Sludge { -bool hasFatal(); +class FatalMsgManager : public Common::Singleton<Sludge::FatalMsgManager>{ +public: + FatalMsgManager(); + ~FatalMsgManager(); + + void reset(); + + bool hasFatal(); + int fatal(const Common::String &str); + void setFatalInfo(const Common::String &userFunc, const Common::String &BIF); + void setResourceForFatal(int n); + +private: + Common::String _fatalMessage; + Common::String _fatalInfo; + + int _resourceForFatal; +}; + +inline bool hasFatal() { + return FatalMsgManager::instance().hasFatal(); +} + +inline int fatal(const Common::String &str) { + return FatalMsgManager::instance().fatal(str); +} + +inline void setFatalInfo(const Common::String &userFunc, const Common::String &BIF) { + FatalMsgManager::instance().setFatalInfo(userFunc, BIF); +} + +inline void setResourceForFatal(int n) { + FatalMsgManager::instance().setResourceForFatal(n); +} -int fatal(const Common::String &str); -int fatal(const Common::String &str1, const Common::String &str2); int checkNew(const void *mem); -void setFatalInfo(const Common::String &userFunc, const Common::String &BIF); -void setResourceForFatal(int n); -const Common::String resourceNameFromNum(int i); +int fatal(const Common::String &str1, const Common::String &str2); } // End of namespace Sludge diff --git a/engines/sludge/people.cpp b/engines/sludge/people.cpp index 4aec5fa8b7..433ab2d895 100644 --- a/engines/sludge/people.cpp +++ b/engines/sludge/people.cpp @@ -22,6 +22,7 @@ #include "sludge/allfiles.h" #include "sludge/floor.h" +#include "sludge/function.h" #include "sludge/graphics.h" #include "sludge/loadsave.h" #include "sludge/moreio.h" @@ -39,7 +40,6 @@ #include "sludge/version.h" #include "sludge/zbuffer.h" -#define ANGLEFIX (180.0 / 3.14157) #define ANI_STAND 0 #define ANI_WALK 1 #define ANI_TALK 2 @@ -47,108 +47,174 @@ namespace Sludge { extern VariableStack *noStack; - extern int ssgVersion; -ScreenRegion personRegion; -extern ScreenRegion *lastRegion; -extern Floor *currentFloor; - -OnScreenPerson *allPeople = NULL; -int16 scaleHorizon = 75; -int16 scaleDivide = 150; -extern ScreenRegion *allScreenRegions; - -void setFrames(OnScreenPerson &m, int a) { - m.myAnim = m.myPersona->animation[(a * m.myPersona->numDirections) + m.direction]; +PersonaAnimation::PersonaAnimation() { + theSprites = nullptr; + numFrames = 0; + frames = nullptr; } -PersonaAnimation *createPersonaAnim(int num, VariableStack *&stacky) { - PersonaAnimation *newP = new PersonaAnimation ; - checkNew(newP); - - newP->numFrames = num; - newP->frames = new AnimFrame [num]; - checkNew(newP->frames); +PersonaAnimation::~PersonaAnimation() { + if (numFrames) { + delete[] frames; + frames = nullptr; + } +} +PersonaAnimation::PersonaAnimation(int num, VariableStack *&stacky) { + theSprites = nullptr; + numFrames = num; + frames = new AnimFrame[num]; int a = num, frameNum, howMany; while (a) { a--; - newP->frames[a].noise = 0; + frames[a].noise = 0; if (stacky->thisVar.varType == SVT_FILE) { - newP->frames[a].noise = stacky->thisVar.varData.intValue; + frames[a].noise = stacky->thisVar.varData.intValue; } else if (stacky->thisVar.varType == SVT_FUNC) { - newP->frames[a].noise = -stacky->thisVar.varData.intValue; + frames[a].noise = -stacky->thisVar.varData.intValue; } else if (stacky->thisVar.varType == SVT_STACK) { - getValueType(frameNum, SVT_INT, stacky->thisVar.varData.theStack->first->thisVar); - getValueType(howMany, SVT_INT, stacky->thisVar.varData.theStack->first->next->thisVar); + stacky->thisVar.varData.theStack->first->thisVar.getValueType(frameNum, SVT_INT); + stacky->thisVar.varData.theStack->first->next->thisVar.getValueType(howMany, SVT_INT); } else { - getValueType(frameNum, SVT_INT, stacky->thisVar); + stacky->thisVar.getValueType(frameNum, SVT_INT); howMany = 1; } trimStack(stacky); - newP->frames[a].frameNum = frameNum; - newP->frames[a].howMany = howMany; + frames[a].frameNum = frameNum; + frames[a].howMany = howMany; } - - return newP; } -PersonaAnimation *makeNullAnim() { - PersonaAnimation *newAnim = new PersonaAnimation ; - if (!checkNew(newAnim)) - return NULL; - - newAnim->theSprites = NULL; - newAnim->numFrames = 0; - newAnim->frames = NULL; - return newAnim; -} - -PersonaAnimation *copyAnim(PersonaAnimation *orig) { +PersonaAnimation::PersonaAnimation(PersonaAnimation *orig) { int num = orig->numFrames; - PersonaAnimation *newAnim = new PersonaAnimation ; - if (!checkNew(newAnim)) - return NULL; - // Copy the easy bits... - newAnim->theSprites = orig->theSprites; - newAnim->numFrames = num; + theSprites = orig->theSprites; + numFrames = num; if (num) { - - // Argh!Frames!We need a whole NEW array of AnimFrame structures... - - newAnim->frames = new AnimFrame [num]; - if (!checkNew(newAnim->frames)) - return NULL; + // Argh! Frames! We need a whole NEW array of AnimFrame structures... + frames = new AnimFrame[num]; for (int a = 0; a < num; a++) { - newAnim->frames[a].frameNum = orig->frames[a].frameNum; - newAnim->frames[a].howMany = orig->frames[a].howMany; - newAnim->frames[a].noise = orig->frames[a].noise; + frames[a].frameNum = orig->frames[a].frameNum; + frames[a].howMany = orig->frames[a].howMany; + frames[a].noise = orig->frames[a].noise; } } else { - newAnim->frames = NULL; + frames = nullptr; } +} - return newAnim; +int PersonaAnimation::getTotalTime() { + int total = 0; + for (int a = 0; a < numFrames; a++) { + total += frames[a].howMany; + } + return total; } -void deleteAnim(PersonaAnimation *orig) { +bool PersonaAnimation::save(Common::WriteStream *stream) { + stream->writeUint16BE(numFrames); + if (numFrames) { + stream->writeUint32LE(theSprites->ID); - if (orig) { - if (orig->numFrames) { - delete[] orig->frames; + for (int a = 0; a < numFrames; a++) { + stream->writeUint32LE(frames[a].frameNum); + stream->writeUint32LE(frames[a].howMany); + stream->writeUint32LE(frames[a].noise); } - delete orig; - orig = NULL; } + return true; +} + +bool PersonaAnimation::load(Common::SeekableReadStream *stream) { + numFrames = stream->readUint16BE(); + + if (numFrames) { + int a = stream->readUint32LE(); + frames = new AnimFrame [numFrames]; + if (!checkNew(frames)) + return false; + theSprites = g_sludge->_gfxMan->loadBankForAnim(a); + + for (a = 0; a < numFrames; a++) { + frames[a].frameNum = stream->readUint32LE(); + frames[a].howMany = stream->readUint32LE(); + if (ssgVersion >= VERSION(2, 0)) { + frames[a].noise = stream->readUint32LE(); + } else { + frames[a].noise = 0; + } + } + } else { + theSprites = NULL; + frames = NULL; + } + return true; +} + +bool Persona::save(Common::WriteStream *stream) { + int a; + stream->writeUint16BE(numDirections); + for (a = 0; a < numDirections * 3; a++) { + if (!animation[a]->save(stream)) + return false; + } + return true; +} + +bool Persona::load(Common::SeekableReadStream *stream) { + int a; + numDirections = stream->readUint16BE(); + animation = new PersonaAnimation *[numDirections * 3]; + if (!checkNew(animation)) + return false; + for (a = 0; a < numDirections * 3; a++) { + animation[a] = new PersonaAnimation; + if (!checkNew(animation[a])) + return false; + + if (!animation[a]->load(stream)) + return false; + } + return true; +} + +void OnScreenPerson::setFrames(int a) { + myAnim = myPersona->animation[(a * myPersona->numDirections) + direction]; +} + +void OnScreenPerson::makeTalker() { + setFrames(ANI_TALK); +} + +void OnScreenPerson::makeSilent() { + setFrames(ANI_STAND); } -void turnMeAngle(OnScreenPerson *thisPerson, int direc) { +PeopleManager::PeopleManager(SludgeEngine *vm) { + _vm = vm; + _allPeople = new OnScreenPersonList; + _scaleHorizon = 75; + _scaleDivide = 150; + _personRegion = new ScreenRegion; +} + +PeopleManager::~PeopleManager() { + kill(); + + delete _personRegion; + _personRegion = nullptr; + + delete _allPeople; + _allPeople = nullptr; +} + +void PeopleManager::turnMeAngle(OnScreenPerson *thisPerson, int direc) { int d = thisPerson->myPersona->numDirections; thisPerson->angle = direc; direc += (180 / d) + 180 + thisPerson->angleOffset; @@ -157,16 +223,14 @@ void turnMeAngle(OnScreenPerson *thisPerson, int direc) { thisPerson->direction = (direc * d) / 360; } -bool initPeople() { - personRegion.sX = 0; - personRegion.sY = 0; - personRegion.di = -1; - allScreenRegions = NULL; - +bool PeopleManager::init() { + _personRegion->sX = 0; + _personRegion->sY = 0; + _personRegion->di = -1; return true; } -void spinStep(OnScreenPerson *thisPerson) { +void PeopleManager::spinStep(OnScreenPerson *thisPerson) { int diff = (thisPerson->angle + 360) - thisPerson->wantAngle; int eachSlice = thisPerson->spinSpeed ? thisPerson->spinSpeed : (360 / thisPerson->myPersona->numDirections); while (diff > 180) { @@ -183,7 +247,7 @@ void spinStep(OnScreenPerson *thisPerson) { } } -void rethinkAngle(OnScreenPerson *thisPerson) { +void PeopleManager::rethinkAngle(OnScreenPerson *thisPerson) { int d = thisPerson->myPersona->numDirections; int direc = thisPerson->angle + (180 / d) + 180 + thisPerson->angleOffset; while (direc >= 360) @@ -191,7 +255,7 @@ void rethinkAngle(OnScreenPerson *thisPerson) { thisPerson->direction = (direc * d) / 360; } -bool turnPersonToFace(int thisNum, int direc) { +bool PeopleManager::turnPersonToFace(int thisNum, int direc) { OnScreenPerson *thisPerson = findPerson(thisNum); if (thisPerson) { if (thisPerson->continueAfterWalking) @@ -200,13 +264,14 @@ bool turnPersonToFace(int thisNum, int direc) { thisPerson->walking = false; thisPerson->spinning = false; turnMeAngle(thisPerson, direc); - setFrames(*thisPerson, g_sludge->_speechMan->isCurrentTalker(thisPerson) ? ANI_TALK : ANI_STAND); + _vm->_speechMan->isCurrentTalker(thisPerson) ? + thisPerson->makeTalker() : thisPerson->makeSilent(); return true; } return false; } -bool setPersonExtra(int thisNum, int extra) { +bool PeopleManager::setPersonExtra(int thisNum, int extra) { OnScreenPerson *thisPerson = findPerson(thisNum); if (thisPerson) { thisPerson->extra = extra; @@ -217,35 +282,34 @@ bool setPersonExtra(int thisNum, int extra) { return false; } -void setScale(int16 h, int16 d) { - scaleHorizon = h; - scaleDivide = d; +void PeopleManager::setScale(int16 h, int16 d) { + _scaleHorizon = h; + _scaleDivide = d; } -void moveAndScale(OnScreenPerson &me, float x, float y) { +void PeopleManager::moveAndScale(OnScreenPerson &me, float x, float y) { me.x = x; me.y = y; - if (!(me.extra & EXTRA_NOSCALE) && scaleDivide) - me.scale = (me.y - scaleHorizon) / scaleDivide; + if (!(me.extra & EXTRA_NOSCALE) && _scaleDivide) + me.scale = (me.y - _scaleHorizon) / _scaleDivide; } -OnScreenPerson *findPerson(int v) { - OnScreenPerson *thisPerson = allPeople; - while (thisPerson) { - if (v == thisPerson->thisType->objectNum) - break; - thisPerson = thisPerson->next; +OnScreenPerson *PeopleManager::findPerson(int v) { + for (OnScreenPersonList::iterator it = _allPeople->begin(); it != _allPeople->end(); ++it) { + if (v == (*it)->thisType->objectNum) { + return (*it); + } } - return thisPerson; + return nullptr; } -void movePerson(int x, int y, int objNum) { +void PeopleManager::movePerson(int x, int y, int objNum) { OnScreenPerson *moveMe = findPerson(objNum); if (moveMe) moveAndScale(*moveMe, x, y); } -void setShown(bool h, int ob) { +void PeopleManager::setShown(bool h, int ob) { OnScreenPerson *moveMe = findPerson(ob); if (moveMe) moveMe->show = h; @@ -275,7 +339,7 @@ enum drawModes { numDrawModes }; -void setMyDrawMode(OnScreenPerson *moveMe, int h) { +void PeopleManager::setMyDrawMode(OnScreenPerson *moveMe, int h) { switch (h) { case drawModeTransparent3: moveMe->r = moveMe->g = moveMe->b = 0; @@ -381,7 +445,7 @@ void setMyDrawMode(OnScreenPerson *moveMe, int h) { } -void setDrawMode(int h, int ob) { +void PeopleManager::setDrawMode(int h, int ob) { OnScreenPerson *moveMe = findPerson(ob); if (!moveMe) return; @@ -389,7 +453,7 @@ void setDrawMode(int h, int ob) { setMyDrawMode(moveMe, h); } -void setPersonTransparency(int ob, byte x) { +void PeopleManager::setPersonTransparency(int ob, byte x) { OnScreenPerson *moveMe = findPerson(ob); if (!moveMe) return; @@ -399,7 +463,7 @@ void setPersonTransparency(int ob, byte x) { moveMe->transparency = x; } -void setPersonColourise(int ob, byte r, byte g, byte b, byte colourmix) { +void PeopleManager::setPersonColourise(int ob, byte r, byte g, byte b, byte colourmix) { OnScreenPerson *moveMe = findPerson(ob); if (!moveMe) return; @@ -410,44 +474,29 @@ void setPersonColourise(int ob, byte r, byte g, byte b, byte colourmix) { moveMe->colourmix = colourmix; } -extern ScreenRegion *overRegion; - -void shufflePeople() { - OnScreenPerson **thisReference = &allPeople; - OnScreenPerson *A, *B; +struct PeopleYComperator { + bool operator()(const OnScreenPerson *p1, const OnScreenPerson *p2) { + float y1 = p1->extra & EXTRA_FRONT ? p1->y + 1000 : p1->y; + float y2 = p2->extra & EXTRA_FRONT ? p2->y + 1000 : p2->y; + return y1 < y2; + } +}; - if (!allPeople) +void PeopleManager::shufflePeople() { + if (_allPeople->empty()) return; - while ((*thisReference)->next) { - float y1 = (*thisReference)->y; - if ((*thisReference)->extra & EXTRA_FRONT) - y1 += 1000; - - float y2 = (*thisReference)->next->y; - if ((*thisReference)->next->extra & EXTRA_FRONT) - y2 += 1000; - - if (y1 > y2) { - A = (*thisReference); - B = (*thisReference)->next; - A->next = B->next; - B->next = A; - (*thisReference) = B; - } else { - thisReference = &((*thisReference)->next); - } - } + Common::sort(_allPeople->begin(), _allPeople->end(), PeopleYComperator()); } -void drawPeople() { +void PeopleManager::drawPeople() { shufflePeople(); - OnScreenPerson *thisPerson = allPeople; - PersonaAnimation *myAnim = NULL; - overRegion = NULL; + PersonaAnimation *myAnim = NULL; + _vm->_regionMan->resetOverRegion(); - while (thisPerson) { + for (OnScreenPersonList::iterator it = _allPeople->begin(); it != _allPeople->end(); ++it) { + OnScreenPerson * thisPerson = (*it); if (thisPerson->show) { myAnim = thisPerson->myAnim; if (myAnim != thisPerson->lastUsedAnim) { @@ -455,7 +504,7 @@ void drawPeople() { thisPerson->frameNum = 0; thisPerson->frameTick = myAnim->frames[0].howMany; if (myAnim->frames[thisPerson->frameNum].noise > 0) { - g_sludge->_soundMan->startSound(myAnim->frames[thisPerson->frameNum].noise, false); + _vm->_soundMan->startSound(myAnim->frames[thisPerson->frameNum].noise, false); thisPerson->frameNum++; thisPerson->frameNum %= thisPerson->myAnim->numFrames; thisPerson->frameTick = thisPerson->myAnim->frames[thisPerson->frameNum].howMany; @@ -476,13 +525,13 @@ void drawPeople() { } if (m != 2) { bool r = false; - r = g_sludge->_gfxMan->scaleSprite(myAnim->theSprites->bank.sprites[fNum], myAnim->theSprites->bank.myPalette, thisPerson, m); + r = _vm->_gfxMan->scaleSprite(myAnim->theSprites->bank.sprites[fNum], myAnim->theSprites->bank.myPalette, thisPerson, m); if (r) { if (!thisPerson->thisType->screenName.empty()) { - if (personRegion.thisType != thisPerson->thisType) - lastRegion = NULL; - personRegion.thisType = thisPerson->thisType; - overRegion = &personRegion; + if (_personRegion->thisType != thisPerson->thisType) + _vm->_regionMan->resetLastRegion(); + _personRegion->thisType = thisPerson->thisType; + _vm->_regionMan->setOverRegion(_personRegion); } } } @@ -493,7 +542,7 @@ void drawPeople() { thisPerson->frameTick = thisPerson->myAnim->frames[thisPerson->frameNum].howMany; if (thisPerson->show && myAnim && myAnim->frames) { if (myAnim->frames[thisPerson->frameNum].noise > 0) { - g_sludge->_soundMan->startSound(myAnim->frames[thisPerson->frameNum].noise, false); + _vm->_soundMan->startSound(myAnim->frames[thisPerson->frameNum].noise, false); thisPerson->frameNum++; thisPerson->frameNum %= thisPerson->myAnim->numFrames; thisPerson->frameTick = thisPerson->myAnim->frames[thisPerson->frameNum].howMany; @@ -506,132 +555,10 @@ void drawPeople() { } } } - thisPerson = thisPerson->next; - } -} - -void makeTalker(OnScreenPerson &me) { - setFrames(me, ANI_TALK); -} - -void makeSilent(OnScreenPerson &me) { - setFrames(me, ANI_STAND); -} - -bool handleClosestPoint(int &setX, int &setY, int &setPoly) { - int gotX = 320, gotY = 200, gotPoly = -1, i, j, xTest1, yTest1, xTest2, yTest2, closestX, closestY, oldJ, currentDistance = 0xFFFFF, thisDistance; - -// FILE * dbug = fopen ("debug_closest.txt", "at"); -// fprintf (dbug, "\nGetting closest point to %i, %i\n", setX, setY); - - for (i = 0; i < currentFloor->numPolygons; i++) { - oldJ = currentFloor->polygon[i].numVertices - 1; - for (j = 0; j < currentFloor->polygon[i].numVertices; j++) { -// fprintf (dbug, "Polygon %i, line %i... ", i, j); - xTest1 = currentFloor->vertex[currentFloor->polygon[i].vertexID[j]].x; - yTest1 = currentFloor->vertex[currentFloor->polygon[i].vertexID[j]].y; - xTest2 = currentFloor->vertex[currentFloor->polygon[i].vertexID[oldJ]].x; - yTest2 = currentFloor->vertex[currentFloor->polygon[i].vertexID[oldJ]].y; - closestPointOnLine(closestX, closestY, xTest1, yTest1, xTest2, yTest2, setX, setY); -// fprintf (dbug, "closest point is %i, %i... ", closestX, closestY); - xTest1 = setX - closestX; - yTest1 = setY - closestY; - thisDistance = xTest1 * xTest1 + yTest1 * yTest1; -// fprintf (dbug, "Distance squared %i\n", thisDistance); - - if (thisDistance < currentDistance) { -// fprintf (dbug, "** We have a new winner!**\n"); - - currentDistance = thisDistance; - gotX = closestX; - gotY = closestY; - gotPoly = i; - } - oldJ = j; - } } -// fclose (dbug); - - if (gotPoly == -1) - return false; - setX = gotX; - setY = gotY; - setPoly = gotPoly; - - return true; } -bool doBorderStuff(OnScreenPerson *moveMe) { - if (moveMe->inPoly == moveMe->walkToPoly) { - moveMe->inPoly = -1; - moveMe->thisStepX = moveMe->walkToX; - moveMe->thisStepY = moveMe->walkToY; - } else { - // The section in which we need to be next... - int newPoly = currentFloor->matrix[moveMe->inPoly][moveMe->walkToPoly]; - if (newPoly == -1) - return false; - - // Grab the index of the second matching corner... - int ID, ID2; - if (!getMatchingCorners(currentFloor->polygon[moveMe->inPoly], currentFloor->polygon[newPoly], ID, ID2)) - return fatal("Not a valid floor plan!"); - - // Remember that we're walking to the new polygon... - moveMe->inPoly = newPoly; - - // Calculate the destination position on the coincidantal line... - int x1 = moveMe->x, y1 = moveMe->y; - int x2 = moveMe->walkToX, y2 = moveMe->walkToY; - int x3 = currentFloor->vertex[ID].x, y3 = currentFloor->vertex[ID].y; - int x4 = currentFloor->vertex[ID2].x, y4 = currentFloor->vertex[ID2].y; - - int xAB = x1 - x2; - int yAB = y1 - y2; - int xCD = x4 - x3; - int yCD = y4 - y3; - - double m = (yAB * (x3 - x1) - xAB * (y3 - y1)); - m /= ((xAB * yCD) - (yAB * xCD)); - - if (m > 0 && m < 1) { - moveMe->thisStepX = x3 + m * xCD; - moveMe->thisStepY = y3 + m * yCD; - } else { - int dx13 = x1 - x3, dx14 = x1 - x4, dx23 = x2 - x3, dx24 = x2 - x4; - int dy13 = y1 - y3, dy14 = y1 - y4, dy23 = y2 - y3, dy24 = y2 - y4; - - dx13 *= dx13; - dx14 *= dx14; - dx23 *= dx23; - dx24 *= dx24; - dy13 *= dy13; - dy14 *= dy14; - dy23 *= dy23; - dy24 *= dy24; - - if (sqrt((double)dx13 + dy13) + sqrt((double)dx23 + dy23) < sqrt((double)dx14 + dy14) + sqrt((double)dx24 + dy24)) { - moveMe->thisStepX = x3; - moveMe->thisStepY = y3; - } else { - moveMe->thisStepX = x4; - moveMe->thisStepY = y4; - } - } - } - - float yDiff = moveMe->thisStepY - moveMe->y; - float xDiff = moveMe->x - moveMe->thisStepX; - if (xDiff || yDiff) { - moveMe->wantAngle = 180 + ANGLEFIX * atan2(xDiff, yDiff * 2); - moveMe->spinning = true; - } - - setFrames(*moveMe, ANI_WALK); - return true; -} - -bool walkMe(OnScreenPerson *thisPerson, bool move = true) { +bool PeopleManager::walkMe(OnScreenPerson *thisPerson, bool move) { float xDiff, yDiff, maxDiff, s; for (;;) { @@ -646,7 +573,7 @@ bool walkMe(OnScreenPerson *thisPerson, bool move = true) { if (ABS(maxDiff) > s) { if (thisPerson->spinning) { spinStep(thisPerson); - setFrames(*thisPerson, ANI_WALK); + thisPerson->setFrames(ANI_WALK); } s = maxDiff / s; if (move) @@ -662,20 +589,20 @@ bool walkMe(OnScreenPerson *thisPerson, bool move = true) { } break; } - if (!doBorderStuff(thisPerson)) + if (!_vm->_floorMan->doBorderStuff(thisPerson)) break; } thisPerson->walking = false; - setFrames(*thisPerson, ANI_STAND); + thisPerson->setFrames(ANI_STAND); moveAndScale(*thisPerson, thisPerson->walkToX, thisPerson->walkToY); return false; } -bool makeWalkingPerson(int x, int y, int objNum, LoadedFunction *func, int di) { +bool PeopleManager::makeWalkingPerson(int x, int y, int objNum, LoadedFunction *func, int di) { if (x == 0 && y == 0) return false; - if (currentFloor->numPolygons == 0) + if (_vm->_floorMan->isFloorNoPolygon()) return false; OnScreenPerson *moveMe = findPerson(objNum); if (!moveMe) @@ -689,20 +616,20 @@ bool makeWalkingPerson(int x, int y, int objNum, LoadedFunction *func, int di) { moveMe->walkToX = x; moveMe->walkToY = y; - moveMe->walkToPoly = inFloor(x, y); + moveMe->walkToPoly = _vm->_floorMan->inFloor(x, y); if (moveMe->walkToPoly == -1) { - if (!handleClosestPoint(moveMe->walkToX, moveMe->walkToY, moveMe->walkToPoly)) + if (!_vm->_floorMan->handleClosestPoint(moveMe->walkToX, moveMe->walkToY, moveMe->walkToPoly)) return false; } - moveMe->inPoly = inFloor(moveMe->x, moveMe->y); + moveMe->inPoly = _vm->_floorMan->inFloor(moveMe->x, moveMe->y); if (moveMe->inPoly == -1) { int xxx = moveMe->x, yyy = moveMe->y; - if (!handleClosestPoint(xxx, yyy, moveMe->inPoly)) + if (!_vm->_floorMan->handleClosestPoint(xxx, yyy, moveMe->inPoly)) return false; } - doBorderStuff(moveMe); + _vm->_floorMan->doBorderStuff(moveMe); if (walkMe(moveMe, false) || moveMe->spinning) { moveMe->continueAfterWalking = func; return true; @@ -711,7 +638,7 @@ bool makeWalkingPerson(int x, int y, int objNum, LoadedFunction *func, int di) { } } -bool stopPerson(int o) { +bool PeopleManager::stopPerson(int o) { OnScreenPerson *moveMe = findPerson(o); if (moveMe) if (moveMe->continueAfterWalking) { @@ -719,13 +646,13 @@ bool stopPerson(int o) { moveMe->continueAfterWalking = NULL; moveMe->walking = false; moveMe->spinning = false; - setFrames(*moveMe, ANI_STAND); + moveMe->setFrames(ANI_STAND); return true; } return false; } -bool forceWalkingPerson(int x, int y, int objNum, LoadedFunction *func, int di) { +bool PeopleManager::forceWalkingPerson(int x, int y, int objNum, LoadedFunction *func, int di) { if (x == 0 && y == 0) return false; OnScreenPerson *moveMe = findPerson(objNum); @@ -746,7 +673,7 @@ bool forceWalkingPerson(int x, int y, int objNum, LoadedFunction *func, int di) moveMe->inPoly = 0; moveMe->walkToPoly = 0; - doBorderStuff(moveMe); + _vm->_floorMan->doBorderStuff(moveMe); if (walkMe(moveMe) || moveMe->spinning) { moveMe->continueAfterWalking = func; return true; @@ -755,7 +682,7 @@ bool forceWalkingPerson(int x, int y, int objNum, LoadedFunction *func, int di) } } -void jumpPerson(int x, int y, int objNum) { +void PeopleManager::jumpPerson(int x, int y, int objNum) { if (x == 0 && y == 0) return; OnScreenPerson *moveMe = findPerson(objNum); @@ -769,7 +696,7 @@ void jumpPerson(int x, int y, int objNum) { moveAndScale(*moveMe, x, y); } -bool floatCharacter(int f, int objNum) { +bool PeopleManager::floatCharacter(int f, int objNum) { OnScreenPerson *moveMe = findPerson(objNum); if (!moveMe) return false; @@ -777,7 +704,7 @@ bool floatCharacter(int f, int objNum) { return true; } -bool setCharacterWalkSpeed(int f, int objNum) { +bool PeopleManager::setCharacterWalkSpeed(int f, int objNum) { if (f <= 0) return false; OnScreenPerson *moveMe = findPerson(objNum); @@ -787,31 +714,29 @@ bool setCharacterWalkSpeed(int f, int objNum) { return true; } -void walkAllPeople() { - OnScreenPerson *thisPerson = allPeople; - - while (thisPerson) { +void PeopleManager::walkAllPeople() { + for (OnScreenPersonList::iterator it = _allPeople->begin(); it != _allPeople->end(); ++it) { + OnScreenPerson *thisPerson = (*it); if (thisPerson->walking) { walkMe(thisPerson); } else if (thisPerson->spinning) { spinStep(thisPerson); - setFrames(*thisPerson, ANI_STAND); + thisPerson->setFrames(ANI_STAND); } if ((!thisPerson->walking) && (!thisPerson->spinning) && thisPerson->continueAfterWalking) { restartFunction(thisPerson->continueAfterWalking); thisPerson->continueAfterWalking = NULL; } - thisPerson = thisPerson->next; } } -bool addPerson(int x, int y, int objNum, Persona *p) { +bool PeopleManager::addPerson(int x, int y, int objNum, Persona *p) { OnScreenPerson *newPerson = new OnScreenPerson; if (!checkNew(newPerson)) return false; - // EASY STUFF - newPerson->thisType = g_sludge->_objMan->loadObjectType(objNum); + // Init newPerson + newPerson->thisType = _vm->_objMan->loadObjectType(objNum); newPerson->scale = 1; newPerson->extra = 0; newPerson->continueAfterWalking = NULL; @@ -839,7 +764,7 @@ bool addPerson(int x, int y, int objNum, Persona *p) { newPerson->lastUsedAnim = 0; newPerson->frameTick = 0; - setFrames(*newPerson, ANI_STAND); + newPerson->setFrames(ANI_STAND); // HEIGHT (BASED ON 1st FRAME OF 1st ANIMATION... INC. SPECIAL CASES) int fNumSigned = p->animation[0]->frames[0].frameNum; @@ -854,27 +779,23 @@ bool addPerson(int x, int y, int objNum, Persona *p) { newPerson->height = p->animation[0]->theSprites->bank.sprites[fNum].yhot + 5; } - // NOW ADD IT IN THE RIGHT PLACE - OnScreenPerson **changethat = &allPeople; - - while (((*changethat) != NULL) && ((*changethat)->y < y)) - changethat = &((*changethat)->next); - - newPerson->next = (*changethat); - (*changethat) = newPerson; + // NOW INSERT IT IN THE RIGHT PLACE + bool inserted = false; + for (OnScreenPersonList::iterator it = _allPeople->begin(); it != _allPeople->end(); ++it) { + if ((*it)->y >= y) { + _allPeople->insert(it, newPerson); + inserted = true; + break; + } + } + if (!inserted) { + _allPeople->push_back(newPerson); + } return (bool)(newPerson->thisType != NULL); } -int timeForAnim(PersonaAnimation *fram) { - int total = 0; - for (int a = 0; a < fram->numFrames; a++) { - total += fram->frames[a].howMany; - } - return total; -} - -void animatePerson(int obj, PersonaAnimation *fram) { // Set a new SINGLE animation +void PeopleManager::animatePerson(int obj, PersonaAnimation *fram) { // Set a new SINGLE animation OnScreenPerson *moveMe = findPerson(obj); if (moveMe) { if (moveMe->continueAfterWalking) @@ -886,231 +807,127 @@ void animatePerson(int obj, PersonaAnimation *fram) { // Set a new SINGLE anima } } -void animatePerson(int obj, Persona *per) { // Set a new costume +void PeopleManager::animatePerson(int obj, Persona *per) { // Set a new costume OnScreenPerson *moveMe = findPerson(obj); if (moveMe) { - // if (moveMe->continueAfterWalking) abortFunction (moveMe->continueAfterWalking); - // moveMe->continueAfterWalking = NULL; - // moveMe->walking = false; moveMe->spinning = false; moveMe->myPersona = per; rethinkAngle(moveMe); if (moveMe->walking) { - setFrames(*moveMe, ANI_WALK); + moveMe->setFrames(ANI_WALK); } else { - setFrames(*moveMe, ANI_STAND); + moveMe->setFrames(ANI_STAND); } } } -void killAllPeople() { - OnScreenPerson *killPeople; - while (allPeople) { - if (allPeople->continueAfterWalking) - abortFunction(allPeople->continueAfterWalking); - allPeople->continueAfterWalking = NULL; - killPeople = allPeople; - allPeople = allPeople->next; - g_sludge->_objMan->removeObjectType(killPeople->thisType); - delete killPeople; +void PeopleManager::kill() { + for (OnScreenPersonList::iterator it = _allPeople->begin(); it != _allPeople->end(); ++it) { + if ((*it)->continueAfterWalking) + abortFunction((*it)->continueAfterWalking); + (*it)->continueAfterWalking = NULL; + _vm->_objMan->removeObjectType((*it)->thisType); + delete (*it); + (*it) = nullptr; } + _allPeople->clear(); } -void killMostPeople() { - OnScreenPerson *killPeople; - OnScreenPerson **lookyHere = &allPeople; - - while (*lookyHere) { - if ((*lookyHere)->extra & EXTRA_NOREMOVE) { - lookyHere = &(*lookyHere)->next; - } else { - killPeople = (*lookyHere); - - // Change last pointer to NEXT in the list instead - (*lookyHere) = killPeople->next; +void PeopleManager::killMostPeople() { + for (OnScreenPersonList::iterator it = _allPeople->begin(); it != _allPeople->end(); ++it) { + if (!((*it)->extra & EXTRA_NOREMOVE)) { + OnScreenPerson *killPeople = (*it); + _allPeople->reverse_erase(it); // Gone from the list... now free some memory if (killPeople->continueAfterWalking) abortFunction(killPeople->continueAfterWalking); killPeople->continueAfterWalking = NULL; - g_sludge->_objMan->removeObjectType(killPeople->thisType); + _vm->_objMan->removeObjectType(killPeople->thisType); delete killPeople; } } } -void removeOneCharacter(int i) { - OnScreenPerson *p = findPerson(i); - - if (p) { - if (overRegion == &personRegion && overRegion->thisType == p->thisType) { - overRegion = NULL; - } - - if (p->continueAfterWalking) - abortFunction(p->continueAfterWalking); - p->continueAfterWalking = NULL; - OnScreenPerson **killPeople; - - for (killPeople = &allPeople; *killPeople != p; killPeople = &((*killPeople)->next)) { - ; +void PeopleManager::removeOneCharacter(int i) { + OnScreenPerson *removePerson = findPerson(i); + if (removePerson) { + ScreenRegion *overRegion = _vm->_regionMan->getOverRegion(); + if (overRegion == _personRegion && overRegion->thisType == removePerson->thisType) { + overRegion = nullptr; } - *killPeople = p->next; - g_sludge->_objMan->removeObjectType(p->thisType); - delete p; - } -} - -bool saveAnim(PersonaAnimation *p, Common::WriteStream *stream) { - stream->writeUint16BE(p->numFrames); - if (p->numFrames) { - stream->writeUint32LE(p->theSprites->ID); + if (removePerson->continueAfterWalking) + abortFunction(removePerson->continueAfterWalking); + removePerson->continueAfterWalking = NULL; - for (int a = 0; a < p->numFrames; a++) { - stream->writeUint32LE(p->frames[a].frameNum); - stream->writeUint32LE(p->frames[a].howMany); - stream->writeUint32LE(p->frames[a].noise); - } + _allPeople->remove(removePerson); + _vm->_objMan->removeObjectType(removePerson->thisType); + delete removePerson; + removePerson = nullptr; } - return true; } -bool loadAnim(PersonaAnimation *p, Common::SeekableReadStream *stream) { - p->numFrames = stream->readUint16BE(); - - if (p->numFrames) { - int a = stream->readUint32LE(); - p->frames = new AnimFrame [p->numFrames]; - if (!checkNew(p->frames)) - return false; - p->theSprites = g_sludge->_gfxMan->loadBankForAnim(a); - - for (a = 0; a < p->numFrames; a++) { - p->frames[a].frameNum = stream->readUint32LE(); - p->frames[a].howMany = stream->readUint32LE(); - if (ssgVersion >= VERSION(2, 0)) { - p->frames[a].noise = stream->readUint32LE(); - } else { - p->frames[a].noise = 0; - } - } - } else { - p->theSprites = NULL; - p->frames = NULL; - } - return true; -} - -bool saveCostume(Persona *cossy, Common::WriteStream *stream) { - int a; - stream->writeUint16BE(cossy->numDirections); - for (a = 0; a < cossy->numDirections * 3; a++) { - if (!saveAnim(cossy->animation[a], stream)) - return false; - } -// debugCostume ("Saved", cossy); - return true; -} - -bool loadCostume(Persona *cossy, Common::SeekableReadStream *stream) { - int a; - cossy->numDirections = stream->readUint16BE(); - cossy->animation = new PersonaAnimation *[cossy->numDirections * 3]; - if (!checkNew(cossy->animation)) - return false; - for (a = 0; a < cossy->numDirections * 3; a++) { - cossy->animation[a] = new PersonaAnimation ; - if (!checkNew(cossy->animation[a])) - return false; - - if (!loadAnim(cossy->animation[a], stream)) - return false; - } -// debugCostume ("Loaded", cossy); - return true; -} - -bool savePeople(Common::WriteStream *stream) { - OnScreenPerson *me = allPeople; - int countPeople = 0, a; - - stream->writeSint16LE(scaleHorizon); - stream->writeSint16LE(scaleDivide); - - while (me) { - countPeople++; - me = me->next; - } - +bool PeopleManager::savePeople(Common::WriteStream* stream) { + stream->writeSint16LE(_scaleHorizon); + stream->writeSint16LE(_scaleDivide); + int countPeople = _allPeople->size(); stream->writeUint16BE(countPeople); - - me = allPeople; - for (a = 0; a < countPeople; a++) { - - stream->writeFloatLE(me->x); - stream->writeFloatLE(me->y); - - saveCostume(me->myPersona, stream); - saveAnim(me->myAnim, stream); - stream->writeByte(me->myAnim == me->lastUsedAnim); - - stream->writeFloatLE(me->scale); - - stream->writeUint16BE(me->extra); - stream->writeUint16BE(me->height); - stream->writeUint16BE(me->walkToX); - stream->writeUint16BE(me->walkToY); - stream->writeUint16BE(me->thisStepX); - stream->writeUint16BE(me->thisStepY); - stream->writeUint16BE(me->frameNum); - stream->writeUint16BE(me->frameTick); - stream->writeUint16BE(me->walkSpeed); - stream->writeUint16BE(me->spinSpeed); - stream->writeSint16LE(me->floaty); - stream->writeByte(me->show); - stream->writeByte(me->walking); - stream->writeByte(me->spinning); - if (me->continueAfterWalking) { + for (OnScreenPersonList::iterator it = _allPeople->begin(); it != _allPeople->end(); ++it) { + stream->writeFloatLE((*it)->x); + stream->writeFloatLE((*it)->y); + (*it)->myPersona->save(stream); + (*it)->myAnim->save(stream); + stream->writeByte((*it)->myAnim == (*it)->lastUsedAnim); + stream->writeFloatLE((*it)->scale); + stream->writeUint16BE((*it)->extra); + stream->writeUint16BE((*it)->height); + stream->writeUint16BE((*it)->walkToX); + stream->writeUint16BE((*it)->walkToY); + stream->writeUint16BE((*it)->thisStepX); + stream->writeUint16BE((*it)->thisStepY); + stream->writeUint16BE((*it)->frameNum); + stream->writeUint16BE((*it)->frameTick); + stream->writeUint16BE((*it)->walkSpeed); + stream->writeUint16BE((*it)->spinSpeed); + stream->writeSint16LE((*it)->floaty); + stream->writeByte((*it)->show); + stream->writeByte((*it)->walking); + stream->writeByte((*it)->spinning); + if ((*it)->continueAfterWalking) { stream->writeByte(1); - saveFunction(me->continueAfterWalking, stream); + saveFunction((*it)->continueAfterWalking, stream); } else { stream->writeByte(0); } - stream->writeUint16BE(me->direction); - stream->writeUint16BE(me->angle); - stream->writeUint16BE(me->angleOffset); - stream->writeUint16BE(me->wantAngle); - stream->writeSint16LE(me->directionWhenDoneWalking); - stream->writeSint16LE(me->inPoly); - stream->writeSint16LE(me->walkToPoly); - - stream->writeByte(me->r); - stream->writeByte(me->g); - stream->writeByte(me->b); - stream->writeByte(me->colourmix); - stream->writeByte(me->transparency); - - g_sludge->_objMan->saveObjectRef(me->thisType, stream); - - me = me->next; + stream->writeUint16BE((*it)->direction); + stream->writeUint16BE((*it)->angle); + stream->writeUint16BE((*it)->angleOffset); + stream->writeUint16BE((*it)->wantAngle); + stream->writeSint16LE((*it)->directionWhenDoneWalking); + stream->writeSint16LE((*it)->inPoly); + stream->writeSint16LE((*it)->walkToPoly); + stream->writeByte((*it)->r); + stream->writeByte((*it)->g); + stream->writeByte((*it)->b); + stream->writeByte((*it)->colourmix); + stream->writeByte((*it)->transparency); + _vm->_objMan->saveObjectRef((*it)->thisType, stream); } return true; } -bool loadPeople(Common::SeekableReadStream *stream) { - OnScreenPerson **pointy = &allPeople; - OnScreenPerson *me; +bool PeopleManager::loadPeople(Common::SeekableReadStream *stream) { + kill(); - scaleHorizon = stream->readSint16LE(); - scaleDivide = stream->readSint16LE(); + _scaleHorizon = stream->readSint16LE(); + _scaleDivide = stream->readSint16LE(); int countPeople = stream->readUint16BE(); int a; - allPeople = NULL; for (a = 0; a < countPeople; a++) { - me = new OnScreenPerson; + OnScreenPerson *me = new OnScreenPerson; if (!checkNew(me)) return false; @@ -1118,15 +935,15 @@ bool loadPeople(Common::SeekableReadStream *stream) { if (!checkNew(me->myPersona)) return false; - me->myAnim = new PersonaAnimation ; + me->myAnim = new PersonaAnimation; if (!checkNew(me->myAnim)) return false; me->x = stream->readFloatLE(); me->y = stream->readFloatLE(); - loadCostume(me->myPersona, stream); - loadAnim(me->myAnim, stream); + me->myPersona->load(stream); + me->myAnim->load(stream); me->lastUsedAnim = stream->readByte() ? me->myAnim : NULL; @@ -1173,7 +990,7 @@ bool loadPeople(Common::SeekableReadStream *stream) { } else { setMyDrawMode(me, stream->readUint16BE()); } - me->thisType = g_sludge->_objMan->loadObjectRef(stream); + me->thisType = _vm->_objMan->loadObjectRef(stream); // Anti-aliasing settings if (ssgVersion >= VERSION(1, 6)) { @@ -1184,13 +1001,22 @@ bool loadPeople(Common::SeekableReadStream *stream) { stream->readFloatLE(); } } - - me->next = NULL; - *pointy = me; - pointy = &(me->next); + _allPeople->push_back(me); } -// db ("End of loadPeople"); return true; } +void PeopleManager::freeze(FrozenStuffStruct *frozenStuff) { + frozenStuff->allPeople = _allPeople; + _allPeople = nullptr; + _allPeople = new OnScreenPersonList; +} + +void PeopleManager::resotre(FrozenStuffStruct *frozenStuff) { + kill(); + delete _allPeople; + _allPeople = nullptr; + _allPeople = frozenStuff->allPeople; +} + } // End of namespace Sludge diff --git a/engines/sludge/people.h b/engines/sludge/people.h index 95b8e923b6..db22d19ad1 100644 --- a/engines/sludge/people.h +++ b/engines/sludge/people.h @@ -26,6 +26,12 @@ namespace Sludge { +struct FrozenStuffStruct; +struct LoadedSpriteBank; +struct ScreenRegion; + +class SludgeEngine; + struct AnimFrame { int frameNum, howMany; int noise; @@ -41,26 +47,41 @@ struct AnimFrame { #define EXTRA_RECTANGULAR 64 struct PersonaAnimation { - struct LoadedSpriteBank *theSprites; - AnimFrame *frames; + LoadedSpriteBank *theSprites; + AnimFrame *frames; int numFrames; + + PersonaAnimation(); + PersonaAnimation(int num, struct VariableStack *&stacky); + PersonaAnimation(PersonaAnimation *orig); + ~PersonaAnimation(); + + // Setter & getter + int getTotalTime(); + + // Save & load + bool save(Common::WriteStream *stream); + bool load(Common::SeekableReadStream *stream); }; struct Persona { - PersonaAnimation **animation; + PersonaAnimation **animation; int numDirections; + + // Save & load + bool save(Common::WriteStream *stream); + bool load(Common::SeekableReadStream *stream); }; struct OnScreenPerson { float x, y; int height, floaty, walkSpeed; float scale; - OnScreenPerson *next; int walkToX, walkToY, thisStepX, thisStepY, inPoly, walkToPoly; bool walking, spinning; struct LoadedFunction *continueAfterWalking; - PersonaAnimation *myAnim; - PersonaAnimation *lastUsedAnim; + PersonaAnimation *myAnim; + PersonaAnimation *lastUsedAnim; Persona *myPersona; int frameNum, frameTick, angle, wantAngle, angleOffset; bool show; @@ -68,64 +89,84 @@ struct OnScreenPerson { struct ObjectType *thisType; int extra, spinSpeed; byte r, g, b, colourmix, transparency; + + void makeTalker(); + void makeSilent(); + void setFrames(int a); }; -// Initialisation and creation -bool initPeople(); -bool addPerson(int x, int y, int objNum, Persona *p); - -// Draw to screen and to backdrop -void drawPeople(); -void freezePeople(int, int); - -// Removalisationisms -void killAllPeople(); -void killMostPeople(); -void removeOneCharacter(int i); - -// Things which affect or use all characters -OnScreenPerson *findPerson(int v); -void setScale(int16 h, int16 d); - -// Things which affect one character -void makeTalker(OnScreenPerson &me); -void makeSilent(OnScreenPerson &me); -void setShown(bool h, int ob); -void setDrawMode(int h, int ob); -void setPersonTransparency(int ob, byte x); -void setPersonColourise(int ob, byte r, byte g, byte b, byte colourmix); - -// Moving 'em -void movePerson(int x, int y, int objNum); -bool makeWalkingPerson(int x, int y, int objNum, struct LoadedFunction *func, int di); -bool forceWalkingPerson(int x, int y, int objNum, struct LoadedFunction *func, int di); -void jumpPerson(int x, int y, int objNum); -void walkAllPeople(); -bool turnPersonToFace(int thisNum, int direc); -bool stopPerson(int o); -bool floatCharacter(int f, int objNum); -bool setCharacterWalkSpeed(int f, int objNum); - -// Animating 'em -void animatePerson(int obj, PersonaAnimation *); -void animatePerson(int obj, Persona *per); -PersonaAnimation *createPersonaAnim(int num, struct VariableStack *&stacky); -inline void setBankFile(PersonaAnimation *newP, LoadedSpriteBank *sB) { - newP->theSprites = sB; -} -bool setPersonExtra(int f, int newSetting); -int timeForAnim(PersonaAnimation *fram); -PersonaAnimation *copyAnim(PersonaAnimation *orig); -PersonaAnimation *makeNullAnim(); -void deleteAnim(PersonaAnimation *orig); - -// Loading and saving -bool saveAnim(PersonaAnimation *p, Common::WriteStream *stream); -bool loadAnim(PersonaAnimation *p, Common::SeekableReadStream *stream); -bool savePeople(Common::WriteStream *stream); -bool loadPeople(Common::SeekableReadStream *stream); -bool saveCostume(Persona *cossy, Common::WriteStream *stream); -bool loadCostume(Persona *cossy, Common::SeekableReadStream *stream); +typedef Common::List<OnScreenPerson *> OnScreenPersonList; + +class PeopleManager { +public: + PeopleManager(SludgeEngine *vm); + ~PeopleManager(); + + // Initialisation and creation + bool init(); + bool addPerson(int x, int y, int objNum, Persona *p); + + // Draw to screen and to backdrop + void drawPeople(); + void freezePeople(int, int); + + // Removalisationisms + void kill(); + void killMostPeople(); + void removeOneCharacter(int i); + + // Things which affect or use all characters + OnScreenPerson *findPerson(int v); + void setScale(int16 h, int16 d); + + // Things which affect one character + void setShown(bool h, int ob); + void setDrawMode(int h, int ob); + void setPersonTransparency(int ob, byte x); + void setPersonColourise(int ob, byte r, byte g, byte b, byte colourmix); + + // Moving 'em + void movePerson(int x, int y, int objNum); + bool makeWalkingPerson(int x, int y, int objNum, struct LoadedFunction *func, int di); + bool forceWalkingPerson(int x, int y, int objNum, struct LoadedFunction *func, int di); + void jumpPerson(int x, int y, int objNum); + void walkAllPeople(); + bool turnPersonToFace(int thisNum, int direc); + bool stopPerson(int o); + bool floatCharacter(int f, int objNum); + bool setCharacterWalkSpeed(int f, int objNum); + + // Animating 'em + void animatePerson(int obj, PersonaAnimation *); + void animatePerson(int obj, Persona *per); + bool setPersonExtra(int f, int newSetting); + + // Loading and saving + bool savePeople(Common::WriteStream *stream); + bool loadPeople(Common::SeekableReadStream *stream); + + // Freeze + void freeze(FrozenStuffStruct *frozenStuff); + void resotre(FrozenStuffStruct *frozenStuff); + +private: + ScreenRegion *_personRegion; + OnScreenPersonList *_allPeople; + int16 _scaleHorizon; + int16 _scaleDivide; + + SludgeEngine *_vm; + + void shufflePeople(); + + // OnScreenPerson manipulation + void turnMeAngle(OnScreenPerson *thisPerson, int direc); + void spinStep(OnScreenPerson *thisPerson); + void rethinkAngle(OnScreenPerson *thisPerson); + void moveAndScale(OnScreenPerson &me, float x, float y); + void setMyDrawMode(OnScreenPerson *moveMe, int h); + bool walkMe(OnScreenPerson *thisPerson, bool move = true); +}; } // End of namespace Sludge diff --git a/engines/sludge/region.cpp b/engines/sludge/region.cpp index 7593fe4aee..4410951057 100644 --- a/engines/sludge/region.cpp +++ b/engines/sludge/region.cpp @@ -33,75 +33,65 @@ namespace Sludge { -ScreenRegion *allScreenRegions = nullptr; -ScreenRegion *overRegion = nullptr; -ScreenRegion *lastRegion = nullptr; - -void showBoxes() { - ScreenRegion*huntRegion = allScreenRegions; - - while (huntRegion) { - g_sludge->_gfxMan->drawVerticalLine(huntRegion->x1, huntRegion->y1, huntRegion->y2); - g_sludge->_gfxMan->drawVerticalLine(huntRegion->x2, huntRegion->y1, huntRegion->y2); - g_sludge->_gfxMan->drawHorizontalLine(huntRegion->x1, huntRegion->y1, huntRegion->x2); - g_sludge->_gfxMan->drawHorizontalLine(huntRegion->x1, huntRegion->y2, huntRegion->x2); - huntRegion = huntRegion->next; - } +RegionManager::RegionManager(SludgeEngine *vm) +{ + _vm = vm; + _allScreenRegions = new ScreenRegionList; + _allScreenRegions->clear(); + _lastRegion = nullptr; + _overRegion = nullptr; +} + +RegionManager::~RegionManager() { + kill(); + + delete _allScreenRegions; + _allScreenRegions = nullptr; } -void removeScreenRegion(int objectNum) { - ScreenRegion **huntRegion = &allScreenRegions; - ScreenRegion *killMe; +void RegionManager::showBoxes() { + for (ScreenRegionList::iterator it = _allScreenRegions->begin(); it != _allScreenRegions->end(); ++it) { + g_sludge->_gfxMan->drawVerticalLine((*it)->x1, (*it)->y1, (*it)->y2); + g_sludge->_gfxMan->drawVerticalLine((*it)->x2, (*it)->y1, (*it)->y2); + g_sludge->_gfxMan->drawHorizontalLine((*it)->x1, (*it)->y1, (*it)->x2); + g_sludge->_gfxMan->drawHorizontalLine((*it)->x1, (*it)->y2, (*it)->x2); + } +} - while (*huntRegion) { - if ((*huntRegion)->thisType->objectNum == objectNum) { - killMe = *huntRegion; - *huntRegion = killMe->next; +void RegionManager::removeScreenRegion(int objectNum) { + for (ScreenRegionList::iterator it = _allScreenRegions->begin(); it != _allScreenRegions->end(); ++it) { + if ((*it)->thisType->objectNum == objectNum) { + ScreenRegion *killMe = *it; g_sludge->_objMan->removeObjectType(killMe->thisType); - if (killMe == overRegion) - overRegion = NULL; + if (killMe == _overRegion) + _overRegion = nullptr; delete killMe; - killMe = NULL; - } else { - huntRegion = &((*huntRegion)->next); + killMe = nullptr; + _allScreenRegions->reverse_erase(it); } } } -void saveRegions(Common::WriteStream *stream) { - int numRegions = 0; - ScreenRegion *thisRegion = allScreenRegions; - while (thisRegion) { - thisRegion = thisRegion->next; - numRegions++; - } +void RegionManager::saveRegions(Common::WriteStream *stream) { + uint numRegions = _allScreenRegions->size(); stream->writeUint16BE(numRegions); - thisRegion = allScreenRegions; - while (thisRegion) { - stream->writeUint16BE(thisRegion->x1); - stream->writeUint16BE(thisRegion->y1); - stream->writeUint16BE(thisRegion->x2); - stream->writeUint16BE(thisRegion->y2); - stream->writeUint16BE(thisRegion->sX); - stream->writeUint16BE(thisRegion->sY); - stream->writeUint16BE(thisRegion->di); - g_sludge->_objMan->saveObjectRef(thisRegion->thisType, stream); - - thisRegion = thisRegion->next; + for (ScreenRegionList::iterator it = _allScreenRegions->begin(); it != _allScreenRegions->end(); ++it) { + stream->writeUint16BE((*it)->x1); + stream->writeUint16BE((*it)->y1); + stream->writeUint16BE((*it)->x2); + stream->writeUint16BE((*it)->y2); + stream->writeUint16BE((*it)->sX); + stream->writeUint16BE((*it)->sY); + stream->writeUint16BE((*it)->di); + g_sludge->_objMan->saveObjectRef((*it)->thisType, stream); } } -void loadRegions(Common::SeekableReadStream *stream) { +void RegionManager::loadRegions(Common::SeekableReadStream *stream) { int numRegions = stream->readUint16BE(); - - ScreenRegion *newRegion; - ScreenRegion **pointy = &allScreenRegions; - while (numRegions--) { - newRegion = new ScreenRegion; - *pointy = newRegion; - pointy = &(newRegion->next); - + ScreenRegion *newRegion = new ScreenRegion; + _allScreenRegions->push_back(newRegion); newRegion->x1 = stream->readUint16BE(); newRegion->y1 = stream->readUint16BE(); newRegion->x2 = stream->readUint16BE(); @@ -111,22 +101,20 @@ void loadRegions(Common::SeekableReadStream *stream) { newRegion->di = stream->readUint16BE(); newRegion->thisType = g_sludge->_objMan->loadObjectRef(stream); } - *pointy = NULL; } -void killAllRegions() { - ScreenRegion *killRegion; - while (allScreenRegions) { - killRegion = allScreenRegions; - allScreenRegions = allScreenRegions->next; +void RegionManager::kill() { + for (ScreenRegionList::iterator it = _allScreenRegions->begin(); it != _allScreenRegions->end(); ++it) { + ScreenRegion *killRegion = (*it); g_sludge->_objMan->removeObjectType(killRegion->thisType); delete killRegion; } - overRegion = nullptr; - lastRegion = nullptr; + _allScreenRegions->clear(); + _overRegion = nullptr; + _lastRegion = nullptr; } -bool addScreenRegion(int x1, int y1, int x2, int y2, int sX, int sY, int di, +bool RegionManager::addScreenRegion(int x1, int y1, int x2, int y2, int sX, int sY, int di, int objectNum) { ScreenRegion *newRegion = new ScreenRegion; if (!checkNew(newRegion)) @@ -139,40 +127,51 @@ bool addScreenRegion(int x1, int y1, int x2, int y2, int sX, int sY, int di, newRegion->sX = sX; newRegion->sY = sY; newRegion->thisType = g_sludge->_objMan->loadObjectType(objectNum); - newRegion->next = allScreenRegions; - allScreenRegions = newRegion; - return (bool) (newRegion->thisType != NULL); + _allScreenRegions->push_front(newRegion); + return (bool) (newRegion->thisType != nullptr); } -void getOverRegion() { +void RegionManager::updateOverRegion() { int cameraX = g_sludge->_gfxMan->getCamX(); int cameraY = g_sludge->_gfxMan->getCamY(); - ScreenRegion *thisRegion = allScreenRegions; - while (thisRegion) { - if ((g_sludge->_evtMan->mouseX() >= thisRegion->x1 - cameraX) - && (g_sludge->_evtMan->mouseY() >= thisRegion->y1 - cameraY) - && (g_sludge->_evtMan->mouseX() <= thisRegion->x2 - cameraX) - && (g_sludge->_evtMan->mouseY() <= thisRegion->y2 - cameraY)) { - overRegion = thisRegion; + for (ScreenRegionList::iterator it = _allScreenRegions->begin(); it != _allScreenRegions->end(); ++it) { + if ((g_sludge->_evtMan->mouseX() >= (*it)->x1 - cameraX) + && (g_sludge->_evtMan->mouseY() >= (*it)->y1 - cameraY) + && (g_sludge->_evtMan->mouseX() <= (*it)->x2 - cameraX) + && (g_sludge->_evtMan->mouseY() <= (*it)->y2 - cameraY)) { + _overRegion = (*it); return; } - thisRegion = thisRegion->next; } - overRegion = NULL; + _overRegion = nullptr; return; } -ScreenRegion *getRegionForObject(int obj) { - ScreenRegion *thisRegion = allScreenRegions; - - while (thisRegion) { - if (obj == thisRegion->thisType->objectNum) { - return thisRegion; +ScreenRegion *RegionManager::getRegionForObject(int obj) { + for (ScreenRegionList::iterator it = _allScreenRegions->begin(); it != _allScreenRegions->end(); ++it) { + if (obj == (*it)->thisType->objectNum) { + return (*it); } - thisRegion = thisRegion->next; } - return NULL; + return nullptr; +} + +void RegionManager::freeze(FrozenStuffStruct *frozenStuff) { + frozenStuff->allScreenRegions = _allScreenRegions; + _allScreenRegions = new ScreenRegionList; + _overRegion = nullptr; +} + +void RegionManager::resotre(FrozenStuffStruct *frozenStuff) { + // kill + kill(); + delete _allScreenRegions; + _allScreenRegions = nullptr; + + // restore + _allScreenRegions = frozenStuff->allScreenRegions; + _overRegion = nullptr; } } // End of namespace Sludge diff --git a/engines/sludge/region.h b/engines/sludge/region.h index 74294814f6..5f307cc9d0 100644 --- a/engines/sludge/region.h +++ b/engines/sludge/region.h @@ -23,24 +23,56 @@ #define SLUDGE_REGION_H #include "sludge/objtypes.h" +#include "sludge/freeze.h" namespace Sludge { struct ScreenRegion { int x1, y1, x2, y2, sX, sY, di; ObjectType *thisType; - ScreenRegion *next; }; +typedef Common::List<ScreenRegion *> ScreenRegionList; -bool addScreenRegion(int x1, int y1, int x2, int y2, int, int, int, int objectNum); -void getOverRegion(); -ScreenRegion *getRegionForObject(int obj); -void removeScreenRegion(int objectNum); -void loadRegions(Common::SeekableReadStream *stream); -void saveRegions(Common::WriteStream *stream); -void killAllRegions(); +class RegionManager { +public: + RegionManager(SludgeEngine *vm); + ~RegionManager(); -void showBoxes(); + // Kill + void kill(); + + // Add & remove region + bool addScreenRegion(int x1, int y1, int x2, int y2, int, int, int, int objectNum); + void removeScreenRegion(int objectNum); + + // Save & load + void loadRegions(Common::SeekableReadStream *stream); + void saveRegions(Common::WriteStream *stream); + + // Draw + void showBoxes(); + + // Setter & getter + ScreenRegion *getRegionForObject(int obj); + ScreenRegion *getOverRegion() const { return _overRegion; } + void setOverRegion(ScreenRegion *newRegion) { _overRegion = newRegion; } + void updateOverRegion(); + bool isRegionChanged() const { return _lastRegion != _overRegion; } + void updateLastRegion() { _lastRegion = _overRegion; } + void resetOverRegion() { _overRegion = nullptr; } + void resetLastRegion() { _lastRegion = nullptr; } + + // Freeze + void freeze(FrozenStuffStruct *frozenStuff); + void resotre(FrozenStuffStruct *frozenStuff); + +private: + SludgeEngine *_vm; + + ScreenRegionList *_allScreenRegions; + ScreenRegion *_overRegion; + ScreenRegion *_lastRegion; +}; } // End of namespace Sludge diff --git a/engines/sludge/savedata.cpp b/engines/sludge/savedata.cpp index 9e2c92395d..b103523f8c 100644 --- a/engines/sludge/savedata.cpp +++ b/engines/sludge/savedata.cpp @@ -20,44 +20,45 @@ * */ -#include "common/file.h" +#include "common/savefile.h" #include "sludge/allfiles.h" #include "sludge/moreio.h" #include "sludge/newfatal.h" +#include "sludge/savedata.h" #include "sludge/variable.h" #define LOAD_ERROR "Can't load custom data...\n\n" namespace Sludge { -const char UTF8_CHECKER[] = {'U', 'N', '\xef', '\xbf', '\xbd', 'L', 'O', '\xef', '\xbf', '\xbd', 'C', 'K', 'E', 'D', '\0'}; -uint16 saveEncoding = false; -char encode1 = 0; -char encode2 = 0; +const char CustomSaveHelper::UTF8_CHECKER[] = {'U', 'N', '\xef', '\xbf', '\xbd', 'L', 'O', '\xef', '\xbf', '\xbd', 'C', 'K', 'E', 'D', '\0'}; +uint16 CustomSaveHelper::_saveEncoding = false; +char CustomSaveHelper::_encode1 = 0; +char CustomSaveHelper::_encode2 = 0; -void writeStringEncoded(const Common::String &s, Common::WriteStream *stream) { - int len = s.size(); +void CustomSaveHelper::writeStringEncoded(const Common::String checker, Common::WriteStream *stream) { + int len = checker.size(); stream->writeUint16BE(len); for (int a = 0; a < len; a++) { - stream->writeByte(s[a] ^ encode1); - encode1 += encode2; + stream->writeByte(checker[a] ^ _encode1); + _encode1 += _encode2; } } -Common::String readStringEncoded(Common::File *fp) { +Common::String CustomSaveHelper::readStringEncoded(Common::SeekableReadStream *fp) { int len = fp->readUint16BE(); Common::String res = ""; for (int a = 0; a < len; a++) { - res += (char)(fp->readByte() ^ encode1); - encode1 += encode2; + res += (char)(fp->readByte() ^ _encode1); + _encode1 += _encode2; } return res; } -char *readTextPlain(Common::File *fp) { +char *CustomSaveHelper::readTextPlain(Common::SeekableReadStream *fp) { int32 startPos; uint32 stringSize = 0; @@ -94,86 +95,66 @@ char *readTextPlain(Common::File *fp) { return reply; } -bool fileToStack(const Common::String &filename, StackHandler *sH) { - +bool CustomSaveHelper::fileToStack(const Common::String &filename, StackHandler *sH) { Variable stringVar; stringVar.varType = SVT_NULL; - Common::String checker = saveEncoding ? "[Custom data (encoded)]\r\n" : "[Custom data (ASCII)]\n"; - - Common::File fd; - - if (!fd.open(filename)) { -#if 0 - char currentDir[1000]; - if (!getcwd(currentDir, 998)) { - debugOut("Can't get current directory.\n"); - } - - if (chdir(gamePath)) { - debugOut("Error: Failed changing to directory %s\n", gamePath); - } + Common::String checker = _saveEncoding ? "[Custom data (encoded)]\r\n" : "[Custom data (ASCII)]\n"; - if (chdir(currentDir)) { - debugOut("Error: Failed changing to directory %s\n", currentDir); - } + Common::InSaveFile *fp = g_system->getSavefileManager()->openForLoading(filename); - if (!fd.open(filename)) { - return fatal("No such file", filename); - } -#endif + if (fp == NULL) { return fatal("No such file", filename); //TODO: false value } - encode1 = (byte)saveEncoding & 255; - encode2 = (byte)(saveEncoding >> 8); + _encode1 = (byte)_saveEncoding & 255; + _encode2 = (byte)(_saveEncoding >> 8); for (uint i = 0; i < checker.size(); ++i) { - if (fd.readByte() != checker[i]) { - fd.close(); + if (fp->readByte() != checker[i]) { + delete fp; return fatal(LOAD_ERROR "This isn't a SLUDGE custom data file:", filename); } } - if (saveEncoding) { - checker = readStringEncoded(&fd); + if (_saveEncoding) { + checker = readStringEncoded(fp); if (checker == UTF8_CHECKER) { - fd.close(); - return fatal( - LOAD_ERROR "The current file encoding setting does not match the encoding setting used when this file was created:", filename); + delete fp; + return fatal(LOAD_ERROR "The current file encoding setting does not match the encoding setting used when this file was created:", filename); } } for (;;) { - if (saveEncoding) { - char i = fd.readByte() ^ encode1; + if (_saveEncoding) { + char i = fp->readByte() ^ _encode1; - if (fd.eos()) + if (fp->eos()) break; switch (i) { case 0: { - Common::String g = readStringEncoded(&fd); - makeTextVar(stringVar, g); + Common::String g = readStringEncoded(fp); + stringVar.makeTextVar(g); } break; case 1: - setVariable(stringVar, SVT_INT, fd.readUint32LE()); + stringVar.setVariable(SVT_INT, fp->readUint32LE()); break; case 2: - setVariable(stringVar, SVT_INT, fd.readByte()); + stringVar.setVariable(SVT_INT, fp->readByte()); break; default: fatal(LOAD_ERROR "Corrupt custom data file:", filename); - fd.close(); + delete fp; return false; } } else { - char *line = readTextPlain(&fd); + char *line = readTextPlain(fp); if (!line) break; - makeTextVar(stringVar, line); + stringVar.makeTextVar(line); } if (sH->first == NULL) { @@ -188,63 +169,66 @@ bool fileToStack(const Common::String &filename, StackHandler *sH) { sH->last = sH->last->next; } } - fd.close(); + + delete fp; return true; } -bool stackToFile(const Common::String &filename, const Variable &from) { -#if 0 - FILE *fp = fopen(filename, saveEncoding ? "wb" : "wt"); - if (!fp) return fatal("Can't create file", filename); +bool CustomSaveHelper::stackToFile(const Common::String &filename, const Variable &from) { + Common::OutSaveFile *fp = g_system->getSavefileManager()->openForSaving(filename); + if (fp == NULL) { + return fatal("Can't create file", filename); + } VariableStack *hereWeAre = from.varData.theStack -> first; - encode1 = (byte)saveEncoding & 255; - encode2 = (byte)(saveEncoding >> 8); + _encode1 = (byte)_saveEncoding & 255; + _encode2 = (byte)(_saveEncoding >> 8); - if (saveEncoding) { - fprintf(fp, "[Custom data (encoded)]\r\n"); + if (_saveEncoding) { + fp->writeString("[Custom data (encoded)]\r\n"); writeStringEncoded(UTF8_CHECKER, fp); } else { - fprintf(fp, "[Custom data (ASCII)]\n"); + fp->writeString("[Custom data (ASCII)]\n"); } while (hereWeAre) { - if (saveEncoding) { + if (_saveEncoding) { switch (hereWeAre -> thisVar.varType) { case SVT_STRING: - fputc(encode1, fp); - writeStringEncoded(hereWeAre -> thisVar.varData.theString, fp); - break; + fp->writeByte(_encode1); + writeStringEncoded(hereWeAre -> thisVar.varData.theString, fp); + break; case SVT_INT: - // Small enough to be stored as a char - if (hereWeAre -> thisVar.varData.intValue >= 0 && hereWeAre -> thisVar.varData.intValue < 256) { - fputc(2 ^ encode1, fp); - fputc(hereWeAre -> thisVar.varData.intValue, fp); - } else { - fputc(1 ^ encode1, fp); - fp->writeUint32LE(hereWeAre -> thisVar.varData.intValue); - } - break; + // Small enough to be stored as a char + if (hereWeAre -> thisVar.varData.intValue >= 0 && hereWeAre -> thisVar.varData.intValue < 256) { + fp->writeByte(2 ^ _encode1); + fp->writeByte(hereWeAre -> thisVar.varData.intValue); + } else { + fp->writeByte(1 ^ _encode1); + fp->writeUint32LE(hereWeAre -> thisVar.varData.intValue); + } + break; default: - fatal("Can't create an encoded custom data file containing anything other than numbers and strings", filename); - fclose(fp); - return false; + fatal("Can't create an encoded custom data file containing anything other than numbers and strings", filename); + delete fp; + return false; } } else { - char *makeSureItsText = getTextFromAnyVar(hereWeAre -> thisVar); - if (makeSureItsText == NULL) break; - fprintf(fp, "%s\n", makeSureItsText); - delete makeSureItsText; + Common::String makeSureItsText = hereWeAre->thisVar.getTextFromAnyVar(); + if (makeSureItsText.empty()) + break; + fp->writeString((makeSureItsText + "\n").c_str()); } hereWeAre = hereWeAre -> next; } - fclose(fp); -#endif + + delete fp; + return true; } diff --git a/engines/sludge/savedata.h b/engines/sludge/savedata.h index 956df93f35..90b093f4ff 100644 --- a/engines/sludge/savedata.h +++ b/engines/sludge/savedata.h @@ -24,8 +24,26 @@ namespace Sludge { -bool fileToStack(const Common::String &filename, StackHandler *sH); -bool stackToFile(const Common::String &filename, const Variable &from); +struct StackHandler; +struct Variable; + +class CustomSaveHelper { +public: + static bool fileToStack(const Common::String &filename, StackHandler *sH); + static bool stackToFile(const Common::String &filename, const Variable &from); + + static uint16 _saveEncoding; + +private: + static const char UTF8_CHECKER[]; + static char _encode1; + static char _encode2; + + static void writeStringEncoded(const Common::String checker, Common::WriteStream *stream); + static Common::String readStringEncoded(Common::SeekableReadStream *fp); + static char *readTextPlain(Common::SeekableReadStream *fp); + +}; } // End of namespace Sludge diff --git a/engines/sludge/sludge.cpp b/engines/sludge/sludge.cpp index d14f92202f..821539d877 100644 --- a/engines/sludge/sludge.cpp +++ b/engines/sludge/sludge.cpp @@ -28,11 +28,15 @@ #include "sludge/cursors.h" #include "sludge/event.h" #include "sludge/fonttext.h" +#include "sludge/floor.h" #include "sludge/graphics.h" +#include "sludge/main_loop.h" +#include "sludge/newfatal.h" +#include "sludge/people.h" +#include "sludge/region.h" #include "sludge/sludge.h" #include "sludge/sound.h" #include "sludge/speech.h" -#include "sludge/main_loop.h" namespace Sludge { @@ -68,11 +72,10 @@ SludgeEngine::SludgeEngine(OSystem *syst, const SludgeGameDescription *gameDesc) launchNext = ""; loadNow = ""; gamePath = ""; - bundleFolder = ""; - fatalMessage = ""; - fatalInfo = "Initialisation error! Something went wrong before we even got started!"; // Init managers + _fatalMan = new FatalMsgManager(); + _peopleMan = new PeopleManager(this); _resMan = new ResourceManager(); _languageMan = new LanguageManager(); _objMan = new ObjectManager(this); @@ -82,6 +85,8 @@ SludgeEngine::SludgeEngine(OSystem *syst, const SludgeGameDescription *gameDesc) _txtMan = new TextManager(); _cursorMan = new CursorManager(this); _speechMan = new SpeechManager(this); + _regionMan = new RegionManager(this); + _floorMan = new FloorManager(this); } SludgeEngine::~SludgeEngine() { @@ -122,6 +127,14 @@ SludgeEngine::~SludgeEngine() { _resMan = nullptr; delete _speechMan; _speechMan = nullptr; + delete _regionMan; + _regionMan = nullptr; + delete _peopleMan; + _peopleMan = nullptr; + delete _floorMan; + _floorMan = nullptr; + delete _fatalMan; + _fatalMan = nullptr; } Common::Error SludgeEngine::run() { diff --git a/engines/sludge/sludge.h b/engines/sludge/sludge.h index 83c6359f52..692af64071 100644 --- a/engines/sludge/sludge.h +++ b/engines/sludge/sludge.h @@ -40,7 +40,11 @@ extern SludgeEngine *g_sludge; class CursorManager; class EventManager; +class FatalMsgManager; +class FloorManager; class GraphicsManager; +class PeopleManager; +class RegionManager; class SoundManager; class SpeechManager; class TextManager; @@ -71,9 +75,6 @@ public: Common::String launchNext; Common::String loadNow; Common::String gamePath; - Common::String bundleFolder; - Common::String fatalMessage; - Common::String fatalInfo; // timer Timer _timer; @@ -88,6 +89,10 @@ public: TextManager *_txtMan; CursorManager *_cursorMan; SpeechManager *_speechMan; + RegionManager *_regionMan; + PeopleManager *_peopleMan; + FloorManager *_floorMan; + FatalMsgManager *_fatalMan; SludgeEngine(OSystem *syst, const SludgeGameDescription *gameDesc); virtual ~SludgeEngine(); diff --git a/engines/sludge/sludger.cpp b/engines/sludge/sludger.cpp index 812f42fb5d..f9dedf22fc 100644 --- a/engines/sludge/sludger.cpp +++ b/engines/sludge/sludger.cpp @@ -32,69 +32,46 @@ #include "sludge/freeze.h" #include "sludge/floor.h" #include "sludge/fileset.h" +#include "sludge/function.h" #include "sludge/graphics.h" #include "sludge/imgloader.h" -#include "sludge/loadsave.h" #include "sludge/language.h" #include "sludge/moreio.h" #include "sludge/newfatal.h" #include "sludge/objtypes.h" #include "sludge/people.h" #include "sludge/region.h" -#include "sludge/statusba.h" -#include "sludge/sprites.h" -#include "sludge/sprbanks.h" -#include "sludge/sound.h" +#include "sludge/savedata.h" #include "sludge/sludge.h" #include "sludge/sludger.h" +#include "sludge/sound.h" #include "sludge/speech.h" -#include "sludge/transition.h" +#include "sludge/sprites.h" +#include "sludge/sprbanks.h" +#include "sludge/statusba.h" #include "sludge/variable.h" #include "sludge/version.h" #include "sludge/zbuffer.h" namespace Sludge { -extern int dialogValue; +extern int numBIFNames; +extern Common::String *allBIFNames; +extern int numUserFunc; +extern Common::String *allUserFunc; -int numBIFNames = 0; -Common::String *allBIFNames; -int numUserFunc = 0; -Common::String *allUserFunc = NULL; -int numResourceNames = 0; -Common::String *allResourceNames = NULL; int selectedLanguage = 0; int gameVersion; FILETIME fileTime; -bool captureAllKeys = false; - -byte brightnessLevel = 255; - -extern LoadedFunction *saverFunc; - -LoadedFunction *allRunningFunctions = NULL; -VariableStack *noStack = NULL; -Variable *globalVars; int numGlobals = 0; -extern SpritePalette pastePalette; extern Variable *launchResult; -extern int lastFramesPerSecond, thumbWidth, thumbHeight; +extern Variable *globalVars; +extern VariableStack *noStack; extern bool allowAnyFilename; -extern byte fadeMode; -extern uint16 saveEncoding; - -const char *sludgeText[] = { "?????", "RETURN", "BRANCH", "BR_ZERO", - "SET_GLOBAL", "SET_LOCAL", "LOAD_GLOBAL", "LOAD_LOCAL", "PLUS", "MINUS", - "MULT", "DIVIDE", "AND", "OR", "EQUALS", "NOT_EQ", "MODULUS", - "LOAD_VALUE", "LOAD_BUILT", "LOAD_FUNC", "CALLIT", "LOAD_STRING", - "LOAD_FILE", "LOAD_OBJTYPE", "NOT", "LOAD_NULL", "STACK_PUSH", - "LESSTHAN", "MORETHAN", "NEGATIVE", "U", "LESS_EQUAL", "MORE_EQUAL", - "INC_LOCAL", "DEC_LOCAL", "INC_GLOBAL", "DEC_GLOBAL", "INDEXSET", - "INDEXGET", "INC_INDEX", "DEC_INDEX", "QUICK_PUSH" }; Common::File *openAndVerify(const Common::String &filename, char extra1, char extra2, const char *er, int &fileVersion) { @@ -146,15 +123,15 @@ Common::File *openAndVerify(const Common::String &filename, char extra1, char ex } void initSludge() { + g_sludge->_timer.reset(); g_sludge->_languageMan->init(); g_sludge->_gfxMan->init(); g_sludge->_resMan->init(); - initPeople(); - initFloor(); + g_sludge->_peopleMan->init(); + g_sludge->_floorMan->init(); g_sludge->_objMan->init(); g_sludge->_speechMan->init(); initStatusBar(); - resetRandW(); g_sludge->_evtMan->init(); g_sludge->_txtMan->init(); g_sludge->_cursorMan->init(); @@ -164,27 +141,23 @@ void initSludge() { g_sludge->_soundMan->initSoundStuff(); } + CustomSaveHelper::_saveEncoding = false; + // global variables numGlobals = 0; launchResult = nullptr; - lastFramesPerSecond = -1; - thumbWidth = thumbHeight = 0; allowAnyFilename = true; - captureAllKeys = false; noStack = nullptr; numBIFNames = numUserFunc = 0; allUserFunc = allBIFNames = nullptr; - brightnessLevel = 255; - fadeMode = 2; - saveEncoding = false; } void killSludge() { killAllFunctions(); - killAllPeople(); - killAllRegions(); - setFloorNull(); + g_sludge->_peopleMan->kill(); + g_sludge->_regionMan->kill(); + g_sludge->_floorMan->kill(); g_sludge->_speechMan->kill(); g_sludge->_languageMan->kill(); g_sludge->_gfxMan->kill(); @@ -196,7 +169,6 @@ void killSludge() { g_sludge->_cursorMan->kill(); // global variables - pastePalette.reset(); numBIFNames = numUserFunc = 0; delete []allUserFunc; delete []allBIFNames; @@ -205,7 +177,6 @@ void killSludge() { bool initSludge(const Common::String &filename) { initSludge(); - int a = 0; Common::File *fp = openAndVerify(filename, 'G', 'E', ERROR_BAD_HEADER, gameVersion); if (!fp) return false; @@ -232,19 +203,9 @@ bool initSludge(const Common::String &filename) { allUserFunc[fn].clear(); allUserFunc[fn] = readString(fp); } - if (gameVersion >= VERSION(1, 3)) { - numResourceNames = fp->readUint16BE(); - debugC(2, kSludgeDebugDataLoad, "numResourceNames %i", - numResourceNames); - allResourceNames = new Common::String[numResourceNames]; - if (!checkNew(allResourceNames)) - return false; - for (int fn = 0; fn < numResourceNames; fn++) { - allResourceNames[fn].clear(); - allResourceNames[fn] = readString(fp); - debugC(2, kSludgeDebugDataLoad, "Resource %i: %s", fn, allResourceNames[fn].c_str()); - } + if (gameVersion >= VERSION(1, 3)) { + g_sludge->_resMan->readResourceNames(fp); } } @@ -256,7 +217,7 @@ bool initSludge(const Common::String &filename) { int specialSettings = fp->readByte(); debugC(2, kSludgeDebugDataLoad, "specialSettings : %i", specialSettings); - g_sludge->_timer.setDesiredfps(1000 / fp->readByte()); + g_sludge->_timer.setDesiredFPS(1000 / fp->readByte()); readString(fp); // Unused - was used for registration purposes. @@ -314,8 +275,6 @@ bool initSludge(const Common::String &filename) { globalVars = new Variable[numGlobals]; if (!checkNew(globalVars)) return false; - for (a = 0; a < numGlobals; a++) - initVarNew(globalVars[a]); // Get language selected by user g_sludge->_resMan->setData(fp); @@ -334,7 +293,7 @@ void displayBase() { g_sludge->_gfxMan->clear(); // Clear screen g_sludge->_gfxMan->drawBackDrop();// Draw Backdrop g_sludge->_gfxMan->drawZBuffer(g_sludge->_gfxMan->getCamX(), g_sludge->_gfxMan->getCamY(), false); - drawPeople();// Then add any moving characters... + g_sludge->_peopleMan->drawPeople();// Then add any moving characters... g_sludge->_gfxMan->displaySpriteLayers(); } @@ -344,712 +303,6 @@ void sludgeDisplay() { drawStatusBar(); g_sludge->_cursorMan->displayCursor(); g_sludge->_gfxMan->display(); - if (brightnessLevel < 255) fixBrightness();// This is for transitionLevel special effects -} - -void pauseFunction(LoadedFunction *fun) { - LoadedFunction **huntAndDestroy = &allRunningFunctions; - while (*huntAndDestroy) { - if (fun == *huntAndDestroy) { - (*huntAndDestroy) = (*huntAndDestroy)->next; - fun->next = NULL; - } else { - huntAndDestroy = &(*huntAndDestroy)->next; - } - } -} - -void restartFunction(LoadedFunction *fun) { - fun->next = allRunningFunctions; - allRunningFunctions = fun; -} - -void killSpeechTimers() { - LoadedFunction *thisFunction = allRunningFunctions; - - while (thisFunction) { - if (thisFunction->freezerLevel == 0 && thisFunction->isSpeech - && thisFunction->timeLeft) { - thisFunction->timeLeft = 0; - thisFunction->isSpeech = false; - } - thisFunction = thisFunction->next; - } - - g_sludge->_speechMan->kill(); -} - -void completeTimers() { - LoadedFunction *thisFunction = allRunningFunctions; - - while (thisFunction) { - if (thisFunction->freezerLevel == 0) - thisFunction->timeLeft = 0; - thisFunction = thisFunction->next; - } -} - -void finishFunction(LoadedFunction *fun) { - int a; - - pauseFunction(fun); - if (fun->stack) - fatal(ERROR_NON_EMPTY_STACK); - delete[] fun->compiledLines; - for (a = 0; a < fun->numLocals; a++) - unlinkVar(fun->localVars[a]); - delete[] fun->localVars; - unlinkVar(fun->reg); - delete fun; - fun = NULL; -} - -void abortFunction(LoadedFunction *fun) { - int a; - - pauseFunction(fun); - while (fun->stack) - trimStack(fun->stack); - delete []fun->compiledLines; - for (a = 0; a < fun->numLocals; a++) - unlinkVar(fun->localVars[a]); - delete []fun->localVars; - unlinkVar(fun->reg); - if (fun->calledBy) - abortFunction(fun->calledBy); - delete fun; - fun = NULL; -} - -int cancelAFunction(int funcNum, LoadedFunction *myself, bool &killedMyself) { - int n = 0; - killedMyself = false; - - LoadedFunction *fun = allRunningFunctions; - while (fun) { - if (fun->originalNumber == funcNum) { - fun->cancelMe = true; - n++; - if (fun == myself) - killedMyself = true; - } - fun = fun->next; - } - return n; } -void freezeSubs() { - LoadedFunction *thisFunction = allRunningFunctions; - - while (thisFunction) { - if (thisFunction->unfreezable) { - //msgBox ("SLUDGE debugging bollocks!", "Trying to freeze an unfreezable function!"); - } else { - thisFunction->freezerLevel++; - } - thisFunction = thisFunction->next; - } -} - -void unfreezeSubs() { - LoadedFunction *thisFunction = allRunningFunctions; - - while (thisFunction) { - if (thisFunction->freezerLevel) - thisFunction->freezerLevel--; - thisFunction = thisFunction->next; - } -} - -bool continueFunction(LoadedFunction *fun) { - bool keepLooping = true; - bool advanceNow; - uint param; - sludgeCommand com; - - if (fun->cancelMe) { - abortFunction(fun); - return true; - } - - while (keepLooping) { - advanceNow = true; - debugC(1, kSludgeDebugStackMachine, "Executing command line %i : ", fun->runThisLine); - param = fun->compiledLines[fun->runThisLine].param; - com = fun->compiledLines[fun->runThisLine].theCommand; - - if (numBIFNames) { - setFatalInfo((fun->originalNumber < numUserFunc) ? allUserFunc[fun->originalNumber] : "Unknown user function", (com < numSludgeCommands) ? sludgeText[com] : ERROR_UNKNOWN_MCODE); - } - - switch (com) { - case SLU_RETURN: - if (fun->calledBy) { - LoadedFunction *returnTo = fun->calledBy; - if (fun->returnSomething) - copyVariable(fun->reg, returnTo->reg); - finishFunction(fun); - fun = returnTo; - restartFunction(fun); - } else { - finishFunction(fun); - advanceNow = false; // So we don't do anything else with "fun" - keepLooping = false; // So we drop out of the loop - } - break; - - case SLU_CALLIT: - switch (fun->reg.varType) { - case SVT_FUNC: - pauseFunction(fun); - if (numBIFNames) - setFatalInfo( - (fun->originalNumber < numUserFunc) ? - allUserFunc[fun->originalNumber] : - "Unknown user function", - (fun->reg.varData.intValue < numUserFunc) ? - allUserFunc[fun->reg.varData.intValue] : - "Unknown user function"); - - if (!startNewFunctionNum(fun->reg.varData.intValue, param, fun, - fun->stack)) - return false; - fun = allRunningFunctions; - advanceNow = false; // So we don't do anything else with "fun" - break; - - case SVT_BUILT: { - debugC(1, kSludgeDebugStackMachine, "Built-in init value: %i", - fun->reg.varData.intValue); - BuiltReturn br = callBuiltIn(fun->reg.varData.intValue, param, - fun); - - switch (br) { - case BR_ERROR: - return fatal( - "Unknown error. This shouldn't happen. Please notify the SLUDGE developers."); - - case BR_PAUSE: - pauseFunction(fun); - // fall through - - case BR_KEEP_AND_PAUSE: - keepLooping = false; - break; - - case BR_ALREADY_GONE: - keepLooping = false; - advanceNow = false; - break; - - case BR_CALLAFUNC: { - int i = fun->reg.varData.intValue; - setVariable(fun->reg, SVT_INT, 1); - pauseFunction(fun); - if (numBIFNames) - setFatalInfo( - (fun->originalNumber < numUserFunc) ? - allUserFunc[fun->originalNumber] : - "Unknown user function", - (i < numUserFunc) ? - allUserFunc[i] : - "Unknown user function"); - if (!startNewFunctionNum(i, 0, fun, noStack, false)) - return false; - fun = allRunningFunctions; - advanceNow = false; // So we don't do anything else with "fun" - } - break; - - default: - break; - } - } - break; - - default: - return fatal(ERROR_CALL_NONFUNCTION); - } - break; - - // These all grab things and shove 'em into the register - - case SLU_LOAD_NULL: - setVariable(fun->reg, SVT_NULL, 0); - break; - - case SLU_LOAD_FILE: - setVariable(fun->reg, SVT_FILE, param); - break; - - case SLU_LOAD_VALUE: - setVariable(fun->reg, SVT_INT, param); - break; - - case SLU_LOAD_LOCAL: - if (!copyVariable(fun->localVars[param], fun->reg)) - return false; - break; - - case SLU_AND: - setVariable(fun->reg, SVT_INT, - getBoolean(fun->reg) && getBoolean(fun->stack->thisVar)); - trimStack(fun->stack); - break; - - case SLU_OR: - setVariable(fun->reg, SVT_INT, - getBoolean(fun->reg) || getBoolean(fun->stack->thisVar)); - trimStack(fun->stack); - break; - - case SLU_LOAD_FUNC: - setVariable(fun->reg, SVT_FUNC, param); - break; - - case SLU_LOAD_BUILT: - setVariable(fun->reg, SVT_BUILT, param); - break; - - case SLU_LOAD_OBJTYPE: - setVariable(fun->reg, SVT_OBJTYPE, param); - break; - - case SLU_UNREG: - if (dialogValue != 1) - fatal(ERROR_HACKER); - break; - - case SLU_LOAD_STRING: - if (!loadStringToVar(fun->reg, param)) { - return false; - } - break; - - case SLU_INDEXGET: - case SLU_INCREMENT_INDEX: - case SLU_DECREMENT_INDEX: - switch (fun->stack->thisVar.varType) { - case SVT_NULL: - if (com == SLU_INDEXGET) { - setVariable(fun->reg, SVT_NULL, 0); - trimStack(fun->stack); - } else { - return fatal(ERROR_INCDEC_UNKNOWN); - } - break; - - case SVT_FASTARRAY: - case SVT_STACK: - if (fun->stack->thisVar.varData.theStack->first == NULL) { - return fatal(ERROR_INDEX_EMPTY); - } else { - int ii; - if (!getValueType(ii, SVT_INT, fun->reg)) - return false; - Variable *grab = - (fun->stack->thisVar.varType == SVT_FASTARRAY) ? - fastArrayGetByIndex( - fun->stack->thisVar.varData.fastArray, - ii) : - stackGetByIndex( - fun->stack->thisVar.varData.theStack->first, - ii); - - trimStack(fun->stack); - - if (!grab) { - setVariable(fun->reg, SVT_NULL, 0); - } else { - int kk; - switch (com) { - case SLU_INCREMENT_INDEX: - if (!getValueType(kk, SVT_INT, *grab)) - return false; - setVariable(fun->reg, SVT_INT, kk); - grab->varData.intValue = kk + 1; - break; - - case SLU_DECREMENT_INDEX: - if (!getValueType(kk, SVT_INT, *grab)) - return false; - setVariable(fun->reg, SVT_INT, kk); - grab->varData.intValue = kk - 1; - break; - - default: - if (!copyVariable(*grab, fun->reg)) - return false; - } - } - } - break; - - default: - return fatal(ERROR_INDEX_NONSTACK); - } - break; - - case SLU_INDEXSET: - switch (fun->stack->thisVar.varType) { - case SVT_STACK: - if (fun->stack->thisVar.varData.theStack->first == NULL) { - return fatal(ERROR_INDEX_EMPTY); - } else { - int ii; - if (!getValueType(ii, SVT_INT, fun->reg)) - return false; - if (!stackSetByIndex( - fun->stack->thisVar.varData.theStack->first, ii, - fun->stack->next->thisVar)) { - return false; - } - trimStack(fun->stack); - trimStack(fun->stack); - } - break; - - case SVT_FASTARRAY: { - int ii; - if (!getValueType(ii, SVT_INT, fun->reg)) - return false; - Variable *v = fastArrayGetByIndex( - fun->stack->thisVar.varData.fastArray, ii); - if (v == NULL) - return fatal("Not within bounds of fast array."); - if (!copyVariable(fun->stack->next->thisVar, *v)) - return false; - trimStack(fun->stack); - trimStack(fun->stack); - } - break; - - default: - return fatal(ERROR_INDEX_NONSTACK); - } - break; - - // What can we do with the register? Well, we can copy it into a local - // variable, a global or onto the stack... - - case SLU_INCREMENT_LOCAL: { - int ii; - if (!getValueType(ii, SVT_INT, fun->localVars[param])) - return false; - setVariable(fun->reg, SVT_INT, ii); - setVariable(fun->localVars[param], SVT_INT, ii + 1); - } - break; - - case SLU_INCREMENT_GLOBAL: { - int ii; - if (!getValueType(ii, SVT_INT, globalVars[param])) - return false; - setVariable(fun->reg, SVT_INT, ii); - setVariable(globalVars[param], SVT_INT, ii + 1); - } - break; - - case SLU_DECREMENT_LOCAL: { - int ii; - if (!getValueType(ii, SVT_INT, fun->localVars[param])) - return false; - setVariable(fun->reg, SVT_INT, ii); - setVariable(fun->localVars[param], SVT_INT, ii - 1); - } - break; - - case SLU_DECREMENT_GLOBAL: { - int ii; - if (!getValueType(ii, SVT_INT, globalVars[param])) - return false; - setVariable(fun->reg, SVT_INT, ii); - setVariable(globalVars[param], SVT_INT, ii - 1); - } - break; - - case SLU_SET_LOCAL: - if (!copyVariable(fun->reg, fun->localVars[param])) - return false; - break; - - case SLU_SET_GLOBAL: -// newDebug (" Copying TO global variable", param); -// newDebug (" Global type at the moment", globalVars[param].varType); - if (!copyVariable(fun->reg, globalVars[param])) - return false; -// newDebug (" New type", globalVars[param].varType); - break; - - case SLU_LOAD_GLOBAL: -// newDebug (" Copying FROM global variable", param); -// newDebug (" Global type at the moment", globalVars[param].varType); - if (!copyVariable(globalVars[param], fun->reg)) - return false; - break; - - case SLU_STACK_PUSH: - if (!addVarToStack(fun->reg, fun->stack)) - return false; - break; - - case SLU_QUICK_PUSH: - if (!addVarToStackQuick(fun->reg, fun->stack)) - return false; - break; - - case SLU_NOT: - setVariable(fun->reg, SVT_INT, !getBoolean(fun->reg)); - break; - - case SLU_BR_ZERO: - if (!getBoolean(fun->reg)) { - advanceNow = false; - fun->runThisLine = param; - } - break; - - case SLU_BRANCH: - advanceNow = false; - fun->runThisLine = param; - break; - - case SLU_NEGATIVE: { - int i; - if (!getValueType(i, SVT_INT, fun->reg)) - return false; - setVariable(fun->reg, SVT_INT, -i); - } - break; - - // All these things rely on there being somet' on the stack - - case SLU_MULT: - case SLU_PLUS: - case SLU_MINUS: - case SLU_MODULUS: - case SLU_DIVIDE: - case SLU_EQUALS: - case SLU_NOT_EQ: - case SLU_LESSTHAN: - case SLU_MORETHAN: - case SLU_LESS_EQUAL: - case SLU_MORE_EQUAL: - if (fun->stack) { - int firstValue, secondValue; - - switch (com) { - case SLU_PLUS: - addVariablesInSecond(fun->stack->thisVar, fun->reg); - trimStack(fun->stack); - break; - - case SLU_EQUALS: - compareVariablesInSecond(fun->stack->thisVar, fun->reg); - trimStack(fun->stack); - break; - - case SLU_NOT_EQ: - compareVariablesInSecond(fun->stack->thisVar, fun->reg); - trimStack(fun->stack); - fun->reg.varData.intValue = !fun->reg.varData.intValue; - break; - - default: - if (!getValueType(firstValue, SVT_INT, fun->stack->thisVar)) - return false; - if (!getValueType(secondValue, SVT_INT, fun->reg)) - return false; - trimStack(fun->stack); - - switch (com) { - case SLU_MULT: - setVariable(fun->reg, SVT_INT, - firstValue * secondValue); - break; - - case SLU_MINUS: - setVariable(fun->reg, SVT_INT, - firstValue - secondValue); - break; - - case SLU_MODULUS: - setVariable(fun->reg, SVT_INT, - firstValue % secondValue); - break; - - case SLU_DIVIDE: - setVariable(fun->reg, SVT_INT, - firstValue / secondValue); - break; - - case SLU_LESSTHAN: - setVariable(fun->reg, SVT_INT, - firstValue < secondValue); - break; - - case SLU_MORETHAN: - setVariable(fun->reg, SVT_INT, - firstValue > secondValue); - break; - - case SLU_LESS_EQUAL: - setVariable(fun->reg, SVT_INT, - firstValue <= secondValue); - break; - - case SLU_MORE_EQUAL: - setVariable(fun->reg, SVT_INT, - firstValue >= secondValue); - break; - - default: - break; - } - } - } else { - return fatal(ERROR_NOSTACK); - } - break; - - default: - return fatal(ERROR_UNKNOWN_CODE); - } - - if (advanceNow) - fun->runThisLine++; - - } - return true; -} - -bool runSludge() { - - LoadedFunction *thisFunction = allRunningFunctions; - LoadedFunction *nextFunction; - - while (thisFunction) { - nextFunction = thisFunction->next; - - if (!thisFunction->freezerLevel) { - if (thisFunction->timeLeft) { - if (thisFunction->timeLeft < 0) { - if (!g_sludge->_soundMan->stillPlayingSound( - g_sludge->_speechMan->getLastSpeechSound())) { - thisFunction->timeLeft = 0; - } - } else if (!--(thisFunction->timeLeft)) { - } - } else { - if (thisFunction->isSpeech) { - thisFunction->isSpeech = false; - g_sludge->_speechMan->kill(); - } - if (!continueFunction(thisFunction)) - return false; - } - } - - thisFunction = nextFunction; - } - - if (!g_sludge->loadNow.empty()) { - if (g_sludge->loadNow[0] == ':') { - saveGame(g_sludge->loadNow.c_str() + 1); - setVariable(saverFunc->reg, SVT_INT, 1); - } else { - if (!loadGame(g_sludge->loadNow)) - return false; - } - g_sludge->loadNow.clear(); - } - - return true; -} - -void killAllFunctions() { - while (allRunningFunctions) - finishFunction(allRunningFunctions); -} - -bool loadFunctionCode(LoadedFunction *newFunc) { - uint numLines, numLinesRead; - - if (!g_sludge->_resMan->openSubSlice(newFunc->originalNumber)) - return false; - - debugC(3, kSludgeDebugDataLoad, "Load function code"); - - Common::SeekableReadStream *readStream = g_sludge->_resMan->getData(); - newFunc->unfreezable = readStream->readByte(); - numLines = readStream->readUint16BE(); - debugC(3, kSludgeDebugDataLoad, "numLines: %i", numLines); - newFunc->numArgs = readStream->readUint16BE(); - debugC(3, kSludgeDebugDataLoad, "numArgs: %i", newFunc->numArgs); - newFunc->numLocals = readStream->readUint16BE(); - debugC(3, kSludgeDebugDataLoad, "numLocals: %i", newFunc->numLocals); - newFunc->compiledLines = new LineOfCode[numLines]; - if (!checkNew(newFunc->compiledLines)) - return false; - - for (numLinesRead = 0; numLinesRead < numLines; numLinesRead++) { - newFunc->compiledLines[numLinesRead].theCommand = (sludgeCommand)readStream->readByte(); - newFunc->compiledLines[numLinesRead].param = readStream->readUint16BE(); - debugC(3, kSludgeDebugDataLoad, "command line %i: %i", numLinesRead, - newFunc->compiledLines[numLinesRead].theCommand); - } - g_sludge->_resMan->finishAccess(); - - // Now we need to reserve memory for the local variables - newFunc->localVars = new Variable[newFunc->numLocals]; - if (!checkNew(newFunc->localVars)) - return false; - for (int a = 0; a < newFunc->numLocals; a++) { - initVarNew(newFunc->localVars[a]); - } - - return true; -} - -int startNewFunctionNum(uint funcNum, uint numParamsExpected, - LoadedFunction *calledBy, VariableStack *&vStack, bool returnSommet) { - LoadedFunction *newFunc = new LoadedFunction; - checkNew(newFunc); - newFunc->originalNumber = funcNum; - - loadFunctionCode(newFunc); - - if (newFunc->numArgs != (int) numParamsExpected) - return fatal("Wrong number of parameters!"); - if (newFunc->numArgs > newFunc->numLocals) - return fatal("More arguments than local Variable space!"); - - // Now, lets copy the parameters from the calling function's stack... - - while (numParamsExpected) { - numParamsExpected--; - if (vStack == NULL) - return fatal( - "Corrupted file!The stack's empty and there were still parameters expected"); - copyVariable(vStack->thisVar, newFunc->localVars[numParamsExpected]); - trimStack(vStack); - } - - newFunc->cancelMe = false; - newFunc->timeLeft = 0; - newFunc->returnSomething = returnSommet; - newFunc->calledBy = calledBy; - newFunc->stack = NULL; - newFunc->freezerLevel = 0; - newFunc->runThisLine = 0; - newFunc->isSpeech = 0; - initVarNew(newFunc->reg); - - restartFunction(newFunc); - return 1; -} - -int lastFramesPerSecond = -1; -int thisFramesPerSecond = -1; - } // End of namespace Sludge diff --git a/engines/sludge/sludger.h b/engines/sludge/sludger.h index 8f554f83cb..8efdfa62f2 100644 --- a/engines/sludge/sludger.h +++ b/engines/sludge/sludger.h @@ -22,12 +22,7 @@ #ifndef SLUDGER_H #define SLUDGER_H -#include "common/file.h" - #include "sludge/allfiles.h" -#include "sludge/variable.h" -#include "sludge/csludge.h" -#include "sludge/language.h" namespace Sludge { @@ -36,52 +31,15 @@ typedef struct _FILETIME { uint32 dwHighDateTime; } FILETIME; -struct Variable; -struct VariableStack; - -struct LineOfCode { - sludgeCommand theCommand; - int32 param; -}; - -struct LoadedFunction { - int originalNumber; - LineOfCode *compiledLines; - int numLocals, timeLeft, numArgs; - Variable *localVars; - VariableStack *stack; - Variable reg; - uint runThisLine; - LoadedFunction *calledBy; - LoadedFunction *next; - bool returnSomething, isSpeech, unfreezable, cancelMe; - byte freezerLevel; -}; - bool initSludge(const Common::String &); -bool runSludge(); - void initSludge(); void killSludge(); void displayBase(); void sludgeDisplay(); -int startNewFunctionNum(uint, uint, LoadedFunction *, VariableStack*&, bool = true); -bool handleInput(); -void restartFunction(LoadedFunction *fun); -bool loadFunctionCode(LoadedFunction *newFunc); -void killAllFunctions(); -void finishFunction(LoadedFunction *fun); -void abortFunction(LoadedFunction *fun); Common::File *openAndVerify(const Common::String &filename, char extra1, char extra2, const char *er, int &fileVersion); -void freezeSubs(); -void unfreezeSubs(); -void completeTimers(); -void killSpeechTimers(); -int cancelAFunction(int funcNum, LoadedFunction *myself, bool &killedMyself); - } // End of namespace Sludge #endif diff --git a/engines/sludge/sound.cpp b/engines/sludge/sound.cpp index 306fd49b4e..25b239c191 100644 --- a/engines/sludge/sound.cpp +++ b/engines/sludge/sound.cpp @@ -402,7 +402,7 @@ bool SoundManager::getSoundCacheStack(StackHandler *sH) { for (int a = 0; a < MAX_SAMPLES; a++) { if (_soundCache[a].fileLoaded != -1) { - setVariable(newFileHandle, SVT_FILE, _soundCache[a].fileLoaded); + newFileHandle.setVariable(SVT_FILE, _soundCache[a].fileLoaded); if (!addVarToStackQuick(newFileHandle, sH->first)) return false; if (sH->last == NULL) diff --git a/engines/sludge/speech.cpp b/engines/sludge/speech.cpp index 1d342a1b65..9c02eda5a6 100644 --- a/engines/sludge/speech.cpp +++ b/engines/sludge/speech.cpp @@ -45,7 +45,7 @@ void SpeechManager::init() { _speech = new SpeechStruct; if (checkNew(_speech)) { _speech->currentTalker = NULL; - _speech->allSpeech = NULL; + _speech->allSpeech.clear(); _speech->speechY = 0; _speech->lastFile = -1; } @@ -61,20 +61,20 @@ void SpeechManager::kill() { } if (_speech->currentTalker) { - makeSilent(*(_speech->currentTalker)); - _speech->currentTalker = NULL; + _speech->currentTalker->makeSilent(); + _speech->currentTalker = nullptr; } - SpeechLine *killMe; - while (_speech->allSpeech) { - killMe = _speech->allSpeech; - _speech->allSpeech = _speech->allSpeech->next; + for (SpeechLineList::iterator it = _speech->allSpeech.begin(); it != _speech->allSpeech.end(); ++it) { + SpeechLine *killMe = *it; delete killMe; + killMe = nullptr; } + _speech->allSpeech.clear(); } void SpeechManager::setObjFontColour(ObjectType *t) { - setFontColour(_speech->talkCol, t->r, t->g, t->b); + _speech->talkCol.setColor(t->r, t->g, t->b); } void SpeechManager::addSpeechLine(const Common::String &theLine, int x, int &offset) { @@ -82,14 +82,16 @@ void SpeechManager::addSpeechLine(const Common::String &theLine, int x, int &off int halfWidth = (g_sludge->_txtMan->stringWidth(theLine) >> 1) / cameraZoom; int xx1 = x - (halfWidth); int xx2 = x + (halfWidth); + + // Create new speech line SpeechLine *newLine = new SpeechLine; checkNew(newLine); - - newLine->next = _speech->allSpeech; newLine->textLine.clear(); newLine->textLine = theLine; newLine->x = xx1; - _speech->allSpeech = newLine; + _speech->allSpeech.push_front(newLine); + + // Calculate offset if ((xx1 < 5) && (offset < (5 - xx1))) { offset = 5 - xx1; } else if ((xx2 >= ((float) g_system->getWidth() / cameraZoom) - 5) @@ -99,7 +101,7 @@ void SpeechManager::addSpeechLine(const Common::String &theLine, int x, int &off } int SpeechManager::isThereAnySpeechGoingOn() { - return _speech->allSpeech ? _speech->lookWhosTalking : -1; + return _speech->allSpeech.empty() ? -1 : _speech->lookWhosTalking; } int SpeechManager::getLastSpeechSound() { @@ -158,10 +160,8 @@ int SpeechManager::wrapSpeechXY(const Common::String &theText, int x, int y, int + (float) (g_system->getHeight() - fontHeight / 3) / cameraZoom; if (offset) { - SpeechLine *viewLine = _speech->allSpeech; - while (viewLine) { - viewLine->x += offset; - viewLine = viewLine->next; + for (SpeechLineList::iterator it = _speech->allSpeech.begin(); it != _speech->allSpeech.end(); ++it) { + (*it)->x += offset; } } return speechTime; @@ -176,7 +176,7 @@ int SpeechManager::wrapSpeechPerson(const Common::String &theText, OnScreenPerso - thePerson.thisType->speechGap, thePerson.thisType->wrapSpeech, sampleFile); if (animPerson) { - makeTalker(thePerson); + thePerson.makeTalker(); _speech->currentTalker = &thePerson; } return i; @@ -188,12 +188,12 @@ int SpeechManager::wrapSpeech(const Common::String &theText, int objT, int sampl int cameraY = g_sludge->_gfxMan->getCamY(); _speech->lookWhosTalking = objT; - OnScreenPerson *thisPerson = findPerson(objT); + OnScreenPerson *thisPerson = g_sludge->_peopleMan->findPerson(objT); if (thisPerson) { setObjFontColour(thisPerson->thisType); i = wrapSpeechPerson(theText, *thisPerson, sampleFile, animPerson); } else { - ScreenRegion *thisRegion = getRegionForObject(objT); + ScreenRegion *thisRegion = g_sludge->_regionMan->getRegionForObject(objT); if (thisRegion) { setObjFontColour(thisRegion->thisType); i = wrapSpeechXY(theText, @@ -214,19 +214,14 @@ void SpeechManager::display() { float cameraZoom = g_sludge->_gfxMan->getCamZoom(); int fontHeight = g_sludge->_txtMan->getFontHeight(); int viewY = _speech->speechY; - SpeechLine *viewLine = _speech->allSpeech; - while (viewLine) { - g_sludge->_txtMan->pasteString(viewLine->textLine, viewLine->x, viewY, _speech->talkCol); + for (SpeechLineList::iterator it = _speech->allSpeech.begin(); it != _speech->allSpeech.end(); ++it) { + g_sludge->_txtMan->pasteString((*it)->textLine, (*it)->x, viewY, _speech->talkCol); viewY -= fontHeight / cameraZoom; - viewLine = viewLine->next; } } void SpeechManager::save(Common::WriteStream *stream) { stream->writeByte(_speechMode); - - SpeechLine *viewLine = _speech->allSpeech; - stream->writeByte(_speech->talkCol.originalRed); stream->writeByte(_speech->talkCol.originalGreen); stream->writeByte(_speech->talkCol.originalBlue); @@ -246,11 +241,10 @@ void SpeechManager::save(Common::WriteStream *stream) { } // Write what's being said - while (viewLine) { + for (SpeechLineList::iterator it = _speech->allSpeech.begin(); it != _speech->allSpeech.end(); ++it) { stream->writeByte(1); - writeString(viewLine->textLine, stream); - stream->writeUint16BE(viewLine->x); - viewLine = viewLine->next; + writeString((*it)->textLine, stream); + stream->writeUint16BE((*it)->x); } stream->writeByte(0); } @@ -264,8 +258,7 @@ bool SpeechManager::load(Common::SeekableReadStream *stream) { byte r = stream->readByte(); byte g = stream->readByte(); byte b = stream->readByte(); - setFontColour(_speech->talkCol, r, g, b); - + _speech->talkCol.setColor(r, g, b); _speechSpeed = stream->readFloatLE(); // Read y co-ordinate @@ -275,24 +268,20 @@ bool SpeechManager::load(Common::SeekableReadStream *stream) { _speech->lookWhosTalking = stream->readUint16BE(); if (stream->readByte()) { - _speech->currentTalker = findPerson(stream->readUint16BE()); + _speech->currentTalker = g_sludge->_peopleMan->findPerson(stream->readUint16BE()); } else { _speech->currentTalker = NULL; } // Read what's being said - SpeechLine **viewLine = &_speech->allSpeech; - SpeechLine *newOne; _speech->lastFile = -1; while (stream->readByte()) { - newOne = new SpeechLine; + SpeechLine *newOne = new SpeechLine; if (!checkNew(newOne)) return false; newOne->textLine = readString(stream); newOne->x = stream->readUint16BE(); - newOne->next = NULL; - (*viewLine) = newOne; - viewLine = &(newOne->next); + _speech->allSpeech.push_back(newOne); } return true; } diff --git a/engines/sludge/speech.h b/engines/sludge/speech.h index 98b6035cb0..776a9f5dea 100644 --- a/engines/sludge/speech.h +++ b/engines/sludge/speech.h @@ -30,13 +30,14 @@ struct ObjectType; struct SpeechLine { Common::String textLine; - SpeechLine *next; int x; }; +typedef Common::List<SpeechLine *> SpeechLineList; + struct SpeechStruct { OnScreenPerson *currentTalker; - SpeechLine *allSpeech; + SpeechLineList allSpeech; int speechY, lastFile, lookWhosTalking; SpritePalette talkCol; }; @@ -60,6 +61,7 @@ public: void setObjFontColour(ObjectType *t); void setSpeechSpeed(float speed) { _speechSpeed = speed; } float getSpeechSpeed() { return _speechSpeed; } + void setSpeechMode(int speechMode) { _speechMode = speechMode; } // load & save void save(Common::WriteStream *stream); diff --git a/engines/sludge/sprites.h b/engines/sludge/sprites.h index e138c6f14f..e18d16e5df 100644 --- a/engines/sludge/sprites.h +++ b/engines/sludge/sprites.h @@ -42,15 +42,8 @@ public: byte originalRed, originalGreen, originalBlue, total; SpritePalette() { init(); } - ~SpritePalette() { kill(); } - void reset() { - kill(); - init(); - } - -private: void init() { pal = nullptr; r = g = b = nullptr; @@ -59,15 +52,29 @@ private: } void kill() { - if (pal) + if (pal) { delete[] pal; - if (r) + pal = nullptr; + } + if (r) { delete[] r; - if (g) + r = nullptr; + } + if (g) { delete[] g; - if (b) + g = nullptr; + } + if (b) { delete[] b; + b = nullptr; + } } + + void setColor(byte red, byte green, byte blue) { + originalRed = red; + originalGreen = green; + originalBlue = blue; + } }; struct SpriteBank { diff --git a/engines/sludge/statusba.cpp b/engines/sludge/statusba.cpp index 1aa24971f1..fc0f065ea9 100644 --- a/engines/sludge/statusba.cpp +++ b/engines/sludge/statusba.cpp @@ -37,8 +37,8 @@ namespace Sludge { SpritePalette verbLinePalette; SpritePalette litVerbLinePalette; -StatusStuff mainStatus; -StatusStuff *nowStatus = & mainStatus; +StatusStuff mainStatus; +StatusStuff *nowStatus = & mainStatus; void setLitStatus(int i) { nowStatus->litStatus = i; @@ -114,14 +114,14 @@ void drawStatusBar() { } void statusBarColour(byte r, byte g, byte b) { - setFontColour(verbLinePalette, r, g, b); + verbLinePalette.setColor(r, g, b); nowStatus->statusR = r; nowStatus->statusG = g; nowStatus->statusB = b; } void statusBarLitColour(byte r, byte g, byte b) { - setFontColour(litVerbLinePalette, r, g, b); + litVerbLinePalette.setColor(r, g, b); nowStatus->statusLR = r; nowStatus->statusLG = g; nowStatus->statusLB = b; @@ -144,7 +144,7 @@ StatusStuff *copyStatusBarStuff(StatusStuff *here) { here->litStatus = -1; here->firstStatusBar = NULL; - StatusStuff *old = nowStatus; + StatusStuff *old = nowStatus; nowStatus = here; return old; @@ -152,8 +152,8 @@ StatusStuff *copyStatusBarStuff(StatusStuff *here) { void restoreBarStuff(StatusStuff *here) { delete nowStatus; - setFontColour(verbLinePalette, here->statusR, here->statusG, here->statusB); - setFontColour(litVerbLinePalette, here->statusLR, here->statusLG, here->statusLB); + verbLinePalette.setColor((byte)here->statusR, (byte)here->statusG, (byte)here->statusB); + litVerbLinePalette.setColor((byte)here->statusLR, (byte)here->statusLG, (byte)here->statusLB); nowStatus = here; } @@ -215,8 +215,8 @@ bool loadStatusBars(Common::SeekableReadStream *stream) { nowStatus->statusLG = stream->readByte(); nowStatus->statusLB = stream->readByte(); - setFontColour(verbLinePalette, nowStatus->statusR, nowStatus->statusG, nowStatus->statusB); - setFontColour(litVerbLinePalette, nowStatus->statusLR, nowStatus->statusLG, nowStatus->statusLB); + verbLinePalette.setColor((byte)nowStatus->statusR, (byte)nowStatus->statusG, (byte)nowStatus->statusB); + litVerbLinePalette.setColor((byte)nowStatus->statusLR, (byte)nowStatus->statusLG, (byte)nowStatus->statusLB); // Read what's being said StatusBar **viewLine = & (nowStatus->firstStatusBar); StatusBar *newOne; diff --git a/engines/sludge/thumbnail.cpp b/engines/sludge/thumbnail.cpp index 2c7e007f71..dcbcd91db3 100644 --- a/engines/sludge/thumbnail.cpp +++ b/engines/sludge/thumbnail.cpp @@ -35,14 +35,23 @@ namespace Sludge { -int thumbWidth = 0, thumbHeight = 0; +bool GraphicsManager::setThumbnailSize(int thumbWidth, int thumbHeight) +{ + if (checkSizeValide(thumbWidth, thumbHeight)) + { + _thumbWidth = thumbWidth; + _thumbHeight = thumbHeight; + return true; + } + return false; +} bool GraphicsManager::saveThumbnail(Common::WriteStream *stream) { - stream->writeUint32LE(thumbWidth); - stream->writeUint32LE(thumbHeight); + stream->writeUint32LE(_thumbWidth); + stream->writeUint32LE(_thumbHeight); - if (thumbWidth && thumbHeight) { + if (_thumbWidth && _thumbHeight) { if (!freeze()) return false; @@ -117,12 +126,12 @@ void GraphicsManager::showThumbnail(const Common::String &filename, int atX, int } bool GraphicsManager::skipThumbnail(Common::SeekableReadStream *stream) { - thumbWidth = stream->readUint32LE(); - thumbHeight = stream->readUint32LE(); + _thumbWidth = stream->readUint32LE(); + _thumbHeight = stream->readUint32LE(); // Load image Graphics::Surface tmp; - if (thumbWidth & thumbHeight) { + if (_thumbWidth & _thumbHeight) { if (!ImgLoader::loadPNGImage(stream, &tmp)) return false; else diff --git a/engines/sludge/timing.cpp b/engines/sludge/timing.cpp index 2e3865498a..c291f57a6b 100644 --- a/engines/sludge/timing.cpp +++ b/engines/sludge/timing.cpp @@ -25,31 +25,63 @@ namespace Sludge { +Timer::Timer(){ + reset(); +} + +void Timer::reset(void) { + _desiredFPS = 300; + _startTime = 0; + _endTime = 0; + _desiredFrameTime = 0; + _addNextTime = 0; + + // FPS stats + _lastFPS = -1; + _thisFPS = -1; + _lastSeconds = 0; +} + void Timer::init(void) { - _desired_frame_time = 1000 / _desiredfps; - _starttime = g_system->getMillis(); + _desiredFrameTime = 1000 / _desiredFPS; + _startTime = g_system->getMillis(); } void Timer::initSpecial(int t) { - _desired_frame_time = 1000 / t; - _starttime = g_system->getMillis(); + _desiredFrameTime = 1000 / t; + _startTime = g_system->getMillis(); +} + +void Timer::updateFpsStats() { + uint32 currentSeconds = g_system->getMillis() / 1000; + if (_lastSeconds != currentSeconds) { + _lastSeconds = currentSeconds; + _lastFPS = _thisFPS; + _thisFPS = 1; + } else { + ++_thisFPS; + } } void Timer::waitFrame(void) { - static uint32 addNextTime = 0; uint32 timetaken; for (;;) { - _endtime = g_system->getMillis(); - timetaken = addNextTime + _endtime - _starttime; - if (timetaken >= _desired_frame_time) break; + _endTime = g_system->getMillis(); + timetaken = _addNextTime + _endTime - _startTime; + if (timetaken >= _desiredFrameTime) + break; g_system->delayMillis(1); } - addNextTime = timetaken - _desired_frame_time; - if (addNextTime > _desired_frame_time) addNextTime = _desired_frame_time; + _addNextTime = timetaken - _desiredFrameTime; + if (_addNextTime > _desiredFrameTime) + _addNextTime = _desiredFrameTime; + + _startTime = _endTime; - _starttime = _endtime; + // Stats + updateFpsStats(); } } // End of namespace Sludge diff --git a/engines/sludge/timing.h b/engines/sludge/timing.h index 0d7ffece8d..e04ddf4732 100644 --- a/engines/sludge/timing.h +++ b/engines/sludge/timing.h @@ -25,18 +25,28 @@ namespace Sludge { class Timer { -private: - int _desiredfps; // desired frames per second - uint32 _starttime, _endtime; - uint32 _desired_frame_time; - public: - void setDesiredfps(int t) { _desiredfps = t; } + Timer(); + + void setDesiredFPS(int t) { _desiredFPS = t; } + void reset(void); void init(void); void initSpecial(int t); void waitFrame(void); - Timer():_desiredfps(300), _starttime(0), _endtime(0), _desired_frame_time(0){} + int getLastFps() const { return _lastFPS; } + +private: + int _desiredFPS; // desired frames per second + uint32 _startTime, _endTime; + uint32 _desiredFrameTime; + uint32 _addNextTime; + + // FPS stats + void updateFpsStats(); + int _lastFPS; + int _thisFPS; + uint32 _lastSeconds; }; } // End of namespace Sludge diff --git a/engines/sludge/transition.cpp b/engines/sludge/transition.cpp index 3a768cb5f4..35f650e8e4 100644 --- a/engines/sludge/transition.cpp +++ b/engines/sludge/transition.cpp @@ -22,15 +22,22 @@ #include "sludge/allfiles.h" #include "sludge/backdrop.h" +#include "sludge/graphics.h" #include "sludge/newfatal.h" namespace Sludge { -extern byte brightnessLevel; - extern float snapTexW, snapTexH; -byte fadeMode = 2; +void GraphicsManager::setBrightnessLevel(int brightnessLevel) +{ + if (brightnessLevel < 0) + _brightnessLevel = 0; + else if (brightnessLevel > 255) + _brightnessLevel = 255; + else + _brightnessLevel = brightnessLevel; +} //---------------------------------------------------- // PROPER BRIGHTNESS FADING @@ -142,7 +149,7 @@ void transitionSnapshotBox() { uint32 randbuffer[KK][2]; // history buffer int p1, p2; -void resetRandW() { +void GraphicsManager::resetRandW() { int32 seed = 12345; for (int i = 0; i < KK; i++) { @@ -373,8 +380,8 @@ void transitionBlinds() { //---------------------------------------------------- -void fixBrightness() { - switch (fadeMode) { +void GraphicsManager::fixBrightness() { + switch (_fadeMode) { case 0: transitionFader(); break; diff --git a/engines/sludge/variable.cpp b/engines/sludge/variable.cpp index 9cbb9f49f8..44381aa97f 100644 --- a/engines/sludge/variable.cpp +++ b/engines/sludge/variable.cpp @@ -38,34 +38,37 @@ const char *typeName[] = { "undefined", "number", "user function", "string", "built-in function", "file", "stack", "object type", "animation", "costume" }; -void unlinkVar(Variable &thisVar) { - switch (thisVar.varType) { +void Variable::unlinkVar() { + switch (varType) { case SVT_STRING: - delete []thisVar.varData.theString; - thisVar.varData.theString = NULL; + delete []varData.theString; + varData.theString = NULL; break; case SVT_STACK: - thisVar.varData.theStack->timesUsed--; - if (thisVar.varData.theStack->timesUsed <= 0) { - while (thisVar.varData.theStack->first) - trimStack(thisVar.varData.theStack->first); - delete thisVar.varData.theStack; - thisVar.varData.theStack = NULL; + varData.theStack->timesUsed--; + if (varData.theStack->timesUsed <= 0) { + while (varData.theStack->first) + trimStack(varData.theStack->first); + delete varData.theStack; + varData.theStack = NULL; } break; case SVT_FASTARRAY: - thisVar.varData.fastArray->timesUsed--; - if (thisVar.varData.theStack->timesUsed <= 0) { - delete thisVar.varData.fastArray->fastVariables; - delete[] thisVar.varData.fastArray; - thisVar.varData.fastArray = NULL; + varData.fastArray->timesUsed--; + if (varData.theStack->timesUsed <= 0) { + delete varData.fastArray->fastVariables; + delete[] varData.fastArray; + varData.fastArray = NULL; } break; case SVT_ANIM: - deleteAnim(thisVar.varData.animHandler); + if (varData.animHandler) { + delete varData.animHandler; + varData.animHandler = nullptr; + } break; default: @@ -73,39 +76,39 @@ void unlinkVar(Variable &thisVar) { } } -void setVariable(Variable &thisVar, VariableType vT, int value) { - unlinkVar(thisVar); - thisVar.varType = vT; - thisVar.varData.intValue = value; +void Variable::setVariable(VariableType vT, int value) { + unlinkVar(); + varType = vT; + varData.intValue = value; } -void newAnimationVariable(Variable &thisVar, PersonaAnimation *i) { - unlinkVar(thisVar); - thisVar.varType = SVT_ANIM; - thisVar.varData.animHandler = i; +void Variable::makeAnimationVariable(PersonaAnimation *i) { + unlinkVar(); + varType = SVT_ANIM; + varData.animHandler = i; } -PersonaAnimation *getAnimationFromVar(Variable &thisVar) { - if (thisVar.varType == SVT_ANIM) - return copyAnim(thisVar.varData.animHandler); +PersonaAnimation *Variable::getAnimationFromVar() { + if (varType == SVT_ANIM) + return new PersonaAnimation(varData.animHandler); - if (thisVar.varType == SVT_INT && thisVar.varData.intValue == 0) - return makeNullAnim(); + if (varType == SVT_INT && varData.intValue == 0) + return new PersonaAnimation(); - fatal("Expecting an animation variable; found Variable of type", typeName[thisVar.varType]); + fatal("Expecting an animation variable; found Variable of type", typeName[varType]); return NULL; } -void newCostumeVariable(Variable &thisVar, Persona *i) { - unlinkVar(thisVar); - thisVar.varType = SVT_COSTUME; - thisVar.varData.costumeHandler = i; +void Variable::makeCostumeVariable(Persona *i) { + unlinkVar(); + varType = SVT_COSTUME; + varData.costumeHandler = i; } -Persona *getCostumeFromVar(Variable &thisVar) { +Persona *Variable::getCostumeFromVar() { Persona *p = NULL; - switch (thisVar.varType) { + switch (varType) { case SVT_ANIM: p = new Persona; if (!checkNew(p)) @@ -116,24 +119,24 @@ Persona *getCostumeFromVar(Variable &thisVar) { return NULL; for (int iii = 0; iii < 3; iii++) - p->animation[iii] = copyAnim(thisVar.varData.animHandler); + p->animation[iii] = new PersonaAnimation(varData.animHandler); break; case SVT_COSTUME: - return thisVar.varData.costumeHandler; + return varData.costumeHandler; break; default: - fatal("Expecting an animation variable; found Variable of type", typeName[thisVar.varType]); + fatal("Expecting an animation variable; found Variable of type", typeName[varType]); } return p; } -int stackSize(const StackHandler *me) { +int StackHandler::getStackSize() const { int r = 0; - VariableStack *a = me->first; + VariableStack *a = first; while (a) { r++; a = a->next; @@ -141,8 +144,7 @@ int stackSize(const StackHandler *me) { return r; } -bool getSavedGamesStack(StackHandler *sH, const Common::String &ext) { - +bool StackHandler::getSavedGamesStack(const Common::String &ext) { // Make pattern uint len = ext.size(); Common::String pattern = "*"; @@ -157,30 +159,30 @@ bool getSavedGamesStack(StackHandler *sH, const Common::String &ext) { Common::StringArray::iterator it; for (it = sa.begin(); it != sa.end(); ++it) { (*it).erase((*it).size() - len, len); - makeTextVar(newName, (*it)); - if (!addVarToStack(newName, sH->first)) + newName.makeTextVar((*it)); + if (!addVarToStack(newName, first)) return false; - if (sH->last == NULL) - sH->last = sH->first; + if (last == NULL) + last = first; } return true; } -bool copyStack(const Variable &from, Variable &to) { - to.varType = SVT_STACK; - to.varData.theStack = new StackHandler; - if (!checkNew(to.varData.theStack)) +bool Variable::copyStack(const Variable &from) { + varType = SVT_STACK; + varData.theStack = new StackHandler; + if (!checkNew(varData.theStack)) return false; - to.varData.theStack->first = NULL; - to.varData.theStack->last = NULL; - to.varData.theStack->timesUsed = 1; + varData.theStack->first = NULL; + varData.theStack->last = NULL; + varData.theStack->timesUsed = 1; VariableStack *a = from.varData.theStack->first; while (a) { - addVarToStack(a->thisVar, to.varData.theStack->first); - if (to.varData.theStack->last == NULL) { - to.varData.theStack->last = to.varData.theStack->first; + addVarToStack(a->thisVar, varData.theStack->first); + if (varData.theStack->last == NULL) { + varData.theStack->last = varData.theStack->first; } a = a->next; } @@ -188,89 +190,78 @@ bool copyStack(const Variable &from, Variable &to) { return true; } -void addVariablesInSecond(Variable &var1, Variable &var2) { - if (var1.varType == SVT_INT && var2.varType == SVT_INT) { - var2.varData.intValue += var1.varData.intValue; +void Variable::addVariablesInSecond(const Variable &other) { + if (other.varType == SVT_INT && varType == SVT_INT) { + varData.intValue += other.varData.intValue; } else { - Common::String string1 = getTextFromAnyVar(var1); - Common::String string2 = getTextFromAnyVar(var2); + Common::String string1 = other.getTextFromAnyVar(); + Common::String string2 = getTextFromAnyVar(); - unlinkVar(var2); - var2.varData.theString = createCString(string1 + string2); - var2.varType = SVT_STRING; + unlinkVar(); + varData.theString = createCString(string1 + string2); + varType = SVT_STRING; } } -int compareVars(const Variable &var1, const Variable &var2) { +int Variable::compareVars(const Variable &other) const { int re = 0; - if (var1.varType == var2.varType) { - switch (var1.varType) { + if (other.varType == varType) { + switch (other.varType) { case SVT_NULL: re = 1; break; case SVT_COSTUME: - re = (var1.varData.costumeHandler == var2.varData.costumeHandler); + re = (other.varData.costumeHandler == varData.costumeHandler); break; case SVT_ANIM: - re = (var1.varData.animHandler == var2.varData.animHandler); + re = (other.varData.animHandler == varData.animHandler); break; case SVT_STRING: - re = (strcmp(var1.varData.theString, var2.varData.theString) == 0); + re = (strcmp(other.varData.theString, varData.theString) == 0); break; case SVT_STACK: - re = (var1.varData.theStack == var2.varData.theStack); + re = (other.varData.theStack == varData.theStack); break; default: - re = (var1.varData.intValue == var2.varData.intValue); + re = (other.varData.intValue == varData.intValue); } } return re; } -void compareVariablesInSecond(const Variable &var1, Variable &var2) { - setVariable(var2, SVT_INT, compareVars(var1, var2)); -} - -char *createCString(const Common::String &s) { - uint n = s.size() + 1; - char *res = new char[n]; - if (!checkNew(res)) { - fatal("createCString : Unable to copy String"); - return NULL; - } - memcpy(res, s.c_str(), n); - return res; +void Variable::compareVariablesInSecond(const Variable &other) { + setVariable(SVT_INT, compareVars(other)); } -void makeTextVar(Variable &thisVar, const Common::String &txt) { - unlinkVar(thisVar); - thisVar.varType = SVT_STRING; - thisVar.varData.theString = createCString(txt); +void Variable::makeTextVar(const Common::String &txt) { + unlinkVar(); + varType = SVT_STRING; + varData.theString = createCString(txt); } -bool loadStringToVar(Variable &thisVar, int value) { - makeTextVar(thisVar, g_sludge->_resMan->getNumberedString(value)); - return (bool)(thisVar.varData.theString != NULL); +bool Variable::loadStringToVar(int value) { + makeTextVar(g_sludge->_resMan->getNumberedString(value)); + return (bool)(varData.theString != NULL); } -Common::String getTextFromAnyVar(const Variable &from) { - switch (from.varType) { +Common::String Variable::getTextFromAnyVar() const { + switch (varType) { case SVT_STRING: - return from.varData.theString; + return varData.theString; case SVT_FASTARRAY: { Common::String builder = "FAST:"; Common::String builder2 = ""; Common::String grabText = ""; - for (int i = 0; i < from.varData.fastArray->size; i++) { + for (int i = 0; i < varData.fastArray->size; i++) { builder2 = builder + " "; - grabText = getTextFromAnyVar(from.varData.fastArray->fastVariables[i]); + grabText = varData.fastArray->fastVariables[i].getTextFromAnyVar(); builder.clear(); builder = builder2 + grabText; } @@ -282,11 +273,11 @@ Common::String getTextFromAnyVar(const Variable &from) { Common::String builder2 = ""; Common::String grabText = ""; - VariableStack *stacky = from.varData.theStack->first; + VariableStack *stacky = varData.theStack->first; while (stacky) { builder2 = builder + " "; - grabText = getTextFromAnyVar(stacky->thisVar); + grabText = stacky->thisVar.getTextFromAnyVar(); builder.clear(); builder = builder2 + grabText; stacky = stacky->next; @@ -295,16 +286,16 @@ Common::String getTextFromAnyVar(const Variable &from) { } case SVT_INT: { - Common::String buff = Common::String::format("%i", from.varData.intValue); + Common::String buff = Common::String::format("%i", varData.intValue); return buff; } case SVT_FILE: { - return resourceNameFromNum(from.varData.intValue); + return g_sludge->_resMan->resourceNameFromNum(varData.intValue); } case SVT_OBJTYPE: { - ObjectType *thisType = g_sludge->_objMan->findObjectType(from.varData.intValue); + ObjectType *thisType = g_sludge->_objMan->findObjectType(varData.intValue); if (thisType) return thisType->screenName; break; @@ -314,25 +305,25 @@ Common::String getTextFromAnyVar(const Variable &from) { break; } - return typeName[from.varType]; + return typeName[varType]; } -bool getBoolean(const Variable &from) { - switch (from.varType) { +bool Variable::getBoolean() const { + switch (varType) { case SVT_NULL: return false; case SVT_INT: - return (bool)(from.varData.intValue != 0); + return (bool)(varData.intValue != 0); case SVT_STACK: - return (bool)(from.varData.theStack->first != NULL); + return (bool)(varData.theStack->first != NULL); case SVT_STRING: - return (bool)(from.varData.theString[0] != 0); + return (bool)(varData.theString[0] != 0); case SVT_FASTARRAY: - return (bool)(from.varData.fastArray->size != 0); + return (bool)(varData.fastArray->size != 0); default: break; @@ -340,37 +331,37 @@ bool getBoolean(const Variable &from) { return true; } -bool copyMain(const Variable &from, Variable &to) { - to.varType = from.varType; - switch (to.varType) { +bool Variable::copyMain(const Variable &from) { + varType = from.varType; + switch (varType) { case SVT_INT: case SVT_FUNC: case SVT_BUILT: case SVT_FILE: case SVT_OBJTYPE: - to.varData.intValue = from.varData.intValue; + varData.intValue = from.varData.intValue; return true; case SVT_FASTARRAY: - to.varData.fastArray = from.varData.fastArray; - to.varData.fastArray->timesUsed++; + varData.fastArray = from.varData.fastArray; + varData.fastArray->timesUsed++; return true; case SVT_STRING: - to.varData.theString = createCString(from.varData.theString); - return to.varData.theString ? true : false; + varData.theString = createCString(from.varData.theString); + return varData.theString ? true : false; case SVT_STACK: - to.varData.theStack = from.varData.theStack; - to.varData.theStack->timesUsed++; + varData.theStack = from.varData.theStack; + varData.theStack->timesUsed++; return true; case SVT_COSTUME: - to.varData.costumeHandler = from.varData.costumeHandler; + varData.costumeHandler = from.varData.costumeHandler; return true; case SVT_ANIM: - to.varData.animHandler = copyAnim(from.varData.animHandler); + varData.animHandler = new PersonaAnimation(from.varData.animHandler); return true; case SVT_NULL: @@ -383,39 +374,36 @@ bool copyMain(const Variable &from, Variable &to) { return false; } -bool copyVariable(const Variable &from, Variable &to) { - unlinkVar(to); - return copyMain(from, to); +bool Variable::copyFrom(const Variable &from) { + unlinkVar(); + return copyMain(from); } -Variable *fastArrayGetByIndex(FastArrayHandler *vS, uint theIndex) { - if ((int)theIndex >= vS->size) +Variable *FastArrayHandler::fastArrayGetByIndex(uint theIndex) { + if ((int)theIndex >= size) return NULL; - return &vS->fastVariables[theIndex]; + return &fastVariables[theIndex]; } -bool makeFastArraySize(Variable &to, int size) { +bool Variable::makeFastArraySize(int size) { if (size < 0) return fatal("Can't create a fast array with a negative number of elements!"); - unlinkVar(to); - to.varType = SVT_FASTARRAY; - to.varData.fastArray = new FastArrayHandler; - if (!checkNew(to.varData.fastArray)) + unlinkVar(); + varType = SVT_FASTARRAY; + varData.fastArray = new FastArrayHandler; + if (!checkNew(varData.fastArray)) return false; - to.varData.fastArray->fastVariables = new Variable[size]; - if (!checkNew(to.varData.fastArray->fastVariables)) + varData.fastArray->fastVariables = new Variable[size]; + if (!checkNew(varData.fastArray->fastVariables)) return false; - for (int i = 0; i < size; i++) { - initVarNew(to.varData.fastArray->fastVariables[i]); - } - to.varData.fastArray->size = size; - to.varData.fastArray->timesUsed = 1; + varData.fastArray->size = size; + varData.fastArray->timesUsed = 1; return true; } -bool makeFastArrayFromStack(Variable &to, const StackHandler *stacky) { - int size = stackSize(stacky); - if (!makeFastArraySize(to, size)) +bool Variable::makeFastArrayFromStack(const StackHandler *stacky) { + int size = stacky->getStackSize(); + if (!makeFastArraySize(size)) return false; // Now let's fill up the new array @@ -423,7 +411,7 @@ bool makeFastArrayFromStack(Variable &to, const StackHandler *stacky) { VariableStack *allV = stacky->first; size = 0; while (allV) { - copyMain(allV->thisVar, to.varData.fastArray->fastVariables[size]); + varData.fastArray->fastVariables[size].copyMain(allV->thisVar); size++; allV = allV->next; } @@ -435,7 +423,7 @@ bool addVarToStack(const Variable &va, VariableStack *&thisStack) { if (!checkNew(newStack)) return false; - if (!copyMain(va, newStack->thisVar)) + if (!newStack->thisVar.copyMain(va)) return false; newStack->next = thisStack; thisStack = newStack; @@ -459,16 +447,18 @@ bool addVarToStackQuick(Variable &va, VariableStack *&thisStack) { return true; } -bool stackSetByIndex(VariableStack *vS, uint theIndex, const Variable &va) { +bool VariableStack::stackSetByIndex(uint theIndex, const Variable &va) { + VariableStack *vS = this; while (theIndex--) { vS = vS->next; if (!vS) return fatal("Index past end of stack."); } - return copyVariable(va, vS->thisVar); + return vS->thisVar.copyFrom(va); } -Variable *stackGetByIndex(VariableStack *vS, uint theIndex) { +Variable *VariableStack::stackGetByIndex(uint theIndex) { + VariableStack *vS = this; while (theIndex--) { vS = vS->next; if (!vS) { @@ -484,10 +474,10 @@ int deleteVarFromStack(const Variable &va, VariableStack *&thisStack, bool allOf int reply = 0; while (*huntVar) { - if (compareVars((*huntVar)->thisVar, va)) { + if (va.compareVars((*huntVar)->thisVar)) { killMe = *huntVar; *huntVar = killMe->next; - unlinkVar(killMe->thisVar); + killMe->thisVar.unlinkVar(); delete killMe; if (!allOfEm) return 1; @@ -501,28 +491,25 @@ int deleteVarFromStack(const Variable &va, VariableStack *&thisStack, bool allOf } // Would be a LOT better just to keep this up to date in the above function... ah well -VariableStack *stackFindLast(VariableStack *hunt) { - if (hunt == NULL) - return NULL; - +VariableStack *VariableStack::stackFindLast() { + VariableStack *hunt = this; while (hunt->next) hunt = hunt->next; return hunt; } -bool getValueType(int &toHere, VariableType vT, const Variable &v) { - //if (! v) return false; - if (v.varType != vT) { +bool Variable::getValueType(int &toHere, VariableType vT) const { + if (varType != vT) { Common::String e1 = "Can only perform specified operation on a value which is of type "; e1 += typeName[vT]; Common::String e2 = "... value supplied was of type "; - e2 += typeName[v.varType]; + e2 += typeName[varType]; fatal(e1, e2); return false; } - toHere = v.varData.intValue; + toHere = varData.intValue; return true; } @@ -533,8 +520,212 @@ void trimStack(VariableStack *&stack) { //debugC(2, kSludgeDebugStackMachine, "Variable %s was removed from stack", getTextFromAnyVar(killMe->thisVar)); // When calling this, we've ALWAYS checked that stack != NULL - unlinkVar(killMe->thisVar); + killMe->thisVar.unlinkVar(); delete killMe; } +//---------------------------------------------------------------------- +// Globals (so we know what's saved already and what's a reference +//---------------------------------------------------------------------- + +struct stackLibrary { + StackHandler *stack; + stackLibrary *next; +}; + +int stackLibTotal = 0; +stackLibrary *stackLib = NULL; + +//---------------------------------------------------------------------- +// For saving and loading stacks... +//---------------------------------------------------------------------- +void saveStack(VariableStack *vs, Common::WriteStream *stream) { + int elements = 0; + int a; + + VariableStack *search = vs; + while (search) { + elements++; + search = search->next; + } + + stream->writeUint16BE(elements); + search = vs; + for (a = 0; a < elements; a++) { + search->thisVar.save(stream); + search = search->next; + } +} + +VariableStack *loadStack(Common::SeekableReadStream *stream, VariableStack **last) { + int elements = stream->readUint16BE(); + int a; + VariableStack *first = NULL; + VariableStack **changeMe = &first; + + for (a = 0; a < elements; a++) { + VariableStack *nS = new VariableStack; + if (!checkNew(nS)) + return NULL; + nS->thisVar.load(stream); + if (last && a == elements - 1) { + *last = nS; + } + nS->next = NULL; + (*changeMe) = nS; + changeMe = &(nS->next); + } + + return first; +} + +bool saveStackRef(StackHandler *vs, Common::WriteStream *stream) { + stackLibrary *s = stackLib; + int a = 0; + while (s) { + if (s->stack == vs) { + stream->writeByte(1); + stream->writeUint16BE(stackLibTotal - a); + return true; + } + s = s->next; + a++; + } + stream->writeByte(0); + saveStack(vs->first, stream); + s = new stackLibrary; + stackLibTotal++; + if (!checkNew(s)) + return false; + s->next = stackLib; + s->stack = vs; + stackLib = s; + return true; +} + +void clearStackLib() { + stackLibrary *k; + while (stackLib) { + k = stackLib; + stackLib = stackLib->next; + delete k; + } + stackLibTotal = 0; +} + +StackHandler *getStackFromLibrary(int n) { + n = stackLibTotal - n; + while (n) { + stackLib = stackLib->next; + n--; + } + return stackLib->stack; +} + +StackHandler *loadStackRef(Common::SeekableReadStream *stream) { + StackHandler *nsh; + + if (stream->readByte()) { // It's one we've loaded already... + nsh = getStackFromLibrary(stream->readUint16BE()); + nsh->timesUsed++; + } else { + // Load the new stack + + nsh = new StackHandler; + if (!checkNew(nsh)) + return NULL; + nsh->last = NULL; + nsh->first = loadStack(stream, &nsh->last); + nsh->timesUsed = 1; + + // Add it to the library of loaded stacks + + stackLibrary *s = new stackLibrary; + if (!checkNew(s)) + return NULL; + s->stack = nsh; + s->next = stackLib; + stackLib = s; + stackLibTotal++; + } + return nsh; +} + +//---------------------------------------------------------------------- +// For saving and loading variables... +//---------------------------------------------------------------------- +bool Variable::save(Common::WriteStream *stream) { + stream->writeByte(varType); + switch (varType) { + case SVT_INT: + case SVT_FUNC: + case SVT_BUILT: + case SVT_FILE: + case SVT_OBJTYPE: + stream->writeUint32LE(varData.intValue); + return true; + + case SVT_STRING: + writeString(varData.theString, stream); + return true; + + case SVT_STACK: + return saveStackRef(varData.theStack, stream); + + case SVT_COSTUME: + varData.costumeHandler->save(stream); + return false; + + case SVT_ANIM: + varData.animHandler->save(stream); + return false; + + case SVT_NULL: + return false; + + default: + fatal("Can't save variables of this type:", (varType < SVT_NUM_TYPES) ? typeName[varType] : "bad ID"); + } + return true; +} + +bool Variable::load(Common::SeekableReadStream *stream) { + varType = (VariableType)stream->readByte(); + switch (varType) { + case SVT_INT: + case SVT_FUNC: + case SVT_BUILT: + case SVT_FILE: + case SVT_OBJTYPE: + varData.intValue = stream->readUint32LE(); + return true; + + case SVT_STRING: + varData.theString = createCString(readString(stream)); + return true; + + case SVT_STACK: + varData.theStack = loadStackRef(stream); + return true; + + case SVT_COSTUME: + varData.costumeHandler = new Persona; + if (!checkNew(varData.costumeHandler)) + return false; + varData.costumeHandler->load(stream); + return true; + + case SVT_ANIM: + varData.animHandler = new PersonaAnimation; + if (!checkNew(varData.animHandler)) + return false; + varData.animHandler->load(stream); + return true; + + default: + break; + } + return true; +} + } // End of namespace Sludge diff --git a/engines/sludge/variable.h b/engines/sludge/variable.h index 005eb1cd05..1ae2acacc7 100644 --- a/engines/sludge/variable.h +++ b/engines/sludge/variable.h @@ -24,6 +24,8 @@ namespace Sludge { +struct Persona; +struct PersonaAnimation; struct Variable; struct VariableStack; @@ -46,76 +48,103 @@ struct FastArrayHandler { struct Variable *fastVariables; int size; int timesUsed; + + Variable *fastArrayGetByIndex(uint theIndex); }; struct StackHandler { struct VariableStack *first; struct VariableStack *last; int timesUsed; + + int getStackSize() const; + bool getSavedGamesStack(const Common::String &ext); }; union VariableData { signed int intValue; const char *theString; StackHandler *theStack; - struct PersonaAnimation *animHandler; - struct Persona *costumeHandler; + PersonaAnimation *animHandler; + Persona *costumeHandler; FastArrayHandler *fastArray; }; struct Variable { VariableType varType; VariableData varData; -}; -struct VariableStack { - Variable thisVar; - VariableStack *next; -}; + Variable() { + varType = SVT_NULL; + varData.intValue = 0; + } + + void unlinkVar(); + void setVariable(VariableType vT, int value); + + // Copy from another variable + bool copyFrom(const Variable &from); + bool copyMain(const Variable &from); // without variable unlink + + // Load & save + bool save(Common::WriteStream *stream); + bool load(Common::SeekableReadStream *stream); -// Initialisation + // Text variable + void makeTextVar(const Common::String &txt); + bool loadStringToVar(int value); -#define initVarNew(thisVar) thisVar.varType = SVT_NULL + // Animation variable + void makeAnimationVariable(PersonaAnimation *i); + struct PersonaAnimation *getAnimationFromVar(); -// Setting variables + // Custome variable + void makeCostumeVariable(Persona *i); + struct Persona *getCostumeFromVar(); -void setVariable(Variable &thisVar, VariableType vT, int value); -bool copyVariable(const Variable &from, Variable &to); -bool loadStringToVar(Variable &thisVar, int value); -void newAnimationVariable(Variable &thisVar, struct PersonaAnimation *i); -void newCostumeVariable(Variable &thisVar, struct Persona *i); -void makeTextVar(Variable &thisVar, const Common::String &txt); -void addVariablesInSecond(Variable &var1, Variable &var2); -void compareVariablesInSecond(const Variable &var1, Variable &var2); -char *createCString(const Common::String &s); + // Fast array variable + bool makeFastArrayFromStack(const StackHandler *stacky); + bool makeFastArraySize(int size); -// Misc. + // Stack variable + bool copyStack(const Variable &from); -void unlinkVar(Variable &thisVar); -Common::String getNumberedString(int value); -Common::String getTextFromAnyVar(const Variable &from); -struct Persona *getCostumeFromVar(Variable &thisVar); -struct PersonaAnimation *getAnimationFromVar(Variable &thisVar); -bool getBoolean(const Variable &from); -bool getValueType(int &toHere, VariableType vT, const Variable &v); + // Add variables + void addVariablesInSecond(const Variable &other); + void compareVariablesInSecond(const Variable &other); + int compareVars(const Variable &other) const; + + // General getters + Common::String getTextFromAnyVar() const; + bool getBoolean() const; + bool getValueType(int &toHere, VariableType vT) const; +}; + +struct VariableStack { + Variable thisVar; + VariableStack *next; + + // Variable getter & setter + bool stackSetByIndex(uint, const Variable &); + Variable *stackGetByIndex(uint); + + // Find last + VariableStack *stackFindLast(); +}; // Stacky stuff bool addVarToStack(const Variable &va, VariableStack *&thisStack); bool addVarToStackQuick(Variable &va, VariableStack *&thisStack); void trimStack(VariableStack *&stack); -int deleteVarFromStack(const Variable &va, VariableStack *&thisStack, - bool allOfEm = false); -VariableStack *stackFindLast(VariableStack *hunt); -bool copyStack(const Variable &from, Variable &to); -int stackSize(const StackHandler *me); -bool stackSetByIndex(VariableStack *, uint, const Variable &); -Variable *stackGetByIndex(VariableStack *, uint); -bool getSavedGamesStack(StackHandler *sH, const Common::String &ext); - -bool makeFastArrayFromStack(Variable &to, const StackHandler *stacky); -bool makeFastArraySize(Variable &to, int size); -Variable *fastArrayGetByIndex(FastArrayHandler *vS, uint theIndex); +int deleteVarFromStack(const Variable &va, VariableStack *&thisStack, bool allOfEm = false); + +// load & save +void saveStack(VariableStack *vs, Common::WriteStream *stream); +VariableStack *loadStack(Common::SeekableReadStream *stream, VariableStack **last); +bool saveStackRef(StackHandler *vs, Common::WriteStream *stream); +StackHandler *loadStackRef(Common::SeekableReadStream *stream); +void clearStackLib(); } // End of namespace Sludge diff --git a/engines/supernova/detection.cpp b/engines/supernova/detection.cpp index 8e9db14db4..b172b7fa49 100644 --- a/engines/supernova/detection.cpp +++ b/engines/supernova/detection.cpp @@ -32,7 +32,7 @@ static const PlainGameDescriptor supernovaGames[] = { {"msn1", "Mission Supernova 1"}, {"msn2", "Mission Supernova 2"}, - {NULL, NULL} + {nullptr, nullptr} }; namespace Supernova { @@ -40,7 +40,7 @@ static const ADGameDescription gameDescriptions[] = { // Mission Supernova 1 { "msn1", - NULL, + nullptr, AD_ENTRY1s("msn_data.000", "f64f16782a86211efa919fbae41e7568", 24163), Common::DE_DEU, Common::kPlatformDOS, @@ -49,7 +49,7 @@ static const ADGameDescription gameDescriptions[] = { }, { "msn1", - NULL, + nullptr, AD_ENTRY1s("msn_data.000", "f64f16782a86211efa919fbae41e7568", 24163), Common::EN_ANY, Common::kPlatformDOS, @@ -60,13 +60,22 @@ static const ADGameDescription gameDescriptions[] = { // Mission Supernova 2 { "msn2", - NULL, + nullptr, AD_ENTRY1s("ms2_data.000", "e595610cba4a6d24a763e428d05cc83f", 24805), Common::DE_DEU, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO1(GUIO_NONE) }, + { + "msn2", + nullptr, + AD_ENTRY1s("ms2_data.000", "e595610cba4a6d24a763e428d05cc83f", 24805), + Common::EN_ANY, + Common::kPlatformDOS, + ADGF_UNSTABLE, + GUIO1(GUIO_NONE) + }, AD_TABLE_END_MARKER }; @@ -122,7 +131,7 @@ bool SupernovaMetaEngine::createInstance(OSystem *syst, Engine **engine, const A *engine = new Supernova::SupernovaEngine(syst); } - return desc != NULL; + return desc != nullptr; } SaveStateList SupernovaMetaEngine::listSaves(const char *target) const { @@ -200,7 +209,11 @@ SaveStateDescriptor SupernovaMetaEngine::querySaveMetaInfos(const char *target, desc.setPlayTime(playTime * 1000); if (Graphics::checkThumbnailHeader(*savefile)) { - Graphics::Surface *const thumbnail = Graphics::loadThumbnail(*savefile); + Graphics::Surface *thumbnail; + if (!Graphics::loadThumbnail(*savefile, thumbnail)) { + delete savefile; + return SaveStateDescriptor(); + } desc.setThumbnail(thumbnail); } diff --git a/engines/supernova/graphics.cpp b/engines/supernova/graphics.cpp index 3a29bacacc..9a05a424ca 100644 --- a/engines/supernova/graphics.cpp +++ b/engines/supernova/graphics.cpp @@ -28,13 +28,14 @@ #include "graphics/palette.h" #include "graphics/surface.h" -#include "graphics.h" -#include "msn_def.h" -#include "supernova.h" +#include "supernova/graphics.h" +#include "supernova/msn_def.h" +#include "supernova/screen.h" +#include "supernova/supernova.h" namespace Supernova { -MSNImageDecoder::MSNImageDecoder() { +MSNImage::MSNImage() { _palette = nullptr; _encodedImage = nullptr; _filenumber = -1; @@ -43,11 +44,11 @@ MSNImageDecoder::MSNImageDecoder() { _numClickFields = 0; } -MSNImageDecoder::~MSNImageDecoder() { +MSNImage::~MSNImage() { destroy(); } -bool MSNImageDecoder::init(int filenumber) { +bool MSNImage::init(int filenumber) { Common::File file; if (!file.open(Common::String::format("msn_data.%03d", filenumber))) { warning("Image data file msn_data.%03d could not be read!", filenumber); @@ -60,7 +61,7 @@ bool MSNImageDecoder::init(int filenumber) { return true; } -bool MSNImageDecoder::loadFromEngineDataFile() { +bool MSNImage::loadFromEngineDataFile() { Common::String name; if (_filenumber == 1) name = "IMG1"; @@ -102,7 +103,7 @@ bool MSNImageDecoder::loadFromEngineDataFile() { return false; } -bool MSNImageDecoder::loadStream(Common::SeekableReadStream &stream) { +bool MSNImage::loadStream(Common::SeekableReadStream &stream) { destroy(); uint size = 0; @@ -199,7 +200,7 @@ bool MSNImageDecoder::loadStream(Common::SeekableReadStream &stream) { return true; } -bool MSNImageDecoder::loadSections() { +bool MSNImage::loadSections() { bool isNewspaper = _filenumber == 1 || _filenumber == 2; int imageWidth = isNewspaper ? 640 : 320; int imageHeight = isNewspaper ? 480 : 200; @@ -238,14 +239,14 @@ bool MSNImageDecoder::loadSections() { return true; } -void MSNImageDecoder::destroy() { +void MSNImage::destroy() { if (_palette) { delete[] _palette; - _palette = NULL; + _palette = nullptr; } if (_encodedImage) { delete[] _encodedImage; - _encodedImage = NULL; + _encodedImage = nullptr; } for (Common::Array<Graphics::Surface *>::iterator it = _sectionSurfaces.begin(); it != _sectionSurfaces.end(); ++it) { diff --git a/engines/supernova/graphics.h b/engines/supernova/graphics.h index 2a820c9432..058da45ba8 100644 --- a/engines/supernova/graphics.h +++ b/engines/supernova/graphics.h @@ -36,10 +36,10 @@ struct Surface; namespace Supernova { -class MSNImageDecoder : public Image::ImageDecoder { +class MSNImage : public Image::ImageDecoder { public: - MSNImageDecoder(); - virtual ~MSNImageDecoder(); + MSNImage(); + virtual ~MSNImage(); virtual void destroy(); virtual bool loadStream(Common::SeekableReadStream &stream); diff --git a/engines/supernova/imageid.h b/engines/supernova/imageid.h new file mode 100644 index 0000000000..7cfa08370e --- /dev/null +++ b/engines/supernova/imageid.h @@ -0,0 +1,654 @@ +/* ScummVM - Graphic Adventure Engine +* +* ScummVM is the legal property of its developers, whose names +* are too numerous to list here. Please refer to the COPYRIGHT +* file distributed with this source distribution. +* +* This program is free software; you can 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 SUPERNOVA_IMAGEID_H +#define SUPERNOVA_IMAGEID_H + +namespace Supernova { + +enum ImageId { + // file 0 + kAxacussanShipBackground, + kAxacussanShipCenterMouthOpen, + kAxacussanShipRightMouthOpen, + // file 1 + kNewspaper1, + // file 2 + kNewspaper2, + // file 3 + kElevatorBackground, + kElevatorGreenButton, + kElevatorRedButton, + kElevatorDoorAnimation1, + kElevatorDoorAnimation2, + kElevatorDoorAnimation3, + kElevatorDoorAnimation4, + kElevatorDoorAnimation5, + kElevatorDummy1, + kElevatorDummy2, + kElevatorDummy3, + kElevatorDummy4, + // file 4 + kShipSpaceBackground, + kShipSpaceRope, + kShipSpaceDummy1, + kShipSpaceDummy2, + // file 5 + kBusStationBackground, + kBusStationArrived, + kBusStationPlantAnimation1, + kBusStationPlantAnimation2, + kBusStationPlantAnimation3, + kBusStationPlantAnimation4, + kBusStationPlantAnimation5, + kBusStationPlantAnimation6, + kBusStationPlantAnimation7, + kBusStationDoorOpened, + // file 6 + kOfficesBackground, + kOfficesDoorOpenTopLeft, + kOfficesDoorOpenBottomLeft, + kOfficesDoorOpenTopRight, + kOfficesDoorOpenBottomRight, + kOfficesAlienCorridorAnimation1, + kOfficesAlienCorridorAnimation2, + kOfficesAlienCorridorAnimation3, + kOfficesAlienCorridorAnimation4, + kOfficesAlienCorridorAnimation5, + kOfficesAlienCorridorAnimation6, + kOfficesAlienTopOfficeAnimation1, + kOfficesAlienTopOfficeAnimation2, + kOfficesAlienTopOfficeAnimation3, + kOfficesAlienTopOfficeAnimation4, + kOfficesAlienTopOfficeAnimation5, + kOfficesAlienBottomOfficeAnimation1, + kOfficesAlienBottomOfficeAnimation2, + kOfficesAlienBottomOfficeAnimation3, + kOfficesAlienBottomOfficeAnimation4, + kOfficesAlienBottomOfficeAnimation5, + kOfficesAlienBottom, + // file 7 + kOfficeLeftBackground, + kOfficeLeftDecoration, + kOfficeLeftMemo, + kOfficeLeftGraffiti, + kOfficeLeftTerminalSmashed, + kOfficeLeftDrawerMoney, + kOfficeLeftSafeOpen, + kOfficeLeftSafeClosed, + kOfficeLeftSafeMoney, + kOfficeLeftDoorOpen, + kOfficeLeftAlienShootAnimation1, + kOfficeLeftAlienShootAnimation2, + kOfficeLeftAlienShootAnimation3, + kOfficeLeftAlienShootAnimation4, + kOfficeLeftAlienShootAnimation5, + kOfficeLeftAlienShootAnimation6, + kOfficeLeftTerminalText, + kOfficeLeftDoorClosed, + kOfficeLeftDummy1, + kOfficeLeftDummy2, + kOfficeLeftDummy3, + kOfficeLeftDummy4, + kOfficeLeftDummy5, + // file 8 + kOfficeRightBackground, + kOfficeRightDecorationPictures, + kOfficeRightDecorationPlants, + kOfficeRightDoorOpen, + kOfficeRightTerminalSmashed, + kOfficeRightAlienShootAnimation1, + kOfficeRightAlienShootAnimation2, + kOfficeRightAlienShootAnimation3, + kOfficeRightAlienTalking, + kOfficeRightDummy1, + kOfficeRightDummy2, + kOfficeRightDummy3, + kOfficeRightDummy4, + // file 9 + kShipCockpitBackground, + kShipCockpitPilotAnimation1, + kShipCockpitPilotAnimation2, + kShipCockpitPilotAnimation3, + kShipCockpitPilotAnimation4, + kShipCockpitPilotAnimation5, + kShipCockpitPilotAnimation6, + kShipCockpitPilotAnimation7, + kShipCockpitPilotAnimation8, + kShipCockpitPilotAnimation9, + kShipCockpitPilotAnimation10, + kShipCockpitPilotAnimation11, + kShipCockpitPilotAnimation12, + kShipCockpitPilotAnimation13, + kShipCockpitPilotAnimation14, + kShipCockpitPilotAnimation15, + kShipCockpitPilotAnimation16, + kShipCockpitPilotAnimation17, + kShipCockpitPilotAnimation18, + kShipCockpitPilotAnimation19, + kShipCockpitPilotAnimation20, + kShipCockpitDisplayStatusAnimation1, + kShipCockpitDisplayStatusAnimation2, + kShipCockpitWindowRocks, + // file 10 + kRestaurantEntranceBackground, + kRestaurantEntrancePorterAnimation1, + kRestaurantEntrancePorterAnimation2, + kRestaurantEntrancePorterAnimation3, + kRestaurantEntrancePorterAnimation4, + kRestaurantEntranceBathroomDoorAnimation1, + kRestaurantEntranceBathroomDoorAnimation2, + kRestaurantEntranceBathroomDoorAnimation3, + kRestaurantEntranceBathroomDoorAnimation4, + kRestaurantEntranceBathroomDoorAnimation5, + kRestaurantEntranceGreenCandy, + kRestaurantEntranceBlueCandy, + kRestaurantEntrancePinkCandy, + kRestaurantEntranceWhiteCandy, + kRestaurantEntranceBlackCandy, + kRestaurantEntraceDummy1, + kRestaurantEntraceDummy2, + // file 11 + kDeathScreen, + // file 12 + kRocksBackground, + kRocksRockAnimation1, + kRocksRockAnimation2, + kRocksRockAnimation3, + // file 13 + kBluePlanetBackground, + kBluePlanetShipAnimation1, + kBluePlanetShipAnimation2, + kBluePlanetShipAnimation3, + kBluePlanetShipAnimation4, + kBluePlanetShipAnimation5, + kBluePlanetShipAnimation6, + kBluePlanetShipAnimation7, + kBluePlanetShipAnimation8, + kBluePlanetShipAnimation9, + kBluePlanetShipAnimation10, + kBluePlanetShipAnimation11, + kBluePlanetShipAnimation12, + kBluePlanetShipAnimation13, + // file 14 + kRogerCrashBackground, + kRogerCrashAnimation1, + kRogerCrashAnimation2, + kRogerCrashAnimation3, + kRogerCrashAnimation4, + kRogerCrashAnimation5, + kRogerCrashAnimation6, + kRogerCrashAnimation7, + kRogerCrashAnimation8, + kRogerCrashAnimation9, + kRogerCrashAnimation10, + kRogerCrashAnimation11, + kRogerCrashAnimation12, + kRogerCrashAnimation13, + kRogerCrashAnimation14, + kRogerCrashAnimation15, + kRogerCrashAnimation16, + kRogerCrashAnimation17, + kRogerCrashAnimation18, + kRogerCrashAnimation19, + // file 15 + kShipCorridorBackground, + kShipCorridorCockpitDoorOpen, + kShipCorridorSleepCabinDoorAnimation1, + kShipCorridorSleepCabinDoorAnimation2, + kShipCorridorSleepCabinDoorAnimation3, + kShipCorridorDummy1, + // file 16 + kAxacussCorridorTileWalled, + kAxacussCorridorToLeft, + kAxacussCorridorToRight, + kAxacussCorridorToTop, + kAxacussCorridorToBottom, + kAxacussCorridorTile, + kAxacussCorridorDoorClosed, + kAxacussCorridorDoorOpen, + kAxacussCorridorDesk, + kAxacussCorridorLaptop, + kAxacussCorridorStuff10, + kAxacussCorridorStuff11, + kAxacussCorridorStuff12, + kAxacussCorridorStuff13, + kAxacussCorridorStuff14, + kAxacussCorridorStuff15, + kAxacussCorridorStuff16, + kAxacussCorridorStuff17, + kAxacussCorridorStuff18, + kAxacussCorridorStuff19, + kAxacussCorridorStuff21, + kAxacussCorridorStuff22, + kAxacussCorridorStuff23, + kAxacussCorridorStuff24, + kAxacussCorridorStuff25, + kAxacussCorridorStuff26, + kAxacussCorridorStuff27, + kAxacussCorridorStuff28, + kAxacussCorridorAlarmStatus, + kAxacussCorridorAlienRight, + kAxacussCorridorAlienLeft, + kAxacussCorridorAlienBottom, + kAxacussCorridorAlienTop, + kAxacussCorridorDummy1, + // file 17 + kShipCorridorCabinBackground, + kShipCorridorCabinL1Open, + kShipCorridorCabinL2Open, + kShipCorridorCabinL3Open, + kShipCorridorCabinR1Open, + kShipCorridorCabinR2Open, + kShipCorridorCabinR3Open, + kShipCorridorCabinAirlockDoorAnimation1, + kShipCorridorCabinAirlockDoorAnimation2, + kShipCorridorCabinAirlockDoorAnimation3, + kShipCorridorCabinDummy1, + kShipCorridorCabinDummy2, + kShipCorridorCabinDummy3, + kShipCorridorCabinDummy4, + kShipCorridorCabinDummy5, + kShipCorridorCabinDummy6, + // file 18 + kShipGeneratorBackground, + kShipGeneratorHatchOpen, + kShipGeneratorJunctionBoxOpen, + kShipGeneratorJunctionBoxOffline, + kShipGeneratorJunctionBoxDisplay, + kShipGeneratorKeycard, + kShipGeneratorSpoolFloating, + kShipGeneratorSpoolOnGround, + kShipGeneratorCableUnplugged, + kShipGeneratorCablePluggedIn, + kShipGeneratorHatchRocks, + kShipGeneratorRopeRocks, + kShipGeneratorRopeSpace, + kShipGeneratorRopeFloor, + kShipGeneratorDummy1, + kShipGeneratorDummy2, + kShipGeneratorDummy3, + kShipGeneratorDummy4, + kShipGeneratorDummy5, + kShipGeneratorDummy6, + kShipGeneratorDummy7, + kShipGeneratorDummy8, + // file 19 + kRogerShipBackground, + kRogerShipButtonPressed1, + kRogerShipButtonPressed2, + kRogerShipButtonPressed3, + kRogerShipButtonPressed4, + kRogerShipCardInSlot, + kRogerShipCompartmentOpen, + kRogerShipUnknown7, + kRogerShipDisplayLeftOn, + kRogerShipGreenDisplayAnimation1, + kRogerShipGreenDisplayAnimation2, + kRogerShipGreenDisplayAnimation3, + kRogerShipGreenDisplayAnimation4, + kRogerShipGreenDisplayAnimation5, + kRogerShipGreenDisplayAnimation6, + kRogerShipGreenDisplayAnimation7, + kRogerShipBlueDisplayAnimation1, + kRogerShipBlueDisplayAnimation2, + kRogerShipBlueDisplayAnimation3, + kRogerShipBlueDisplayAnimation4, + kRogerShipBlueDisplayAnimation5, + kRogerShipBlueDisplayAnimation6, + kRogerShipBlueDisplayAnimation7, + kRogerShipUnknown23, + kRogerShipDisplaySinewaveAnimation1, + kRogerShipDisplaySinewaveAnimation2, + kRogerShipDisplaySinewaveAnimation3, + kRogerShipDisplaySinewaveAnimation4, + kRogerShipDisplaySinewaveAnimation5, + kRogerShipDisplaySinewaveAnimation6, + kRogerShipDisplaySinewaveAnimation7, + kRogerShipDisplaySinewaveAnimation8, + kRogerShipDisplaySinewaveAnimation9, + kRogerShipDisplaySinewaveAnimation10, + kRogerShipDisplaySinewaveAnimation11, + kRogerShipDisplaySinewaveAnimation12, + kRogerShipDisplaySinewaveAnimation13, + kRogerShipDisplaySinewaveAnimation14, + kRogerShipCompartmentContent, + kRogerShipDummy1, + kRogerShipDummy2, + // file 20 + kHelpScreen, + // file 21 + kShipCabinLeftBackground, + kShipCabinLeftPaintingSunset, + kShipCabinLeftPaintingLandscape, + kShipCabinLeftPaintingAbstract, + kShipCabinLeftTableStuff1, + kShipCabinLeftCeilingPencil, + kShipCabinLeft3Decoration, + kShipCabinLeftSocketPluggedIn, + kShipCabinLeftVinyl, + kShipCabinLeftTurntable, + kShipCabinLeftSocketUnplugged, + kShipCabinLeftTurntableCableCut, + kShipCabinLeftTurntableCable, + kShipCabinLeftTurntableAnimation1, + kShipCabinLeftTurntableAnimation2, + kShipCabinLeftTurntableAnimation3, + kShipCabinLeftTableStuff2, + kShipCabinLeftLockerOpen, + kShipCabinLeftLockerBooksOpen, + kShipCabinLeftLockerSpoolOpen, + kShipCabinLeftLockerPistolRemoved, + kShipCabinLeftLockerSpoolRemoved, + kShipCabinLeftBedSafeOpen, + kShipCabinLeftBedSafeEmpty, + kShipCabinLeftDoorClosed, + kShipCabinLeftTurntableCableSparks, + kShipCabinLeftTurntableCableCutEnd, + kShipCabinLeftDummy1, + kShipCabinLeftDummy2, + kShipCabinLeftDummy3, + kShipCabinLeftDummy4, + kShipCabinLeftDummy5, + kShipCabinLeftDummy6, + kShipCabinLeftDummy7, + kShipCabinLeftDummy8, + kShipCabinLeftDummy9, + kShipCabinLeftDummy10, + kShipCabinLeftDummy11, + kShipCabinLeftDummy12, + // file 22 + kShipCabinRightBackground, + kShipCabinRightPosterSnowman, + kShipCabinRightTableStuff, + kShipCabinRightCeilingChess, + kShipCabinRightTennisRacket, + kShipCabinRightTennisBallsFloating, + kShipCabinRightTennisBallsOnGround, + kShipCabinRightTableChess, + kShipCabinRight2Bed, + kShipCabinRightLockerBooksOpen, + kShipCabinRightLockerRopeOpen, + kShipCabinRightLockerOpen, + kShipCabinRightLockerRopeRemoved, + kShipCabinRightBedSafeOpen, + kShipCabinRightBedSafeEmpty, + kShipCabinRightDoorClosed, + kShipCabinRightLockerDiscmanRemoved, + kShipCabinRightDummy1, + kShipCabinRightDummy2, + // file 23 + kShipBathroomBackground, + // file 24 + kShipHoldBackgroundFloating, + kShipHoldBackgroundOnGround, + kShipHoldLandingModuleDoorOpen, + kShipHoldGeneratorHatchOpen, + kShipHoldLandingModuleSpool, + kShipHoldCableToGeneratorUnplugged, + kShipHoldCableToGeneratorPluggedIn, + kShipHoldDummy1, + kShipHoldDummy2, + // file 25 + kShipLandingModuleBackground, + kShipLandingModuleDoorClosed, + kShipLandingModuleDisplayLeftOn, + kShipLandingModuleCablePluggedIn, + kShipLandingModuleCableSpoolConnected, + kShipLandingModuleCableToHold, + kShipLandingModuleDisplayRightOn, + kShipLandingModuleDisplayTop1On, + kShipLandingModuleDisplayTop2On, + kShipLandingModuleDisplayTop3On, + kShipLandingModuleCableWithTerminalStrip, + kShipLandingModuleDummy1, + kShipLandingModuleDummy2, + kShipLandingModuleDummy3, + kShipLandingModuleDummy4, + kShipLandingModuleDummy5, + kShipLandingModuleDummy6, + kShipLandingModuleDummy7, + kShipLandingModuleDummy8, + // file 26 + kArsanoStar, + // file 27 + kSaveLoadScreen, + // file 28 + kRestaurantBackground, + kRestaurantAnimation1, + kRestaurantAnimation2, + kRestaurantAnimation3, + kRestaurantAnimation4, + kRestaurantAnimation5, + kRestaurantAnimation6, + kRestaurantAnimation7, + kRestaurantAnimation8, + kRestaurantAnimation9, + kRestaurantAnimation10, + kRestaurantAnimation11, + kRestaurantAnimation12, + kRestaurantAnimation13, + kRestaurantAnimation14, + kRestaurantAnimation15, + kRestaurantAnimation16, + kRestaurantAnimation17, + kRestaurantAnimation18, + kRestaurantAnimation19, + kRestaurantAnimation20, + kRestaurantAnimation21, + kRestaurantAnimation22, + // file 29 + kRestaurantRogerBackground, + kRestaurantRogerEyes1Closed, + kRestaurantRogerMouthOpen, + kRestaurantRogerPlayingChess, + kRestaurantRogerWalletRemoved, + kRestaurantRogerHandAnimation1, + kRestaurantRogerHandAnimation2, + kRestaurantRogerHandAnimation3, + kRestaurantRogerHandAnimation4, + kRestaurantRogerHandAnimation5, + kRestaurantRogerEyes2Closed, + kRestaurantRogerChessBoard, + kRestaurantRogerEyes2Open, + kRestaurantRogerDummy1, + // file 30 + kRogerOutsideBackground, + kRogerOutsideMouthOpen, + // file 31 + kIntroScreenBackground, + kIntroScreenShipAnimation1, + kIntroScreenShipAnimation2, + kIntroScreenShipAnimation3, + kIntroScreenShipAnimation4, + kIntroScreenShipAnimation5, + kIntroScreenShipAnimation6, + // file 32 + kBusStationSignBackground, + kBusStationSignPrice, + kBusStationSignPleaseWait, + kBusStationSignPleasantFlight, + // file 33 + kShipSleepCabinBackground, + kShipSleepCabinTerminalWarning, + kShipSleepCabinTerminal1, + kShipSleepCabinTerminal2, + kShipSleepCabinStatusLight, + kShipSleepCabinTerminal3, + // file 34 + kShipAirlockBackground, + kShipAirlockDoorLeftAnimation1, + kShipAirlockDoorLeftAnimation2, + kShipAirlockDoorLeftAnimation3, + kShipAirlockDoorRightAnimation1, + kShipAirlockDoorRightAnimation2, + kShipAirlockDoorRightAnimation3, + kShipAirlockHelmetRemoved, + kShipAirlockSpacesuitRemoved, + kShipAirlockSupplyRemoved, + kShipAirlockDoorLeftButton, + kShipAirlockDoorRightButton, + kShipAirlockManometerAnimation1, + kShipAirlockManometerAnimation2, + kShipAirlockManometerAnimation3, + kShipAirlockManometerAnimation4, + kShipAirlockManometerAnimation5, + kShipAirlockManometerAnimation6, + // file 35 + kAxacussSpaceBackground, + kAxacussSpaceShipAnimation1, + kAxacussSpaceShipAnimation2, + kAxacussSpaceShipAnimation3, + kAxacussSpaceShipAnimation4, + kAxacussSpaceShipAnimation5, + kAxacussSpaceShipAnimation6, + kAxacussSpaceShipAnimation7, + kAxacussSpaceBusAnimation1, + kAxacussSpaceBusAnimation2, + kAxacussSpaceBusAnimation3, + kAxacussSpaceBusAnimation4, + kAxacussSpaceBusAnimation5, + kAxacussSpaceBusAnimation6, + kAxacussSpaceBusAnimation7, + kAxacussSpaceBusAnimation8, + kAxacussSpaceBusAnimation9, + kAxacussSpaceBusAnimation10, + kAxacussSpaceBusAnimation11, + kAxacussSpaceBusAnimation12, + kAxacussSpaceBusAnimation13, + kAxacussSpaceBusAnimation14, + // file 36 + kAxacussanCapsuleBackground, + kAxacussanCapsuleRobotAnimation1, + kAxacussanCapsuleRobotAnimation2, + kAxacussanCapsuleRobotAnimation3, + kAxacussanCapsuleRobotAnimation4, + kAxacussanCapsuleDummy1, + // file 37 + kArsanoMeetupBackground, + kArsanoMeetupRestaurantLightAnimation1, + kArsanoMeetupRestaurantLightAnimation2, + kArsanoMeetupRestaurantLightAnimation3, + kArsanoMeetupRestaurantLightAnimation4, + kArsanoMeetupRestaurantLightAnimation5, + kArsanoMeetupRestaurantDoorAnimation1, + kArsanoMeetupRestaurantDoorAnimation2, + kArsanoMeetupRestaurantDoorSignAnimation1, + kArsanoMeetupRestaurantDoorSignAnimation2, + kArsanoMeetupRestaurantDoorSignAnimation3, + kArsanoMeetupRestaurantDoorSignAnimation4, + kArsanoMeetupRestaurantDoorSignAnimation5, + kArsanoMeetupRestaurantSignAnimation1, + kArsanoMeetupRestaurantSignAnimation2, + kArsanoMeetupRestaurantSignAnimation3, + kArsanoMeetupRestaurantSignAnimation4, + kArsanoMeetupRestaurantSignAnimation5, + kArsanoMeetupRestaurantSignAnimation6, + kArsanoMeetupRestaurantSignAnimation7, + kArsanoMeetupRestaurantSignAnimation8, + kArsanoMeetupRestaurantSignAnimation9, + kArsanoMeetupRestaurantSignAnimation10, + kArsanoMeetupRestaurantSignAnimation11, + kArsanoMeetupRestaurantSignAnimation12, + kArsanoMeetupRestaurantSignAnimation13, + kArsanoMeetupRestaurantSignAnimation14, + // file 38 + kArsanoAfterNovaBackground, + kArsanoAfterNovaRogerShipAnimation1, + kArsanoAfterNovaRogerShipAnimation2, + kArsanoAfterNovaRogerShipAnimation3, + kArsanoAfterNovaRogerShipAnimation4, + kArsanoAfterNovaRogerShipAnimation5, + kArsanoAfterNovaRogerShipAnimation6, + kArsanoAfterNovaRogerShipAnimation7, + kArsanoAfterNovaRogerShipAnimation8, + kArsanoAfterNovaRogerShipAnimation9, + kArsanoAfterNovaRogerShipAnimation10, + kArsanoAfterNovaRogerShipAnimation11, + kArsanoAfterNovaRoger, + // file 39 + kArsanoDesolate, + // file 40 + kIntersectionBackground, + kIntersectionGuardRemoved, + kIntersectionGuardMouthOpen, + kIntersectionGuardShootAnimation1, + kIntersectionGuardShootAnimation2, + kIntersectionGuardMouthClosed, + kIntersectionDoorOpen, + kIntersectoinKeycard, + // file 41 + kInformationDeskBackground, + kInformationDeskAlienMouthOpen, + kInformationDeskAlienHandMoved, + kInformationDeskAlienShoot, + // file 42 + kArtGalleryBackground, + kArtGalleryAlienShootAnimation1, + kArtGalleryAlienShootAnimation2, + kArtGalleryAlienShootAnimation3, + kArtGalleryThrowingBlockAnimation1, + kArtGalleryThrowingBlockAnimation2, + kArtGalleryThrowingBlockAnimation3, + kArtGalleryThrowingBlockAnimation4, + kArtGalleryThrowingBlockAnimation5, + kArtGalleryThrowingBlockAnimation6, + kArtGalleryThrowingBlockAnimation7, + kArtGalleryDummy1, + // file 43 + kCellBackground, + kCellCablePluggedIn, + kCellCableUnplugged, + kCellCableCutUnplugged, + kCellCableCutPluggedIn, + kCellCableCutTableUnplugged, + kCellCableCutTablePluggedIn, + kCellTableTablet, + kCellRobotComesAnimation1, + kCellRobotComesAnimation2, + kCellRobotComesAnimation3, + kCellRobotComesAnimation4, + kCellRobotComesAnimation5, + kCellRobotComesAnimation6, + kCellRobotComesAnimation7, + kCellRobotComesAnimation8, + kCellRobotComesAnimation9, + kCellRobotComesAnimation10, + kCellRobotComesAnimation11, + kCellRobotLeavesAnimation1, + kCellRobotLeavesAnimation2, + kCellRobotLeavesAnimation3, + kCellRobotLeavesAnimation4, + kCellRobotLeavesAnimation5, + kCellRobotLeavesAnimation6, + kCellRobotLeavesAnimation7, + kCellRobotLeavesAnimation8, + kCellRobotLeavesAnimation9, + kCellRobotSparks, + kCellRobotBroken, + kCellDoorClosed, + kCellDummy1 +}; + +} + +#endif diff --git a/engines/supernova/module.mk b/engines/supernova/module.mk index 9baf196c7c..722a230cde 100644 --- a/engines/supernova/module.mk +++ b/engines/supernova/module.mk @@ -4,9 +4,12 @@ MODULE_OBJS := \ console.o \ detection.o \ graphics.o \ - supernova.o \ + resman.o \ rooms.o \ - state.o + screen.o \ + sound.o \ + state.o \ + supernova.o MODULE_DIRS += \ engines/supernova diff --git a/engines/supernova/msn_def.h b/engines/supernova/msn_def.h index c18baa068c..6ce16b8283 100644 --- a/engines/supernova/msn_def.h +++ b/engines/supernova/msn_def.h @@ -25,10 +25,6 @@ namespace Supernova { -const int kScreenWidth = 320; -const int kScreenHeight = 200; -const int kFontWidth = 5; -const int kFontHeight = 8; const int kTextSpeed[] = {19, 14, 10, 7, 4}; const int kMsecPerTick = 55; @@ -50,266 +46,6 @@ enum MessagePosition { kMessageTop }; -enum AudioIndex { - kAudioFoundLocation, // 44|0 - kAudioCrash, // 45|0 - kAudioVoiceHalt, // 46|0 - kAudioGunShot, // 46|2510 - kAudioSmash, // 46|4020 - kAudioVoiceSupernova, // 47|0 - kAudioVoiceYeah, // 47|24010 - kAudioRobotShock, // 48|0 - kAudioRobotBreaks, // 48|2510 - kAudioShock, // 48|10520 - kAudioTurntable, // 48|13530 - kAudioSiren, // 50|0 - kAudioSnoring, // 50|12786 - kAudioRocks, // 51|0 - kAudioDeath, // 53|0 - kAudioAlarm, // 54|0 - kAudioSuccess, // 54|8010 - kAudioSlideDoor, // 54|24020 - kAudioDoorOpen, // 54|30030 - kAudioDoorClose, // 54|31040 - kAudioNumSamples -}; - -enum MusicIndex { - kMusicIntro = 52, - kMusicOutro = 49 -}; - -struct AudioInfo { - int _filenumber; - int _offsetStart; - int _offsetEnd; -}; - -const int kColorBlack = 0; -const int kColorWhite25 = 1; -const int kColorWhite35 = 2; -const int kColorWhite44 = 3; -const int kColorWhite99 = 4; -const int kColorDarkGreen = 5; -const int kColorGreen = 6; -const int kColorDarkRed = 7; -const int kColorRed = 8; -const int kColorDarkBlue = 9; -const int kColorBlue = 10; -const int kColorWhite63 = 11; -const int kColorLightBlue = 12; -const int kColorLightGreen = 13; -const int kColorLightYellow = 14; -const int kColorLightRed = 15; -const int kColorCursorTransparent = kColorWhite25; - -const byte mouseNormal[64] = { - 0xff,0x3f,0xff,0x1f,0xff,0x0f,0xff,0x07, - 0xff,0x03,0xff,0x01,0xff,0x00,0x7f,0x00, - 0x3f,0x00,0x1f,0x00,0x0f,0x00,0x0f,0x00, - 0xff,0x00,0x7f,0x18,0x7f,0x38,0x7f,0xfc, - - 0x00,0x00,0x00,0x40,0x00,0x60,0x00,0x70, - 0x00,0x78,0x00,0x7c,0x00,0x7e,0x00,0x7f, - 0x80,0x7f,0xc0,0x7f,0xe0,0x7f,0x00,0x7e, - 0x00,0x66,0x00,0x43,0x00,0x03,0x00,0x00 -}; - -const byte mouseWait[64] = { - 0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x80, - 0x01,0x80,0x01,0x80,0x11,0x88,0x31,0x8c, - 0x31,0x8c,0x11,0x88,0x01,0x80,0x01,0x80, - 0x01,0x80,0x00,0x00,0x00,0x00,0x00,0x00, - - 0x00,0x00,0xfe,0x7f,0xf4,0x2f,0xf4,0x2f, - 0x14,0x28,0x24,0x24,0x44,0x22,0x84,0x21, - 0x84,0x21,0xc4,0x23,0xe4,0x27,0x74,0x2e, - 0x34,0x2c,0x14,0x28,0xfe,0x7f,0x00,0x00 -}; - -const byte font[][5] = { - {0x00,0x00,0x00,0xff,0x00}, - {0x5f,0xff,0x00,0x00,0x00}, - {0x03,0x00,0x03,0xff,0x00}, - {0x14,0x7f,0x14,0x7f,0x14}, - {0x24,0x2a,0x7f,0x2a,0x12}, - {0x61,0x10,0x08,0x04,0x43}, - {0x38,0x4e,0x59,0x26,0x50}, - {0x03,0xff,0x00,0x00,0x00}, - {0x3e,0x41,0xff,0x00,0x00}, - {0x41,0x3e,0xff,0x00,0x00}, - {0x10,0x54,0x38,0x54,0x10}, - {0x10,0x10,0x7c,0x10,0x10}, - {0x80,0x40,0xff,0x00,0x00}, - {0x10,0x10,0x10,0x10,0x10}, - {0x40,0xff,0x00,0x00,0x00}, - {0x60,0x10,0x08,0x04,0x03}, - - {0x3e,0x41,0x41,0x41,0x3e}, /* digits */ - {0x04,0x02,0x7f,0xff,0x00}, - {0x42,0x61,0x51,0x49,0x46}, - {0x22,0x41,0x49,0x49,0x36}, - {0x18,0x14,0x12,0x7f,0x10}, - {0x27,0x45,0x45,0x45,0x39}, - {0x3e,0x49,0x49,0x49,0x32}, - {0x01,0x61,0x19,0x07,0x01}, - {0x36,0x49,0x49,0x49,0x36}, - {0x26,0x49,0x49,0x49,0x3e}, - - {0x44,0xff,0x00,0x00,0x00}, - {0x80,0x44,0xff,0x00,0x00}, - {0x10,0x28,0x44,0xff,0x00}, - {0x28,0x28,0x28,0x28,0x28}, - {0x44,0x28,0x10,0xff,0x00}, - {0x02,0x01,0x51,0x09,0x06}, - {0x3e,0x41,0x5d,0x5d,0x1e}, - - {0x7c,0x12,0x11,0x12,0x7c}, /* uppercase letters*/ - {0x7f,0x49,0x49,0x49,0x36}, - {0x3e,0x41,0x41,0x41,0x22}, - {0x7f,0x41,0x41,0x22,0x1c}, - {0x7f,0x49,0x49,0x49,0xff}, - {0x7f,0x09,0x09,0x09,0xff}, - {0x3e,0x41,0x41,0x49,0x3a}, - {0x7f,0x08,0x08,0x08,0x7f}, - {0x41,0x7f,0x41,0xff,0x00}, - {0x20,0x40,0x40,0x3f,0xff}, - {0x7f,0x08,0x14,0x22,0x41}, - {0x7f,0x40,0x40,0x40,0xff}, - {0x7f,0x02,0x04,0x02,0x7f}, - {0x7f,0x02,0x0c,0x10,0x7f}, - {0x3e,0x41,0x41,0x41,0x3e}, - {0x7f,0x09,0x09,0x09,0x06}, - {0x3e,0x41,0x51,0x21,0x5e}, - {0x7f,0x09,0x19,0x29,0x46}, - {0x26,0x49,0x49,0x49,0x32}, - {0x01,0x01,0x7f,0x01,0x01}, - {0x3f,0x40,0x40,0x40,0x3f}, - {0x07,0x18,0x60,0x18,0x07}, - {0x1f,0x60,0x18,0x60,0x1f}, - {0x63,0x14,0x08,0x14,0x63}, - {0x03,0x04,0x78,0x04,0x03}, - {0x61,0x51,0x49,0x45,0x43}, - - {0x7f,0x41,0x41,0xff,0x00}, - {0x03,0x04,0x08,0x10,0x60}, - {0x41,0x41,0x7f,0xff,0x00}, - {0x02,0x01,0x02,0xff,0x00}, - {0x80,0x80,0x80,0x80,0x80}, - {0x01,0x02,0xff,0x00,0x00}, - - {0x38,0x44,0x44,0x44,0x7c}, /* lowercase letters */ - {0x7f,0x44,0x44,0x44,0x38}, - {0x38,0x44,0x44,0x44,0x44}, - {0x38,0x44,0x44,0x44,0x7f}, - {0x38,0x54,0x54,0x54,0x58}, - {0x04,0x7e,0x05,0x01,0xff}, - {0x98,0xa4,0xa4,0xa4,0x7c}, - {0x7f,0x04,0x04,0x04,0x78}, - {0x7d,0xff,0x00,0x00,0x00}, - {0x80,0x80,0x7d,0xff,0x00}, - {0x7f,0x10,0x28,0x44,0xff}, - {0x7f,0xff,0x00,0x00,0x00}, - {0x7c,0x04,0x7c,0x04,0x78}, - {0x7c,0x04,0x04,0x04,0x78}, - {0x38,0x44,0x44,0x44,0x38}, - {0xfc,0x24,0x24,0x24,0x18}, - {0x18,0x24,0x24,0x24,0xfc}, - {0x7c,0x08,0x04,0x04,0xff}, - {0x48,0x54,0x54,0x54,0x24}, - {0x04,0x3e,0x44,0x40,0xff}, - {0x7c,0x40,0x40,0x40,0x3c}, - {0x0c,0x30,0x40,0x30,0x0c}, - {0x3c,0x40,0x3c,0x40,0x3c}, - {0x44,0x28,0x10,0x28,0x44}, - {0x9c,0xa0,0xa0,0xa0,0x7c}, - {0x44,0x64,0x54,0x4c,0x44}, - - {0x08,0x36,0x41,0xff,0x00}, - {0x77,0xff,0x00,0x00,0x00}, - {0x41,0x36,0x08,0xff,0x00}, - {0x02,0x01,0x02,0x01,0xff}, - {0xff,0x00,0x00,0x00,0x00}, - - {0xfe,0x49,0x49,0x4e,0x30}, /* sharp S */ - - {0x7c,0x41,0x40,0x41,0x3c}, /* umlauts */ - - {0x04,0x06,0x7f,0x06,0x04}, /* arrows */ - {0x20,0x60,0xfe,0x60,0x20}, - - {0x38,0x45,0x44,0x45,0x7c}, /* umlauts */ - {0xff,0x00,0x00,0x00,0x00}, - {0xff,0x00,0x00,0x00,0x00}, - {0xff,0x00,0x00,0x00,0x00}, - {0xff,0x00,0x00,0x00,0x00}, - {0xff,0x00,0x00,0x00,0x00}, - {0xff,0x00,0x00,0x00,0x00}, - {0xff,0x00,0x00,0x00,0x00}, - {0xff,0x00,0x00,0x00,0x00}, - {0xff,0x00,0x00,0x00,0x00}, - {0x79,0x14,0x12,0x14,0x79}, - {0xff,0x00,0x00,0x00,0x00}, - {0xff,0x00,0x00,0x00,0x00}, - {0xff,0x00,0x00,0x00,0x00}, - {0xff,0x00,0x00,0x00,0x00}, - {0xff,0x00,0x00,0x00,0x00}, - {0x38,0x45,0x44,0x45,0x38}, - {0xff,0x00,0x00,0x00,0x00}, - {0xff,0x00,0x00,0x00,0x00}, - {0xff,0x00,0x00,0x00,0x00}, - {0xff,0x00,0x00,0x00,0x00}, - {0x3d,0x42,0x42,0x42,0x3d}, - {0x3d,0x40,0x40,0x40,0x3d}, -}; - -// Default palette -const byte initVGAPalette[768] = { - 0x00, 0x00, 0x00, 0x40, 0x40, 0x40, 0x58, 0x58, 0x58, 0x70, 0x70, 0x70, 0xfc, 0xfc, 0xfc, 0x00, 0xd0, 0x00, - 0x00, 0xfc, 0x00, 0xd8, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0xb0, 0xa0, 0xa0, 0xa0, - 0x50, 0xc8, 0xfc, 0x28, 0xfc, 0x28, 0xf0, 0xf0, 0x00, 0xfc, 0x28, 0x28, 0x00, 0x00, 0x00, 0x14, 0x14, 0x14, - 0x20, 0x20, 0x20, 0x2c, 0x2c, 0x2c, 0x38, 0x38, 0x38, 0x44, 0x44, 0x44, 0x50, 0x50, 0x50, 0x60, 0x60, 0x60, - 0x70, 0x70, 0x70, 0x80, 0x80, 0x80, 0x90, 0x90, 0x90, 0xa0, 0xa0, 0xa0, 0xb4, 0xb4, 0xb4, 0xc8, 0xc8, 0xc8, - 0xe0, 0xe0, 0xe0, 0xfc, 0xfc, 0xfc, 0x00, 0x00, 0xfc, 0x40, 0x00, 0xfc, 0x7c, 0x00, 0xfc, 0xbc, 0x00, 0xfc, - 0xfc, 0x00, 0xfc, 0xfc, 0x00, 0xbc, 0xfc, 0x00, 0x7c, 0xfc, 0x00, 0x40, 0xfc, 0x00, 0x00, 0xfc, 0x40, 0x00, - 0xfc, 0x7c, 0x00, 0xfc, 0xbc, 0x00, 0xfc, 0xfc, 0x00, 0xbc, 0xfc, 0x00, 0x7c, 0xfc, 0x00, 0x40, 0xfc, 0x00, - 0x00, 0xfc, 0x00, 0x00, 0xfc, 0x40, 0x00, 0xfc, 0x7c, 0x00, 0xfc, 0xbc, 0x00, 0xfc, 0xfc, 0x00, 0xbc, 0xfc, - 0x00, 0x7c, 0xfc, 0x00, 0x40, 0xfc, 0x7c, 0x7c, 0xfc, 0x9c, 0x7c, 0xfc, 0xbc, 0x7c, 0xfc, 0xdc, 0x7c, 0xfc, - 0xfc, 0x7c, 0xfc, 0xfc, 0x7c, 0xdc, 0xfc, 0x7c, 0xbc, 0xfc, 0x7c, 0x9c, 0xfc, 0x7c, 0x7c, 0xfc, 0x9c, 0x7c, - 0xfc, 0xbc, 0x7c, 0xfc, 0xdc, 0x7c, 0xfc, 0xfc, 0x7c, 0xdc, 0xfc, 0x7c, 0xbc, 0xfc, 0x7c, 0x9c, 0xfc, 0x7c, - 0x7c, 0xfc, 0x7c, 0x7c, 0xfc, 0x9c, 0x7c, 0xfc, 0xbc, 0x7c, 0xfc, 0xdc, 0x7c, 0xfc, 0xfc, 0x7c, 0xdc, 0xfc, - 0x7c, 0xbc, 0xfc, 0x7c, 0x9c, 0xfc, 0xb4, 0xb4, 0xfc, 0xc4, 0xb4, 0xfc, 0xd8, 0xb4, 0xfc, 0xe8, 0xb4, 0xfc, - 0xfc, 0xb4, 0xfc, 0xfc, 0xb4, 0xe8, 0xfc, 0xb4, 0xd8, 0xfc, 0xb4, 0xc4, 0xfc, 0xb4, 0xb4, 0xfc, 0xc4, 0xb4, - 0xfc, 0xd8, 0xb4, 0xfc, 0xe8, 0xb4, 0xfc, 0xfc, 0xb4, 0xe8, 0xfc, 0xb4, 0xd8, 0xfc, 0xb4, 0xc4, 0xfc, 0xb4, - 0xb4, 0xfc, 0xb4, 0xb4, 0xfc, 0xc4, 0xb4, 0xfc, 0xd8, 0xb4, 0xfc, 0xe8, 0xb4, 0xfc, 0xfc, 0xb4, 0xe8, 0xfc, - 0xb4, 0xd8, 0xfc, 0xb4, 0xc4, 0xfc, 0x00, 0x00, 0x70, 0x1c, 0x00, 0x70, 0x38, 0x00, 0x70, 0x54, 0x00, 0x70, - 0x70, 0x00, 0x70, 0x70, 0x00, 0x54, 0x70, 0x00, 0x38, 0x70, 0x00, 0x1c, 0x70, 0x00, 0x00, 0x70, 0x1c, 0x00, - 0x70, 0x38, 0x00, 0x70, 0x54, 0x00, 0x70, 0x70, 0x00, 0x54, 0x70, 0x00, 0x38, 0x70, 0x00, 0x1c, 0x70, 0x00, - 0x00, 0x70, 0x00, 0x00, 0x70, 0x1c, 0x00, 0x70, 0x38, 0x00, 0x70, 0x54, 0x00, 0x70, 0x70, 0x00, 0x54, 0x70, - 0x00, 0x38, 0x70, 0x00, 0x1c, 0x70, 0x38, 0x38, 0x70, 0x44, 0x38, 0x70, 0x54, 0x38, 0x70, 0x60, 0x38, 0x70, - 0x70, 0x38, 0x70, 0x70, 0x38, 0x60, 0x70, 0x38, 0x54, 0x70, 0x38, 0x44, 0x70, 0x38, 0x38, 0x70, 0x44, 0x38, - 0x70, 0x54, 0x38, 0x70, 0x60, 0x38, 0x70, 0x70, 0x38, 0x60, 0x70, 0x38, 0x54, 0x70, 0x38, 0x44, 0x70, 0x38, - 0x38, 0x70, 0x38, 0x38, 0x70, 0x44, 0x38, 0x70, 0x54, 0x38, 0x70, 0x60, 0x38, 0x70, 0x70, 0x38, 0x60, 0x70, - 0x38, 0x54, 0x70, 0x38, 0x44, 0x70, 0x50, 0x50, 0x70, 0x58, 0x50, 0x70, 0x60, 0x50, 0x70, 0x68, 0x50, 0x70, - 0x70, 0x50, 0x70, 0x70, 0x50, 0x68, 0x70, 0x50, 0x60, 0x70, 0x50, 0x58, 0x70, 0x50, 0x50, 0x70, 0x58, 0x50, - 0x70, 0x60, 0x50, 0x70, 0x68, 0x50, 0x70, 0x70, 0x50, 0x68, 0x70, 0x50, 0x60, 0x70, 0x50, 0x58, 0x70, 0x50, - 0x50, 0x70, 0x50, 0x50, 0x70, 0x58, 0x50, 0x70, 0x60, 0x50, 0x70, 0x68, 0x50, 0x70, 0x70, 0x50, 0x68, 0x70, - 0x50, 0x60, 0x70, 0x50, 0x58, 0x70, 0x00, 0x00, 0x40, 0x10, 0x00, 0x40, 0x20, 0x00, 0x40, 0x30, 0x00, 0x40, - 0x40, 0x00, 0x40, 0x40, 0x00, 0x30, 0x40, 0x00, 0x20, 0x40, 0x00, 0x10, 0x40, 0x00, 0x00, 0x40, 0x10, 0x00, - 0x40, 0x20, 0x00, 0x40, 0x30, 0x00, 0x40, 0x40, 0x00, 0x30, 0x40, 0x00, 0x20, 0x40, 0x00, 0x10, 0x40, 0x00, - 0x00, 0x40, 0x00, 0x00, 0x40, 0x10, 0x00, 0x40, 0x20, 0x00, 0x40, 0x30, 0x00, 0x40, 0x40, 0x00, 0x30, 0x40, - 0x00, 0x20, 0x40, 0x00, 0x10, 0x40, 0x20, 0x20, 0x40, 0x28, 0x20, 0x40, 0x30, 0x20, 0x40, 0x38, 0x20, 0x40, - 0x40, 0x20, 0x40, 0x40, 0x20, 0x38, 0x40, 0x20, 0x30, 0x40, 0x20, 0x28, 0x40, 0x20, 0x20, 0x40, 0x28, 0x20, - 0x40, 0x30, 0x20, 0x40, 0x38, 0x20, 0x40, 0x40, 0x20, 0x38, 0x40, 0x20, 0x30, 0x40, 0x20, 0x28, 0x40, 0x20, - 0x20, 0x40, 0x20, 0x20, 0x40, 0x28, 0x20, 0x40, 0x30, 0x20, 0x40, 0x38, 0x20, 0x40, 0x40, 0x20, 0x38, 0x40, - 0x20, 0x30, 0x40, 0x20, 0x28, 0x40, 0x2c, 0x2c, 0x40, 0x30, 0x2c, 0x40, 0x34, 0x2c, 0x40, 0x3c, 0x2c, 0x40, - 0x40, 0x2c, 0x40, 0x40, 0x2c, 0x3c, 0x40, 0x2c, 0x34, 0x40, 0x2c, 0x30, 0x40, 0x2c, 0x2c, 0x40, 0x30, 0x2c, - 0x40, 0x34, 0x2c, 0x40, 0x3c, 0x2c, 0x40, 0x40, 0x2c, 0x3c, 0x40, 0x2c, 0x34, 0x40, 0x2c, 0x30, 0x40, 0x2c, - 0x2c, 0x40, 0x2c, 0x2c, 0x40, 0x30, 0x2c, 0x40, 0x34, 0x2c, 0x40, 0x3c, 0x2c, 0x40, 0x40, 0x2c, 0x3c, 0x40, - 0x2c, 0x34, 0x40, 0x2c, 0x30, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - enum ObjectType { NULLTYPE = 0, TAKE = 1, @@ -341,7 +77,7 @@ enum Action { ACTION_GIVE }; -enum RoomID { +enum RoomId { INTRO,CORRIDOR,HALL,SLEEP,COCKPIT,AIRLOCK, HOLD,LANDINGMODULE,GENERATOR,OUTSIDE, CABIN_R1,CABIN_R2,CABIN_R3,CABIN_L1,CABIN_L2,CABIN_L3,BATHROOM, @@ -353,7 +89,7 @@ enum RoomID { ELEVATOR,STATION,SIGN,OUTRO,NUMROOMS,NULLROOM }; -enum ObjectID { +enum ObjectId { INVALIDOBJECT = -1, NULLOBJECT = 0, KEYCARD,KNIFE,WATCH, @@ -397,7 +133,7 @@ enum ObjectID { JUNGLE,STATION_SLOT,STATION_SIGN }; -enum StringID { +enum StringId { kNoString = -1, // 0 kStringCommandGo = 0, kStringCommandLook, kStringCommandTake, kStringCommandOpen, kStringCommandClose, @@ -568,8 +304,6 @@ ObjectType &operator&=(ObjectType &a, ObjectType b); ObjectType &operator^=(ObjectType &a, ObjectType b); struct Object { - static const Object nullObject; - Object() : _name(kNoString) , _description(kStringDefaultDescription) @@ -582,8 +316,8 @@ struct Object { , _exitRoom(NULLROOM) , _direction(0) {} - Object(byte roomId, StringID name, StringID description, ObjectID id, ObjectType type, - byte click, byte click2, byte section = 0, RoomID exitRoom = NULLROOM, byte direction = 0) + Object(byte roomId, StringId name, StringId description, ObjectId id, ObjectType type, + byte click, byte click2, byte section = 0, RoomId exitRoom = NULLROOM, byte direction = 0) : _name(name) , _description(description) , _id(id) @@ -596,12 +330,6 @@ struct Object { , _direction(direction) {} - static void setObjectNull(Object *&obj) { - obj = const_cast<Object *>(&nullObject); - } - static bool isNullObject(Object *obj) { - return obj == &nullObject; - } void resetProperty(ObjectType type = NULLTYPE) { _type = type; } @@ -618,7 +346,7 @@ struct Object { return _type & type; } - static bool combine(Object &obj1, Object &obj2, ObjectID id1, ObjectID id2) { + static bool combine(Object &obj1, Object &obj2, ObjectId id1, ObjectId id2) { if (obj1.hasProperty(COMBINABLE)) return (((obj1._id == id1) && (obj2._id == id2)) || ((obj1._id == id2) && (obj2._id == id1))); @@ -627,14 +355,14 @@ struct Object { } byte _roomId; - StringID _name; - StringID _description; - ObjectID _id; + StringId _name; + StringId _description; + ObjectId _id; ObjectTypes _type; byte _click; byte _click2; byte _section; - RoomID _exitRoom; + RoomId _exitRoom; byte _direction; }; diff --git a/engines/supernova/resman.cpp b/engines/supernova/resman.cpp new file mode 100644 index 0000000000..1c178846ed --- /dev/null +++ b/engines/supernova/resman.cpp @@ -0,0 +1,395 @@ +/* ScummVM - Graphic Adventure Engine +* +* ScummVM is the legal property of its developers, whose names +* are too numerous to list here. Please refer to the COPYRIGHT +* file distributed with this source distribution. +* +* This program is free software; you can 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 "audio/audiostream.h" +#include "audio/decoders/raw.h" +#include "audio/mixer.h" +#include "audio/mods/protracker.h" +#include "common/memstream.h" +#include "common/system.h" +#include "graphics/cursorman.h" +#include "graphics/palette.h" + +#include "supernova/graphics.h" +#include "supernova/resman.h" +#include "supernova/screen.h" +#include "supernova/supernova.h" + +namespace Supernova { + +struct AudioInfo { + int _filenumber; + int _offsetStart; + int _offsetEnd; +}; + +static Common::MemoryReadStream *convertToMod(const char *filename, int version = 1); + +static const AudioInfo audioInfo[kAudioNumSamples] = { + {44, 0, -1}, + {45, 0, -1}, + {46, 0, 2510}, + {46, 2510, 4020}, + {46, 4020, -1}, + {47, 0, 24010}, + {47, 24010, -1}, + {48, 0, 2510}, + {48, 2510, 10520}, + {48, 10520, 13530}, + {48, 13530, -1}, + {50, 0, 12786}, + {50, 12786, -1}, + {51, 0, -1}, + {53, 0, -1}, + {54, 0, 8010}, + {54, 8010, 24020}, + {54, 24020, 30030}, + {54, 30030, 31040}, + {54, 31040, -1} +}; + +static const byte mouseNormal[64] = { + 0xff,0x3f,0xff,0x1f,0xff,0x0f,0xff,0x07, + 0xff,0x03,0xff,0x01,0xff,0x00,0x7f,0x00, + 0x3f,0x00,0x1f,0x00,0x0f,0x00,0x0f,0x00, + 0xff,0x00,0x7f,0x18,0x7f,0x38,0x7f,0xfc, + + 0x00,0x00,0x00,0x40,0x00,0x60,0x00,0x70, + 0x00,0x78,0x00,0x7c,0x00,0x7e,0x00,0x7f, + 0x80,0x7f,0xc0,0x7f,0xe0,0x7f,0x00,0x7e, + 0x00,0x66,0x00,0x43,0x00,0x03,0x00,0x00 +}; + +static const byte mouseWait[64] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x80, + 0x01,0x80,0x01,0x80,0x11,0x88,0x31,0x8c, + 0x31,0x8c,0x11,0x88,0x01,0x80,0x01,0x80, + 0x01,0x80,0x00,0x00,0x00,0x00,0x00,0x00, + + 0x00,0x00,0xfe,0x7f,0xf4,0x2f,0xf4,0x2f, + 0x14,0x28,0x24,0x24,0x44,0x22,0x84,0x21, + 0x84,0x21,0xc4,0x23,0xe4,0x27,0x74,0x2e, + 0x34,0x2c,0x14,0x28,0xfe,0x7f,0x00,0x00 +}; + + +ResourceManager::ResourceManager() + : _audioRate(11931) { + initSoundFiles(); + initGraphics(); +} + +void ResourceManager::initSoundFiles() { + // Sound + // Note: + // - samples start with a header of 6 bytes: 01 SS SS 00 AD 00 + // where SS SS (LE uint16) is the size of the sound sample + 2 + // - samples end with a footer of 4 bytes: 00 00 + // Skip those in the buffer + Common::File file; + + for (int i = 0; i < kAudioNumSamples; ++i) { + if (!file.open(Common::String::format("msn_data.%03d", audioInfo[i]._filenumber))) { + error("File %s could not be read!", file.getName()); + } + + int length = 0; + byte *buffer = nullptr; + + if (audioInfo[i]._offsetEnd == -1) { + file.seek(0, SEEK_END); + length = file.pos() - audioInfo[i]._offsetStart - 10; + } else { + length = audioInfo[i]._offsetEnd - audioInfo[i]._offsetStart - 10; + } + buffer = new byte[length]; + file.seek(audioInfo[i]._offsetStart + 6); + file.read(buffer, length); + file.close(); + + byte streamFlag = Audio::FLAG_UNSIGNED | Audio::FLAG_LITTLE_ENDIAN; + _soundSamples[i].reset(Audio::makeRawStream(buffer, length, _audioRate, + streamFlag, DisposeAfterUse::YES)); + } + + _musicIntroBuffer.reset(convertToMod("msn_data.052")); + _musicOutroBuffer.reset(convertToMod("msn_data.049")); +} + +void ResourceManager::initGraphics() { + Screen::initPalette(); + initCursorGraphics(); + initImages(); +} + +void ResourceManager::initCursorGraphics() { + const uint16 *bufferNormal = reinterpret_cast<const uint16 *>(mouseNormal); + const uint16 *bufferWait = reinterpret_cast<const uint16 *>(mouseWait); + for (uint i = 0; i < sizeof(mouseNormal) / 4; ++i) { + for (uint bit = 0; bit < 16; ++bit) { + uint mask = 0x8000 >> bit; + uint bitIndex = i * 16 + bit; + + _cursorNormal[bitIndex] = (READ_LE_UINT16(bufferNormal + i) & mask) ? + kColorCursorTransparent : kColorBlack; + if (READ_LE_UINT16(bufferNormal + i + 16) & mask) + _cursorNormal[bitIndex] = kColorLightRed; + + _cursorWait[bitIndex] = (READ_LE_UINT16(bufferWait + i) & mask) ? + kColorCursorTransparent : kColorBlack; + if (READ_LE_UINT16(bufferWait + i + 16) & mask) + _cursorWait[bitIndex] = kColorLightRed; + } + } +} + +void ResourceManager::initImages() { + for (int i = 0; i < kNumImageFiles; ++i) { + if (!_images[i].init(i)) + error("Failed reading image file msn_data.%03d", i); + } +} + +Audio::SeekableAudioStream *ResourceManager::getSoundStream(AudioId index) { + Audio::SeekableAudioStream *stream = _soundSamples[index].get(); + stream->rewind(); + + return stream; +} + +Audio::AudioStream *ResourceManager::getSoundStream(MusicId index) { + switch (index) { + case kMusicIntro: + _musicIntro.reset(Audio::makeProtrackerStream(_musicIntroBuffer.get())); + return _musicIntro.get(); + case kMusicOutro: + _musicOutro.reset(Audio::makeProtrackerStream(_musicOutroBuffer.get())); + return _musicOutro.get(); + default: + error("Invalid music constant in playAudio()"); + } +} + +const MSNImage *ResourceManager::getImage(int filenumber) const { + assert(filenumber < kNumImageFiles); + + return &_images[filenumber]; +} + +const byte *ResourceManager::getImage(CursorId id) const { + switch (id) { + case kCursorNormal: + return _cursorNormal; + case kCursorWait: + return _cursorWait; + default: + return nullptr; + } +} + +static Common::MemoryReadStream *convertToMod(const char *filename, int version) { + // MSN format + struct { + uint16 seg; + uint16 start; + uint16 end; + uint16 loopStart; + uint16 loopEnd; + char volume; + char dummy[5]; + } instr2[22]; + int nbInstr2; // 22 for version1, 15 for version 2 + int16 songLength; + char arrangement[128]; + int16 patternNumber; + int32 note2[28][64][4]; + + nbInstr2 = ((version == 1) ? 22 : 15); + + Common::File msnFile; + msnFile.open(filename); + if (!msnFile.isOpen()) { + warning("Data file '%s' not found", msnFile.getName()); + return nullptr; + } + + for (int i = 0 ; i < nbInstr2 ; ++i) { + instr2[i].seg = msnFile.readUint16LE(); + instr2[i].start = msnFile.readUint16LE(); + instr2[i].end = msnFile.readUint16LE(); + instr2[i].loopStart = msnFile.readUint16LE(); + instr2[i].loopEnd = msnFile.readUint16LE(); + instr2[i].volume = msnFile.readByte(); + msnFile.read(instr2[i].dummy, 5); + } + songLength = msnFile.readSint16LE(); + msnFile.read(arrangement, 128); + patternNumber = msnFile.readSint16LE(); + for (int p = 0 ; p < patternNumber ; ++p) { + for (int n = 0 ; n < 64 ; ++n) { + for (int k = 0 ; k < 4 ; ++k) { + note2[p][n][k] = msnFile.readSint32LE(); + } + } + } + + /* MOD format */ + struct { + char iname[22]; + uint16 length; + char finetune; + char volume; + uint16 loopStart; + uint16 loopLength; + } instr[31]; + int32 note[28][64][4]; + + // We can't recover some MOD effects since several of them are mapped to 0. + // Assume the MSN effect of value 0 is Arpeggio (MOD effect of value 0). + const char invConvEff[8] = {0, 1, 2, 3, 10, 12, 13 ,15}; + + // Reminder from convertToMsn + // 31 30 29 28 27 26 25 24 - 23 22 21 20 19 18 17 16 - 15 14 13 12 11 10 09 08 - 07 06 05 04 03 02 01 00 + // h h h h g g g g f f f f e e e e d d d d c c c c b b b b a a a a + // + // MSN: + // hhhh (4 bits) Cleared to 0 + // dddd c (5 bits) Sample index | after mapping through convInstr + // ccc (3 bits) Effect type | after mapping through convEff + // bbbb aaaa (8 bits) Effect value | unmodified + // gggg ffff eeee (12 bits) Sample period | unmodified + // + // MS2: + // hhhh (4 bits) Cleared to 0 + // dddd (4 bits) Sample index | after mapping through convInstr + // cccc (4 bits) Effect type | unmodified + // bbbb aaaa (8 bits) Effect value | unmodified + // gggg ffff eeee (12 bits) Sample period | transformed (0xE000 / p) - 256 + // + // MOD: + // hhhh dddd (8 bits) Sample index + // cccc (4 bits) Effect type for this channel/division + // bbbb aaaa (8 bits) Effect value + // gggg ffff eeee (12 bits) Sample period + + // Can we recover the instruments mapping? I don't think so as part of the original instrument index is cleared. + // And it doesn't really matter as long as we are consistent. + // However we need to make sure 31 (or 15 in MS2) is mapped to 0 in MOD. + // We just add 1 to all other values, and this means a 1 <-> 1 mapping for the instruments + for (int p = 0; p < patternNumber; ++p) { + for (int n = 0; n < 64; ++n) { + for (int k = 0; k < 4; ++k) { + int32* l = &(note[p][n][k]); + *l = note2[p][n][k]; + int32 i = 0; + if (nbInstr2 == 22) { // version 1 + i = ((*l & 0xF800) >> 11); + int32 e = ((*l & 0x0700) >> 8); + int32 e1 = invConvEff[e]; + *l &= 0x0FFF00FF; + *l |= (e1 << 8); + } else { // version 2 + int32 h = (*l >> 16); + i = ((*l & 0xF000) >> 12); + *l &= 0x00000FFF; + if (h) + h = 0xE000 / (h + 256); + *l |= (h << 16); + if (i == 15) + i = 31; + } + + // Add back index in note + if (i != 31) { + ++i; + *l |= ((i & 0x0F) << 12); + *l |= ((i & 0xF0) << 24); + } + } + } + } + + for (int i = 0; i < 31; ++i) { + // iname is not stored in the mod file. Just set it to 'instrument#' + // finetune is not stored either. Assume 0. + memset(instr[i].iname, 0, 22); + sprintf(instr[i].iname, "instrument%d", i+1); + instr[i].length = 0; + instr[i].finetune = 0; + instr[i].volume = 0; + instr[i].loopStart = 0; + instr[i].loopLength = 0; + + if (i < nbInstr2) { + instr[i].length = ((instr2[i].end - instr2[i].start) >> 1); + instr[i].loopStart = ((instr2[i].loopStart - instr2[i].start) >> 1); + instr[i].loopLength = (( instr2[i].loopEnd - instr2[i].loopStart) >> 1); + instr[i].volume = instr2[i].volume; + } + } + + // The ciaaSpeed is kind of useless and not present in the MSN file. + // Traditionally 0x78 in SoundTracker. Was used in NoiseTracker as a restart point. + // ProTracker uses 0x7F. FastTracker uses it as a restart point, whereas ScreamTracker 3 uses 0x7F like ProTracker. + // You can use this to roughly detect which tracker made a MOD, and detection gets more accurate for more obscure MOD types. + char ciaaSpeed = 0x7F; + + // The mark cannot be recovered either. Since we have 4 channels and 31 instrument it can be either ID='M.K.' or ID='4CHN'. + // Assume 'M.K.' + const char mark[4] = { 'M', '.', 'K', '.' }; + + Common::MemoryWriteStreamDynamic buffer(DisposeAfterUse::NO); + + buffer.write(msnFile.getName(), 19); + buffer.writeByte(0); + + for (int i = 0 ; i < 31 ; ++i) { + buffer.write(instr[i].iname, 22); + buffer.writeUint16BE(instr[i].length); + buffer.writeByte(instr[i].finetune); + buffer.writeByte(instr[i].volume); + buffer.writeUint16BE(instr[i].loopStart); + buffer.writeUint16BE(instr[i].loopLength); + } + buffer.writeByte((char)songLength); + buffer.writeByte(ciaaSpeed); + buffer.write(arrangement, 128); + buffer.write(mark, 4); + + for (int p = 0 ; p < patternNumber ; ++p) { + for (int n = 0 ; n < 64 ; ++n) { + for (int k = 0 ; k < 4 ; ++k) { +// buffer.writeUint32BE(*((uint32*)(note[p][n]+k))); + buffer.writeSint32BE(note[p][n][k]); + } + } + } + + uint nb; + char buf[4096]; + while ((nb = msnFile.read(buf, 4096)) > 0) + buffer.write(buf, nb); + + return new Common::MemoryReadStream(buffer.getData(), buffer.size(), DisposeAfterUse::YES); +} + +} diff --git a/engines/supernova/resman.h b/engines/supernova/resman.h new file mode 100644 index 0000000000..080ecc5da4 --- /dev/null +++ b/engines/supernova/resman.h @@ -0,0 +1,77 @@ +/* ScummVM - Graphic Adventure Engine +* +* ScummVM is the legal property of its developers, whose names +* are too numerous to list here. Please refer to the COPYRIGHT +* file distributed with this source distribution. +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +* +*/ + +#ifndef SUPERNOVA_RESOURCES_H +#define SUPERNOVA_RESOURCES_H + +#include "audio/audiostream.h" +#include "common/ptr.h" + +#include "supernova/graphics.h" +#include "supernova/sound.h" + + +namespace Common { +class MemoryReadStream; +} + +namespace Supernova { + +class ResourceManager { +public: + enum CursorId { + kCursorNormal, + kCursorWait + }; + +public: + static const int kNumImageFiles = 44; + +public: + ResourceManager(); + + Audio::SeekableAudioStream *getSoundStream(AudioId index); + Audio::AudioStream *getSoundStream(MusicId index); + const MSNImage *getImage(int filenumber) const; + const byte *getImage(CursorId id) const; + +private: + void initSoundFiles(); + void initGraphics(); + void initCursorGraphics(); + void initImages(); + +private: + Common::ScopedPtr<Audio::SeekableAudioStream> _soundSamples[kAudioNumSamples]; + Common::ScopedPtr<Common::MemoryReadStream> _musicIntroBuffer; + Common::ScopedPtr<Common::MemoryReadStream> _musicOutroBuffer; + Common::ScopedPtr<Audio::AudioStream> _musicIntro; + Common::ScopedPtr<Audio::AudioStream> _musicOutro; + int _audioRate; + MSNImage _images[kNumImageFiles]; + byte _cursorNormal[256]; + byte _cursorWait[256]; +}; + +} + +#endif diff --git a/engines/supernova/rooms.cpp b/engines/supernova/rooms.cpp index faa6129723..9360d58aa1 100644 --- a/engines/supernova/rooms.cpp +++ b/engines/supernova/rooms.cpp @@ -24,6 +24,7 @@ #include "graphics/palette.h" #include "graphics/cursorman.h" +#include "supernova/screen.h" #include "supernova/supernova.h" #include "supernova/state.h" @@ -79,15 +80,15 @@ bool Room::deserialize(Common::ReadStream *in, int version) { int numObjects = in->readSint32LE(); for (int i = 0; i < numObjects; ++i) { - _objectState[i]._name = static_cast<StringID>(in->readSint32LE()); - _objectState[i]._description = static_cast<StringID>(in->readSint32LE()); + _objectState[i]._name = static_cast<StringId>(in->readSint32LE()); + _objectState[i]._description = static_cast<StringId>(in->readSint32LE()); _objectState[i]._roomId = in->readByte(); - _objectState[i]._id = static_cast<ObjectID>(in->readSint32LE()); + _objectState[i]._id = static_cast<ObjectId>(in->readSint32LE()); _objectState[i]._type = static_cast<ObjectType>(in->readSint32LE()); _objectState[i]._click = in->readByte(); _objectState[i]._click2 = in->readByte(); _objectState[i]._section = in->readByte(); - _objectState[i]._exitRoom = static_cast<RoomID>(in->readSint32LE()); + _objectState[i]._exitRoom = static_cast<RoomId>(in->readSint32LE()); _objectState[i]._direction = in->readByte(); } @@ -147,102 +148,11 @@ void Intro::onEntrance() { leaveCutscene(); } -class Marquee { -public: - enum MarqueeIndex { - kMarqueeIntro, - kMarqueeOutro - }; - - Marquee(SupernovaEngine *vm, MarqueeIndex id, const char *text) - : _text(text) - , _textBegin(text) - , _delay(0) - , _color(kColorLightBlue) - , _loop(false) - , _vm(vm) - { - if (id == kMarqueeIntro) { - _y = 191; - _loop = true; - } else if (id == kMarqueeOutro) { - _y = 1; - } - _textWidth = _vm->textWidth(_text); - _x = kScreenWidth / 2 - _textWidth / 2; - _vm->_textCursorX = _x; - _vm->_textCursorY = _y; - _vm->_textColor = _color; - } - - void renderCharacter(); - -private: - void clearText(); - - SupernovaEngine *_vm; - MarqueeIndex _id; - const char *const _textBegin; - const char *_text; - bool _loop; - int _delay; - int _color; - int _x; - int _y; - int _textWidth; -}; - -void Marquee::clearText() { - _vm->renderBox(_x, _y - 1, _textWidth + 1, 9, kColorBlack); -} - -void Marquee::renderCharacter() { - if (_delay != 0) { - _delay--; - return; - } - - switch (*_text) { - case '\233': - if (_loop) { - _loop = false; - _text = _textBegin; - clearText(); - _textWidth = _vm->textWidth(_text); - _x = kScreenWidth / 2 - _textWidth / 2; - _vm->_textCursorX = _x; - } - break; - case '\0': - clearText(); - _text++; - _textWidth = _vm->textWidth(_text); - _x = kScreenWidth / 2 - _textWidth / 2; - _vm->_textCursorX = _x; - _color = kColorLightBlue; - _vm->_textColor = _color; - break; - case '^': - _color = kColorLightYellow; - _vm->_textColor = _color; - _text++; - break; - case '#': - _delay = 50; - _text++; - break; - default: - _vm->renderText((uint16)*_text++); - _delay = 1; - break; - } -} - void Intro::titleScreen() { // Newspaper CursorMan.showMouse(false); - _vm->_brightness = 0; - _vm->_menuBrightness = 0; + _vm->_screen->setViewportBrightness(0); + _vm->_screen->setGuiBrightness(0); _vm->paletteBrightness(); _vm->setCurrentImage(1); _vm->renderImage(0); @@ -254,25 +164,25 @@ void Intro::titleScreen() { _vm->setCurrentImage(31); _vm->renderImage(0); _vm->paletteFadeIn(); - _gm->wait2(1); + _gm->wait(1); _vm->playSound(kAudioVoiceSupernova); - while (_vm->_mixer->isSoundHandleActive(_vm->_soundHandle)) - _gm->wait2(1); + while (_vm->_sound->isPlaying()) + _gm->wait(1); titleFadeIn(); _vm->renderText(kStringTitleVersion, 295, 190, kColorWhite44); const Common::String& title1 = _vm->getGameString(kStringTitle1); const Common::String& title2 = _vm->getGameString(kStringTitle2); const Common::String& title3 = _vm->getGameString(kStringTitle3); - _vm->renderText(title1, 78 - _vm->textWidth(title1) / 2, 120, kColorLightBlue); - _vm->renderText(title2, 78 - _vm->textWidth(title2) / 2, 132, kColorWhite99); - _vm->renderText(title3, 78 - _vm->textWidth(title3) / 2, 142, kColorWhite99); - _gm->wait2(1); + _vm->_screen->renderText(title1, 78 - Screen::textWidth(title1) / 2, 120, kColorLightBlue); + _vm->_screen->renderText(title2, 78 - Screen::textWidth(title2) / 2, 132, kColorWhite99); + _vm->_screen->renderText(title3, 78 - Screen::textWidth(title3) / 2, 142, kColorWhite99); + _gm->wait(1); CursorMan.showMouse(true); - _vm->playSoundMod(kMusicIntro); + _vm->playSound(kMusicIntro); - Marquee marquee(_vm, Marquee::kMarqueeIntro, _introText.c_str()); + Marquee marquee(_vm->_screen, Marquee::kMarqueeIntro, _introText.c_str()); while (!_vm->shouldQuit()) { - _vm->updateEvents(); + _gm->updateEvents(); marquee.renderCharacter(); if (_gm->_mouseClicked || _gm->_keyPressed) break; @@ -280,8 +190,8 @@ void Intro::titleScreen() { g_system->delayMillis(_vm->_delay); } _vm->playSound(kAudioVoiceYeah); - while (_vm->_mixer->isSoundHandleActive(_vm->_soundHandle)) - _gm->wait2(1); + while (_vm->_sound->isPlaying()) + _gm->wait(1); _vm->paletteFadeOut(); } @@ -319,7 +229,7 @@ bool Intro::animate(int section1, int section2, int duration) { } bool Intro::animate(int section1, int section2, int duration, - MessagePosition position, StringID textId) { + MessagePosition position, StringId textId) { Common::KeyCode key = Common::KEYCODE_INVALID; const Common::String& text = _vm->getGameString(textId); _vm->renderMessage(text, position); @@ -344,7 +254,7 @@ bool Intro::animate(int section1, int section2, int duration, } bool Intro::animate(int section1, int section2, int section3, int section4, - int duration, MessagePosition position, StringID textId) { + int duration, MessagePosition position, StringId textId) { Common::KeyCode key = Common::KEYCODE_INVALID; const Common::String& text = _vm->getGameString(textId); _vm->renderMessage(text, position); @@ -381,11 +291,11 @@ void Intro::cutscene() { _vm->_system->fillScreen(kColorBlack); _vm->setCurrentImage(31); - _vm->_menuBrightness = 255; + _vm->_screen->setGuiBrightness(255); _vm->paletteBrightness(); if (!animate(0, 0, 0, kMessageNormal, kStringIntroCutscene1)) return; - _vm->_menuBrightness = 0; + _vm->_screen->setGuiBrightness(0); _vm->paletteBrightness(); exitOnEscape(1); @@ -423,7 +333,7 @@ void Intro::cutscene() { exitOnEscape(28); _vm->removeMessage(); - StringID textCounting[4] = + StringId textCounting[4] = {kStringIntroCutscene7, kStringIntroCutscene8, kStringIntroCutscene9, kStringIntroCutscene10}; _vm->setCurrentImage(31); _vm->renderImage(0); @@ -527,15 +437,15 @@ void Intro::cutscene() { return; _vm->paletteFadeOut(); - while (_vm->_mixer->isSoundHandleActive(_vm->_soundHandle)) + while (_vm->_sound->isPlaying()) exitOnEscape(1); _vm->_system->fillScreen(kColorBlack); - _vm->_menuBrightness = 255; + _vm->_screen->setGuiBrightness(255); _vm->paletteBrightness(); if (!animate(0, 0, 0, kMessageNormal, kStringIntroCutscene25)) return; - _vm->_menuBrightness = 5; + _vm->_screen->setGuiBrightness(5); _vm->paletteBrightness(); _vm->setCurrentImage(31); @@ -558,20 +468,20 @@ void Intro::cutscene() { return; CursorMan.showMouse(false); - _vm->_brightness = 0; + _vm->_screen->setViewportBrightness(0); _vm->paletteBrightness(); exitOnEscape(10); _vm->playSound(kAudioSnoring); - while (_vm->_mixer->isSoundHandleActive(_vm->_soundHandle)) - _gm->wait2(1); + while (_vm->_sound->isPlaying()) + _gm->wait(1); exitOnEscape(10); _vm->playSound(kAudioSnoring); - while (_vm->_mixer->isSoundHandleActive(_vm->_soundHandle)) - _gm->wait2(1); + while (_vm->_sound->isPlaying()) + _gm->wait(1); exitOnEscape(10); _vm->playSound(kAudioSnoring); - while (_vm->_mixer->isSoundHandleActive(_vm->_soundHandle)) - _gm->wait2(1); + while (_vm->_sound->isPlaying()) + _gm->wait(1); exitOnEscape(30); CursorMan.showMouse(true); @@ -605,7 +515,7 @@ void Intro::cutscene() { } void Intro::leaveCutscene() { - _vm->_brightness = 255; + _vm->_screen->setViewportBrightness(255); _vm->removeMessage(); _gm->changeRoom(CABIN_R3); _gm->_guiEnabled = true; @@ -620,19 +530,19 @@ bool ShipCorridor::interact(Action verb, Object &obj1, Object &obj2) { _objectState[6].disableProperty(OPENED); _vm->renderImage(8); setSectionVisible(9, false); - _gm->wait2(2); + _gm->wait(2); _vm->renderImage(7); setSectionVisible(8, false); - _gm->wait2(2); + _gm->wait(2); _vm->renderImage(_gm->invertSection(7)); } else { _vm->playSound(kAudioSlideDoor); _objectState[6].setProperty(OPENED); _vm->renderImage(7); - _gm->wait2(2); + _gm->wait(2); _vm->renderImage(8); setSectionVisible(7, false); - _gm->wait2(2); + _gm->wait(2); _vm->renderImage(9); setSectionVisible(8, false); } @@ -649,18 +559,18 @@ bool ShipHall::interact(Action verb, Object &obj1, Object &obj2) { _objectState[2].disableProperty(OPENED); _vm->renderImage(3); setSectionVisible(4, false); - _gm->wait2(2); + _gm->wait(2); _vm->renderImage(2); setSectionVisible(3, false); - _gm->wait2(2); + _gm->wait(2); _vm->renderImage(_gm->invertSection(2)); } else { _objectState[2].setProperty(OPENED); _vm->renderImage(2); - _gm->wait2(2); + _gm->wait(2); _vm->renderImage(3); setSectionVisible(2, false); - _gm->wait2(2); + _gm->wait(2); _vm->renderImage(4); setSectionVisible(3, false); _gm->great(1); @@ -727,13 +637,13 @@ bool ShipSleepCabin::interact(Action verb, Object &obj1, Object &obj2) { if (daysSleep != 0) { _gm->_state._timeSleep = daysSleep; _vm->renderText(kStringShipSleepCabin8, 30, 105, kColorWhite99); - _gm->wait2(18); + _gm->wait(18); setSectionVisible(5, true); } } while (daysSleep == 0); } else { _vm->renderText(kStringShipSleepCabin9, 100, 125, kColorLightRed); - _gm->wait2(18); + _gm->wait(18); } } } @@ -813,12 +723,12 @@ bool ShipSleepCabin::interact(Action verb, Object &obj1, Object &obj2) { _gm->_state._dream = true; _gm->loadTime(); } - _gm->wait2(18); + _gm->wait(18); _vm->paletteFadeIn(); if (_gm->_state._arrivalDaysLeft == 0) { _vm->playSound(kAudioCrash); _gm->screenShake(); - _gm->wait2(18); + _gm->wait(18); _vm->renderMessage(kStringShipSleepCabin12); } } @@ -860,10 +770,10 @@ void ShipSleepCabin::animation() { void ShipSleepCabin::onEntrance() { if (_gm->_state._dream && (_gm->_rooms[CAVE]->getObject(1)->_exitRoom == MEETUP3)) { _vm->renderMessage(kStringShipSleepCabin14); - _gm->waitOnInput(_gm->_timer1); + _gm->waitOnInput(_gm->_messageDuration); _vm->removeMessage(); _vm->renderMessage(kStringShipSleepCabin15); - _gm->waitOnInput(_gm->_timer1); + _gm->waitOnInput(_gm->_messageDuration); _vm->removeMessage(); _vm->renderMessage(kStringShipSleepCabin16); _gm->_state._dream = false; @@ -1004,7 +914,7 @@ bool ShipCabinL3::interact(Action verb, Object &obj1, Object &obj2) { setSectionVisible(15, false); for (int i = 3; i; i--) { _vm->playSound(kAudioTurntable); - while (_vm->_mixer->isSoundHandleActive(_vm->_soundHandle)) { + while (_vm->_sound->isPlaying()) { if (isSectionVisible(13)) { _vm->renderImage(14); setSectionVisible(13, false); @@ -1012,7 +922,7 @@ bool ShipCabinL3::interact(Action verb, Object &obj1, Object &obj2) { _vm->renderImage(13); setSectionVisible(14, false); } - _gm->wait2(3); + _gm->wait(3); } } @@ -1037,7 +947,7 @@ bool ShipCabinL3::interact(Action verb, Object &obj1, Object &obj2) { setSectionVisible(10, false); getObject(10)->_click = 20; } else if ((verb == ACTION_USE) && Object::combine(obj1, obj2, KNIFE, WIRE2)) - _vm->renderMessage(kStringShipCabinL3_4); + _vm->renderMessage(kStringShipCabinL3_4); // cutting near plug else if ((verb == ACTION_USE) && Object::combine(obj1, obj2, KNIFE, WIRE)) { r = _gm->_rooms[AIRLOCK]; if (!isSectionVisible(10) && !r->getObject(5)->hasProperty(WORN)) { @@ -1125,20 +1035,20 @@ bool ShipAirlock::interact(Action verb, Object &obj1, Object &obj2) { if (getObject(0)->hasProperty(OPENED)) { getObject(0)->disableProperty(OPENED); _vm->renderImage(1); - _gm->wait2(2); + _gm->wait(2); _vm->renderImage(2); setSectionVisible(1, false); - _gm->wait2(2); + _gm->wait(2); _vm->renderImage(3); setSectionVisible(2, false); } else { getObject(0)->setProperty(OPENED); _vm->renderImage(2); setSectionVisible(3, false); - _gm->wait2(2); + _gm->wait(2); _vm->renderImage(1); setSectionVisible(2, false); - _gm->wait2(2); + _gm->wait(2); _vm->renderImage(_gm->invertSection(1)); } _vm->renderImage(_gm->invertSection(10)); @@ -1150,53 +1060,53 @@ bool ShipAirlock::interact(Action verb, Object &obj1, Object &obj2) { _vm->playSound(kAudioSlideDoor); getObject(1)->disableProperty(OPENED); _vm->renderImage(4); - _gm->wait2(2); + _gm->wait(2); _vm->renderImage(5); setSectionVisible(4, false); - _gm->wait2(2); + _gm->wait(2); _vm->renderImage(6); setSectionVisible(5, false); _vm->renderImage(16); setSectionVisible(17, false); - _gm->wait2(3); + _gm->wait(3); _vm->renderImage(15); setSectionVisible(16, false); - _gm->wait2(3); + _gm->wait(3); _vm->renderImage(14); setSectionVisible(15, false); - _gm->wait2(3); + _gm->wait(3); _vm->renderImage(13); setSectionVisible(14, false); - _gm->wait2(3); + _gm->wait(3); _vm->renderImage(12); setSectionVisible(13, false); - _gm->wait2(3); + _gm->wait(3); _vm->renderImage(_gm->invertSection(12)); } else { getObject(1)->setProperty(OPENED); _vm->renderImage(12); - _gm->wait2(3); + _gm->wait(3); _vm->renderImage(13); setSectionVisible(12, false); - _gm->wait2(3); + _gm->wait(3); _vm->renderImage(14); setSectionVisible(13, false); - _gm->wait2(3); + _gm->wait(3); _vm->renderImage(15); setSectionVisible(14, false); - _gm->wait2(3); + _gm->wait(3); _vm->renderImage(16); setSectionVisible(15, false); - _gm->wait2(3); + _gm->wait(3); _vm->renderImage(17); setSectionVisible(16, false); _vm->playSound(kAudioSlideDoor); _vm->renderImage(5); setSectionVisible(6, false); - _gm->wait2(2); + _gm->wait(2); _vm->renderImage(4); setSectionVisible(5, false); - _gm->wait2(2); + _gm->wait(2); _vm->renderImage(_gm->invertSection(4)); r = _gm->_rooms[AIRLOCK]; if (!r->getObject(4)->hasProperty(WORN) || @@ -1302,13 +1212,13 @@ bool ShipLandingModule::interact(Action verb, Object &obj1, Object &obj2) { r = _gm->_rooms[SLEEP]; r->setSectionVisible(1, false); r->setSectionVisible(2, false); - _gm->wait2(2); + _gm->wait(2); _vm->renderImage(2); - _gm->wait2(3); + _gm->wait(3); _vm->renderImage(8); - _gm->wait2(2); + _gm->wait(2); _vm->renderImage(9); - _gm->wait2(1); + _gm->wait(1); _vm->renderImage(10); } } @@ -1529,9 +1439,9 @@ bool ArsanoRocks::interact(Action verb, Object &obj1, Object &obj2) { if (((verb == ACTION_PULL) || (verb == ACTION_PRESS)) && (obj1._id == STONE) && !isSectionVisible(3)) { _vm->renderImage(1); - _gm->wait2(2); + _gm->wait(2); _vm->renderImage(2); - _gm->wait2(2); + _gm->wait(2); _vm->renderImage(3); _vm->playSound(kAudioRocks); obj1._click = 3; @@ -1544,10 +1454,10 @@ bool ArsanoRocks::interact(Action verb, Object &obj1, Object &obj2) { void ArsanoMeetup::onEntrance() { if (isSectionVisible(7)) { - _gm->wait2(3); + _gm->wait(3); _vm->renderImage(6); setSectionVisible(7, false); - _gm->wait2(3); + _gm->wait(3); _vm->renderImage(_gm->invertSection(6)); } if (!(_gm->_state._greatFlag & 0x8000)) { @@ -1591,10 +1501,10 @@ bool ArsanoMeetup::interact(Action verb, Object &obj1, Object &obj2) { _vm->paletteBrightness(); } else if ((verb == ACTION_WALK) && (obj1._id == DOOR)) { _vm->renderImage(6); - _gm->wait2(3); + _gm->wait(3); _vm->renderImage(7); setSectionVisible(6, false); - _gm->wait2(3); + _gm->wait(3); return false; } else if ((verb == ACTION_LOOK) && (obj1._id == MEETUP_SIGN) && _gm->_state._language) { @@ -1616,21 +1526,21 @@ bool ArsanoMeetup::interact(Action verb, Object &obj1, Object &obj2) { } void ArsanoEntrance::animation() { - if (!_vm->_messageDisplayed && isSectionVisible(kMaxSection - 5)) { + if (!_vm->_screen->isMessageShown() && isSectionVisible(kMaxSection - 5)) { _gm->animationOff(); // to avoid recursive call _vm->playSound(kAudioSlideDoor); _vm->renderImage(8); setSectionVisible(9, false); - _gm->wait2(2); + _gm->wait(2); _vm->renderImage(7); setSectionVisible(8, false); - _gm->wait2(2); + _gm->wait(2); _vm->renderImage(6); setSectionVisible(7, false); - _gm->wait2(2); + _gm->wait(2); _vm->renderImage(5); setSectionVisible(6, false); - _gm->wait2(2); + _gm->wait(2); _vm->renderImage(_gm->invertSection(5)); getObject(11)->_click = 255; setSectionVisible(kMaxSection - 5, false); @@ -1695,7 +1605,7 @@ bool ArsanoEntrance::interact(Action verb, Object &obj1, Object &obj2) { } } else if ((verb == ACTION_WALK) && (obj1._id == STAIRCASE) && (_gm->_state._shoes != 3)) { _vm->renderImage(3); - _gm->wait2(2); + _gm->wait(2); _vm->renderImage(4); setSectionVisible(3, false); if (_gm->_rooms[AIRLOCK]->getObject(4)->hasProperty(WORN)) @@ -1706,7 +1616,7 @@ bool ArsanoEntrance::interact(Action verb, Object &obj1, Object &obj2) { _gm->reply(kStringArsanoEntrance12, 1, _gm->invertSection(1)); _vm->renderImage(3); setSectionVisible(4, false); - _gm->wait2(2); + _gm->wait(2); _vm->renderImage(_gm->invertSection(3)); if (!_gm->_rooms[AIRLOCK]->getObject(4)->hasProperty(WORN)) { if (_gm->_state._language) { @@ -1732,13 +1642,13 @@ bool ArsanoEntrance::interact(Action verb, Object &obj1, Object &obj2) { break; case 3: _vm->renderImage(3); - _gm->wait2(2); + _gm->wait(2); _vm->renderImage(4); setSectionVisible(3, false); _gm->reply(kStringArsanoEntrance16, 1, 1 + 128); _vm->renderImage(3); setSectionVisible(4, false); - _gm->wait2(2); + _gm->wait(2); _vm->renderImage(_gm->invertSection(3)); break; } @@ -1752,16 +1662,16 @@ bool ArsanoEntrance::interact(Action verb, Object &obj1, Object &obj2) { } else if ((verb == ACTION_PRESS) && (obj1._id == BATHROOM_BUTTON)) { _vm->playSound(kAudioSlideDoor); _vm->renderImage(5); - _gm->wait2(2); + _gm->wait(2); _vm->renderImage(6); setSectionVisible(5, false); - _gm->wait2(2); + _gm->wait(2); _vm->renderImage(7); setSectionVisible(6, false); - _gm->wait2(2); + _gm->wait(2); _vm->renderImage(8); setSectionVisible(7, false); - _gm->wait2(2); + _gm->wait(2); _vm->renderImage(9); setSectionVisible(8, false); getObject(11)->_click = 9; @@ -1782,7 +1692,7 @@ bool ArsanoEntrance::interact(Action verb, Object &obj1, Object &obj2) { _vm->renderMessage(kStringArsanoEntrance20); else { _vm->renderMessage(kStringArsanoEntrance21); - _gm->waitOnInput(_gm->_timer1); + _gm->waitOnInput(_gm->_messageDuration); _vm->removeMessage(); _vm->renderMessage(kStringArsanoEntrance22); _gm->takeObject(*getObject(16)); @@ -1829,7 +1739,7 @@ bool ArsanoEntrance::interact(Action verb, Object &obj1, Object &obj2) { _gm->_rooms[AIRLOCK]->getObject(4)->setProperty(WORN); _gm->_rooms[AIRLOCK]->getObject(5)->setProperty(WORN); _gm->_rooms[AIRLOCK]->getObject(6)->setProperty(WORN); - _gm->waitOnInput(_gm->_timer1); + _gm->waitOnInput(_gm->_messageDuration); _vm->removeMessage(); } return false; @@ -2097,12 +2007,12 @@ bool ArsanoRoger::interact(Action verb, Object &obj1, Object &obj2) { _vm->paletteFadeOut(); _gm->_inventory.remove(*_gm->_rooms[CABIN_R3]->getObject(0)); // Chess board g_system->fillScreen(kColorBlack); - _vm->_menuBrightness = 255; + _vm->_screen->setGuiBrightness(255); _vm->paletteBrightness(); _vm->renderMessage(kStringArsanoRoger39); - _gm->waitOnInput(_gm->_timer1); + _gm->waitOnInput(_gm->_messageDuration); _vm->removeMessage(); - _vm->_menuBrightness = 0; + _vm->_screen->setGuiBrightness(0); _vm->paletteBrightness(); _gm->_state._time += ticksToMsec(125000); // 2 hours _gm->_state._alarmOn = (_gm->_state._timeAlarm > _gm->_state._time); @@ -2117,7 +2027,7 @@ bool ArsanoRoger::interact(Action verb, Object &obj1, Object &obj2) { getObject(6)->_click = 7; _vm->paletteFadeIn(); _vm->renderMessage(kStringArsanoRoger40); - _gm->waitOnInput(_gm->_timer1); + _gm->waitOnInput(_gm->_messageDuration); _vm->removeMessage(); } else return false; @@ -2140,7 +2050,7 @@ bool ArsanoGlider::interact(Action verb, Object &obj1, Object &obj2) { static char l, r; if ((verb == ACTION_USE) && Object::combine(obj1, obj2, KEYCARD_R, GLIDER_SLOT)) { _vm->renderImage(5); - _gm->wait2(7); + _gm->wait(7); _vm->renderImage(8); getObject(5)->_click = 10; _gm->_inventory.remove(*_gm->_rooms[ROGER]->getObject(8)); @@ -2192,7 +2102,7 @@ bool ArsanoGlider::interact(Action verb, Object &obj1, Object &obj2) { } } } - _gm->wait2(4); + _gm->wait(4); _vm->renderImage(_gm->invertSection(i)); } else if ((verb == ACTION_USE) && (obj1._id == GLIDER_BUTTONS)) _vm->renderMessage(kStringArsanoGlider1); @@ -2264,40 +2174,40 @@ bool ArsanoMeetup2::interact(Action verb, Object &obj1, Object &obj2) { _vm->setCurrentImage(13); _vm->renderImage(0); _vm->paletteBrightness(); - _gm->wait2(36); + _gm->wait(36); for (int i = 1; i <= 13; i++) { if (i > 1) _vm->renderImage(_gm->invertSection(i - 1)); _vm->renderImage(i); - _gm->wait2(2); + _gm->wait(2); } _vm->renderImage(_gm->invertSection(13)); - _gm->wait2(20); + _gm->wait(20); _vm->setCurrentImage(14); _vm->renderImage(0); _vm->paletteBrightness(); - _gm->wait2(36); + _gm->wait(36); for (int i = 1; i <= 13; i++) { if (i > 1) _vm->renderImage(_gm->invertSection(i - 1)); _vm->renderImage(i); - _gm->wait2(2); + _gm->wait(2); } _vm->renderImage(_gm->invertSection(13)); - _gm->wait2(9); + _gm->wait(9); _vm->playSound(kAudioCrash); for (int i = 14; i <= 19; i++) { _vm->renderImage(i); - _gm->wait2(3); + _gm->wait(3); } _vm->paletteFadeOut(); _vm->setCurrentImage(11); _vm->renderImage(0); _vm->paletteFadeIn(); - _gm->wait2(18); + _gm->wait(18); _vm->renderMessage(kStringArsanoMeetup2_12); _gm->great(0); - _gm->waitOnInput(_gm->_timer1); + _gm->waitOnInput(_gm->_messageDuration); _vm->removeMessage(); _vm->paletteFadeOut(); g_system->fillScreen(kColorBlack); @@ -2321,14 +2231,14 @@ bool ArsanoMeetup2::interact(Action verb, Object &obj1, Object &obj2) { } void ArsanoMeetup2::shipStart() { - _gm->wait2(12); + _gm->wait(12); for (int i = 2; i <= 11; ++i) { if (i >= 9) _vm->renderImage(i - 1 + 128); else setSectionVisible(i - 1, false); _vm->renderImage(i); - _gm->wait2(2); + _gm->wait(2); } _vm->renderImage(11 + 128); } @@ -2354,34 +2264,34 @@ bool ArsanoMeetup3::interact(Action verb, Object &obj1, Object &obj2) { _vm->paletteBrightness(); _gm->dialog(3, rowsX, _dialogsX, 0); _vm->renderImage(1); - _gm->wait2(3); + _gm->wait(3); _vm->renderImage(2); - _gm->wait2(3); + _gm->wait(3); _vm->renderImage(3); - _gm->wait2(6); + _gm->wait(6); _vm->renderImage(4); _vm->playSound(kAudioGunShot); - while (_vm->_mixer->isSoundHandleActive(_vm->_soundHandle)) - _gm->wait2(1); + while (_vm->_sound->isPlaying()) + _gm->wait(1); _vm->renderImage(5); - _gm->wait2(3); + _gm->wait(3); _vm->renderImage(4); _vm->playSound(kAudioGunShot); - while (_vm->_mixer->isSoundHandleActive(_vm->_soundHandle)) - _gm->wait2(1); + while (_vm->_sound->isPlaying()) + _gm->wait(1); _vm->renderImage(5); _vm->paletteFadeOut(); - _gm->wait2(12); + _gm->wait(12); _vm->setCurrentImage(0); _vm->renderImage(0); _vm->paletteFadeIn(); - _gm->wait2(18); + _gm->wait(18); _gm->reply(kStringArsanoMeetup3_1, 2, 2 + 128); - _gm->wait2(10); + _gm->wait(10); _gm->reply(kStringArsanoMeetup3_2, 1, 1 + 128); do { @@ -2604,8 +2514,8 @@ bool AxacussCell::interact(Action verb, Object &obj1, Object &obj2) { return false; _vm->playSound(kAudioGunShot); - while (_vm->_mixer->isSoundHandleActive(_vm->_soundHandle)) - _gm->wait2(1); + while (_vm->_sound->isPlaying()) + _gm->wait(1); _vm->playSound(kAudioGunShot); _vm->playSound(kAudioGunShot); @@ -2693,26 +2603,29 @@ bool AxacussCorridor5::handleMoneyDialog() { } switch (_gm->dialog(4, _rows, _dialog3, 2)) { case 1: - _gm->wait2(3); + _gm->wait(3); _vm->renderImage(1); _vm->playSound(kAudioVoiceHalt); _vm->renderImage(_gm->invertSection(1)); - _gm->wait2(5); + _gm->wait(5); _vm->renderImage(2); - _gm->wait2(2); + _gm->wait(2); _gm->shot(3, _gm->invertSection(3)); break; - case 3: - if (_gm->_state._money >= 900) { - stopInteract(_gm->_state._money); - return true; - } case 2: if (_gm->_state._money > 1100) { stopInteract(_gm->_state._money - 200); return true; } _gm->reply(kStringAxacussCorridor5_6, 1, 1 + 128); + break; + case 3: + if (_gm->_state._money >= 900) { + stopInteract(_gm->_state._money); + return true; + } + _gm->reply(kStringAxacussCorridor5_6, 1, 1 + 128); + break; } } return false; @@ -2958,7 +2871,7 @@ bool AxacussExit::interact(Action verb, Object &obj1, Object &obj2) { _vm->renderImage(i); if (i == 11) _vm->playSound(kAudioSmash); // 046/4020 - _gm->wait2(1); + _gm->wait(1); _vm->renderImage(i + 128); } _gm->_state._powerOff = true; @@ -3175,9 +3088,9 @@ bool AxacussElevator::interact(Action verb, Object &obj1, Object &obj2) { _vm->renderImage(1); getObject(2)->resetProperty(); _vm->playSound(kAudioSlideDoor); - _gm->wait2(25); + _gm->wait(25); for (int i = 3; i <= 7; i++) { - _gm->wait2(2); + _gm->wait(2); _vm->renderImage(i); } getObject(3)->resetProperty(EXIT); @@ -3195,10 +3108,10 @@ bool AxacussElevator::interact(Action verb, Object &obj1, Object &obj2) { getObject(3)->_click = 255; _vm->playSound(kAudioSlideDoor); for (int i = 7; i >= 3; i--) { - _gm->wait2(2); + _gm->wait(2); _vm->renderImage(_gm->invertSection(i)); } - _gm->wait2(25); + _gm->wait(25); _vm->playSound(kAudioSlideDoor); getObject(2)->resetProperty(EXIT); _vm->renderImage(_gm->invertSection(2)); @@ -3206,12 +3119,12 @@ bool AxacussElevator::interact(Action verb, Object &obj1, Object &obj2) { } else if ((verb == ACTION_WALK) && (obj1._id == JUNGLE)) { _vm->paletteFadeOut(); g_system->fillScreen(kColorBlack); - _vm->_menuBrightness = 255; + _vm->_screen->setGuiBrightness(255); _vm->paletteBrightness(); _vm->renderMessage(kStringAxacussElevator_3); - _gm->waitOnInput(_gm->_timer1); + _gm->waitOnInput(_gm->_messageDuration); _vm->removeMessage(); - _vm->_menuBrightness = 0; + _vm->_screen->setGuiBrightness(0); _vm->paletteBrightness(); _gm->_state._time += ticksToMsec(125000); // 2 hours _gm->_state._alarmOn = (_gm->_state._timeAlarm > _gm->_state._time); @@ -3280,24 +3193,24 @@ void Outro::onEntrance() { _vm->renderImage(0); _vm->renderImage(1); _vm->paletteFadeIn(); - _gm->wait2(10); + _gm->wait(10); for (int i = 8; i <= 21; i++) { _vm->renderImage(i); - _gm->wait2(2); + _gm->wait(2); _vm->renderImage(_gm->invertSection(i)); } - _gm->wait2(18); + _gm->wait(18); _vm->renderImage(_gm->invertSection(1)); for (int i = 2; i <= 7; i++) { _vm->renderImage(i); - _gm->wait2(3); + _gm->wait(3); _vm->renderImage(_gm->invertSection(i)); } - _vm->playSoundMod(kMusicOutro); - Marquee marquee(_vm, Marquee::kMarqueeOutro, _outroText.c_str()); + _vm->playSound(kMusicOutro); + Marquee marquee(_vm->_screen, Marquee::kMarqueeOutro, _outroText.c_str()); while (!_vm->shouldQuit()) { - _vm->updateEvents(); + _gm->updateEvents(); marquee.renderCharacter(); if (_gm->_mouseClicked || _gm->_keyPressed) break; @@ -3310,7 +3223,7 @@ void Outro::onEntrance() { _vm->paletteFadeIn(); _gm->getInput(); _vm->paletteFadeOut(); - _vm->_brightness = 1; + _vm->_screen->setViewportBrightness(1); Common::Event event; event.type = Common::EVENT_RTL; @@ -3324,9 +3237,9 @@ void Outro::animate(int filenumber, int section1, int section2, int duration) { _vm->setCurrentImage(filenumber); while (duration) { _vm->renderImage(section1); - _gm->wait2(2); + _gm->wait(2); _vm->renderImage(section2); - _gm->wait2(2); + _gm->wait(2); --duration; } } @@ -3339,10 +3252,10 @@ void Outro::animate(int filenumber, int section1, int section2, int duration, while (delay) { if (section1) _vm->renderImage(section1); - _gm->wait2(2); + _gm->wait(2); if (section2) _vm->renderImage(section2); - _gm->wait2(2); + _gm->wait(2); --delay; } _vm->removeMessage(); @@ -3358,10 +3271,10 @@ void Outro::animate(int filenumber, int section1, int section2, int section3, in while(duration) { _vm->renderImage(section1); _vm->renderImage(section3); - _gm->wait2(2); + _gm->wait(2); _vm->renderImage(section2); _vm->renderImage(section4); - _gm->wait2(2); + _gm->wait(2); duration--; } _vm->removeMessage(); diff --git a/engines/supernova/rooms.h b/engines/supernova/rooms.h index 3cedc356db..5c9b283dcd 100644 --- a/engines/supernova/rooms.h +++ b/engines/supernova/rooms.h @@ -27,6 +27,11 @@ #include "msn_def.h" +namespace Common { +class ReadStream; +class WriteStream; +} + namespace Supernova { class GameManager; @@ -52,7 +57,7 @@ public: int getFileNumber() const { return _fileNumber; } - RoomID getId() const { + RoomId getId() const { return _id; } @@ -112,7 +117,7 @@ protected: bool _shown[kMaxSection]; byte _sentenceRemoved[kMaxDialog]; Object _objectState[kMaxObject]; - RoomID _id; + RoomId _id; SupernovaEngine *_vm; GameManager *_gm; @@ -129,9 +134,9 @@ public: private: bool animate(int section1, int section2, int duration); bool animate(int section1, int section2, int duration, MessagePosition position, - StringID text); + StringId text); bool animate(int section1, int section2, int section3, int section4, int duration, - MessagePosition position, StringID text); + MessagePosition position, StringId text); void titleScreen(); void titleFadeIn(); @@ -681,9 +686,9 @@ public: virtual void animation(); private: - StringID _dialog1[5]; - StringID _dialog2[5]; - StringID _dialog3[5]; + StringId _dialog1[5]; + StringId _dialog2[5]; + StringId _dialog3[5]; byte _eyewitness; }; class ArsanoRemaining : public Room { @@ -742,7 +747,7 @@ public: virtual bool interact(Action verb, Object &obj1, Object &obj2); private: - StringID _dialog1[4]; + StringId _dialog1[4]; byte _eyewitness; byte _hands; }; @@ -809,10 +814,10 @@ public: private: // TODO: change to 6, fix initialization - StringID _dialog1[2]; - StringID _dialog2[2]; - StringID _dialog3[4]; - StringID _dialog4[3]; + StringId _dialog1[2]; + StringId _dialog2[2]; + StringId _dialog3[4]; + StringId _dialog4[3]; bool _found; bool _flug; @@ -846,11 +851,11 @@ public: virtual bool interact(Action verb, Object &obj1, Object &obj2); private: - StringID _dialog2[4]; - StringID _dialog3[2]; + StringId _dialog2[4]; + StringId _dialog3[2]; // TODO: Hack, to be move away and renamed when the other uses are found - StringID _dialogsX[6]; + StringId _dialogsX[6]; // }; @@ -1025,9 +1030,9 @@ private: bool handleMoneyDialog(); // TODO: Change to 6, or change struct, and fix initialization - StringID _dialog1[2]; - StringID _dialog2[2]; - StringID _dialog3[4]; + StringId _dialog1[2]; + StringId _dialog2[2]; + StringId _dialog3[4]; byte _rows[6]; }; @@ -1176,7 +1181,7 @@ public: virtual bool interact(Action verb, Object &obj1, Object &obj2); private: - StringID _dialogsX[6]; + StringId _dialogsX[6]; }; class AxacussExit : public Room { @@ -1205,7 +1210,7 @@ public: virtual bool interact(Action verb, Object &obj1, Object &obj2); private: - StringID _dialogsX[6]; + StringId _dialogsX[6]; }; class AxacussOffice1 : public Room { public: diff --git a/engines/supernova/screen.cpp b/engines/supernova/screen.cpp new file mode 100644 index 0000000000..2c441acf7b --- /dev/null +++ b/engines/supernova/screen.cpp @@ -0,0 +1,636 @@ +/* ScummVM - Graphic Adventure Engine +* +* ScummVM is the legal property of its developers, whose names +* are too numerous to list here. Please refer to the COPYRIGHT +* file distributed with this source distribution. +* +* This program is free software; you can 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/system.h" +#include "engines/util.h" +#include "graphics/cursorman.h" +#include "graphics/palette.h" +#include "graphics/surface.h" + +#include "supernova/imageid.h" +#include "supernova/resman.h" +#include "supernova/state.h" +#include "supernova/screen.h" +#include "supernova/supernova.h" + +#include "supernova/screenstatic.cpp" + +namespace Supernova { + +ScreenBuffer::ScreenBuffer() + : _x(0) + , _y(0) + , _width(0) + , _height(0) + , _pixels(nullptr) { +} + +ScreenBufferStack::ScreenBufferStack() + : _last(_buffer) { +} + +void ScreenBufferStack::push(int x, int y, int width, int height) { + if (_last == ARRAYEND(_buffer)) + return; + + Graphics::Surface *screenSurface = g_system->lockScreen(); + + if (x < 0) { + width += x; + x = 0; + } + + if (x + width > screenSurface->w) + width = screenSurface->w - x; + + if (y < 0) { + height += y; + y = 0; + } + + if (y + height > screenSurface->h) + height = screenSurface->h - y; + + _last->_pixels = new byte[width * height]; + byte *pixels = _last->_pixels; + const byte *screen = static_cast<const byte *>(screenSurface->getBasePtr(x, y)); + for (int i = 0; i < height; ++i) { + Common::copy(screen, screen + width, pixels); + screen += screenSurface->pitch; + pixels += width; + } + g_system->unlockScreen(); + + _last->_x = x; + _last->_y = y; + _last->_width = width; + _last->_height = height; + + ++_last; +} + +void ScreenBufferStack::restore() { + if (_last == _buffer) + return; + + --_last; + g_system->lockScreen()->copyRectToSurface(_last->_pixels, _last->_width, _last->_x, + _last->_y, _last->_width, _last->_height); + g_system->unlockScreen(); + + delete[] _last->_pixels; +} + +Marquee::Marquee(Screen *screen, MarqueeId id, const char *text) + : _text(text) + , _textBegin(text) + , _delay(0) + , _color(kColorLightBlue) + , _loop(false) + , _screen(screen) { + if (id == kMarqueeIntro) { + _y = 191; + _loop = true; + } else if (id == kMarqueeOutro) { + _y = 1; + } + + _textWidth = Screen::textWidth(_text); + _x = kScreenWidth / 2 - _textWidth / 2; + _screen->_textCursorX = _x; + _screen->_textCursorY = _y; + _screen->_textColor = _color; +} + +void Marquee::clearText() { + _screen->renderBox(_x, _y - 1, _textWidth + 1, 9, kColorBlack); +} + +void Marquee::renderCharacter() { + if (_delay != 0) { + _delay--; + return; + } + + switch (*_text) { + case '\233': + if (_loop) { + _loop = false; + _text = _textBegin; + clearText(); + _textWidth = Screen::textWidth(_text); + _x = kScreenWidth / 2 - _textWidth / 2; + _screen->_textCursorX = _x; + } + break; + case '\0': + clearText(); + _text++; + _textWidth = Screen::textWidth(_text); + _x = kScreenWidth / 2 - _textWidth / 2; + _screen->_textCursorX = _x; + _color = kColorLightBlue; + _screen->_textColor = _color; + break; + case '^': + _color = kColorLightYellow; + _screen->_textColor = _color; + _text++; + break; + case '#': + _delay = 50; + _text++; + break; + default: + _screen->renderText((uint16)*_text++); + _delay = 1; + break; + } +} + +Screen::Screen(SupernovaEngine *vm, GameManager *gm, ResourceManager *resMan) + : _vm(vm) + , _gm(gm) + , _resMan(resMan) + , _currentImage(nullptr) + , _viewportBrightness(255) + , _guiBrightness(255) + , _screenWidth(320) + , _screenHeight(200) + , _textColor(kColorBlack) + , _textCursorX(0) + , _textCursorY(0) + , _messageShown(false) { + + CursorMan.replaceCursor(_resMan->getImage(ResourceManager::kCursorNormal), + 16, 16, 0, 0, kColorCursorTransparent); + CursorMan.replaceCursorPalette(initVGAPalette, 0, 16); + CursorMan.showMouse(true); +} + +int Screen::getGuiBrightness() const { + return _guiBrightness; +} + +void Screen::setViewportBrightness(int brightness) { + _viewportBrightness = brightness; +} + +int Screen::getViewportBrightness() const { + return _viewportBrightness; +} + +void Screen::setGuiBrightness(int brightness) { + _guiBrightness = brightness; +} + +const MSNImage *Screen::getCurrentImage() const { + return _currentImage; +} + +bool Screen::isMessageShown() const { + return _messageShown; +} + +Common::Point Screen::getTextCursorPos() { + return Common::Point(_textCursorX, _textCursorY); +} + +void Screen::setTextCursorPos(int x, int y) { + _textCursorX = x; + _textCursorY = y; +} + +byte Screen::getTextCursorColor() { + return _textColor; +} + +void Screen::setTextCursorColor(byte color) { + _textColor = color; +} + +void Screen::renderMessage(StringId stringId, MessagePosition position, + Common::String var1, Common::String var2) { + Common::String text = _vm->getGameString(stringId); + + if (!var1.empty()) { + if (!var2.empty()) + text = Common::String::format(text.c_str(), var1.c_str(), var2.c_str()); + else + text = Common::String::format(text.c_str(), var1.c_str()); + } + + renderMessage(text, position); +} + +void Screen::renderMessage(const Common::String &text, MessagePosition position) { + if (!text.empty()) + renderMessage(text.c_str(), position); +} + +void Screen::renderText(const uint16 character) { + char text[2]; + text[0] = character & 0xFF; + text[1] = 0; + renderText(text, _textCursorX, _textCursorY, _textColor); +} + +void Screen::renderText(const char *text) { + renderText(text, _textCursorX, _textCursorY, _textColor); +} + +void Screen::renderText(StringId stringId) { + renderText(_vm->getGameString(stringId)); +} + +void Screen::renderText(const Common::String &text) { + if (!text.empty()) + renderText(text.c_str()); +} + +void Screen::renderText(const GuiElement &guiElement) { + renderText(guiElement.getText(), guiElement.getTextPos().x, + guiElement.getTextPos().y, guiElement.getTextColor()); +} + +void Screen::renderText(const uint16 character, int x, int y, byte color) { + char text[2]; + text[0] = character & 0xFF; + text[1] = 0; + renderText(text, x, y, color); +} + +void Screen::renderText(const char *text, int x, int y, byte color) { + Graphics::Surface *screen = _vm->_system->lockScreen(); + byte *cursor = static_cast<byte *>(screen->getBasePtr(x, y)); + const byte *basePtr = cursor; + + byte c; + while ((c = *text++) != '\0') { + if (c < 32) { + continue; + } else if (c == 225) { + c = 128; + } + + for (uint i = 0; i < 5; ++i) { + if (font[c - 32][i] == 0xff) { + break; + } + + byte *ascentLine = cursor; + for (byte j = font[c - 32][i]; j != 0; j >>= 1) { + if (j & 1) { + *cursor = color; + } + cursor += kScreenWidth; + } + cursor = ++ascentLine; + } + ++cursor; + } + _vm->_system->unlockScreen(); + + uint numChars = cursor - basePtr; + uint absPosition = y * kScreenWidth + x + numChars; + _textCursorX = absPosition % kScreenWidth; + _textCursorY = absPosition / kScreenWidth; + _textColor = color; +} + +void Screen::renderText(const Common::String &text, int x, int y, byte color) { + if (!text.empty()) + renderText(text.c_str(), x, y, color); +} + +void Screen::renderText(StringId stringId, int x, int y, byte color) { + renderText(_vm->getGameString(stringId), x, y, color); +} + +void Screen::renderImageSection(const MSNImage *image, int section) { + // Note: inverting means we are removing the section. So we should get the rect for that + // section but draw the background (section 0) instead. + bool invert = false; + if (section > 128) { + section -= 128; + invert = true; + } + if (section > image->_numSections - 1) + return; + + Common::Rect sectionRect(image->_section[section].x1, + image->_section[section].y1, + image->_section[section].x2 + 1, + image->_section[section].y2 + 1); + if (image->_filenumber == 1 || image->_filenumber == 2) { + sectionRect.setWidth(640); + sectionRect.setHeight(480); + if (_screenWidth != 640) { + _screenWidth = 640; + _screenHeight = 480; + initGraphics(_screenWidth, _screenHeight); + } + } else { + if (_screenWidth != 320) { + _screenWidth = 320; + _screenHeight = 200; + initGraphics(_screenWidth, _screenHeight); + } + } + + uint offset = 0; + int pitch = sectionRect.width(); + if (invert) { + pitch = image->_pitch; + offset = image->_section[section].y1 * pitch + + image->_section[section].x1; + section = 0; + } + + void *pixels = image->_sectionSurfaces[section]->getPixels(); + _vm->_system->copyRectToScreen(static_cast<const byte *>(pixels) + offset, + pitch, sectionRect.left, sectionRect.top, + sectionRect.width(), sectionRect.height()); +} + +void Screen::renderImage(ImageId id, bool removeImage) { + ImageInfo info = imageInfo[id]; + const MSNImage *image = _resMan->getImage(info.filenumber); + + if (_currentImage != image) { + _currentImage = image; + _vm->_system->getPaletteManager()->setPalette(image->getPalette(), 16, 239); + } + + do { + if (removeImage) + renderImageSection(image, info.section + 128); + else + renderImageSection(image, info.section); + + info.section = image->_section[info.section].next; + } while (info.section != 0); +} + +void Screen::renderImage(int section) { + if (!_currentImage) + return; + + bool sectionVisible = true; + + if (section > 128) { + sectionVisible = false; + section -= 128; + } + + _gm->_currentRoom->setSectionVisible(section, sectionVisible); + + do { + if (sectionVisible) + renderImageSection(_currentImage, section); + else + renderImageSection(_currentImage, section + 128); + section = _currentImage->_section[section].next; + } while (section != 0); +} + +bool Screen::setCurrentImage(int filenumber) { + _currentImage = _resMan->getImage(filenumber); + _vm->_system->getPaletteManager()->setPalette(_currentImage->getPalette(), 16, 239); + paletteBrightness(); + + return true; +} + +void Screen::saveScreen(int x, int y, int width, int height) { + _screenBuffer.push(x, y, width, height); +} + +void Screen::saveScreen(const GuiElement &guiElement) { + saveScreen(guiElement.left, guiElement.top, guiElement.width(), guiElement.height()); +} + +void Screen::restoreScreen() { + _screenBuffer.restore(); +} + +void Screen::renderRoom(Room &room) { + if (room.getId() == INTRO) + return; + + if (setCurrentImage(room.getFileNumber())) { + for (int i = 0; i < _currentImage->_numSections; ++i) { + int section = i; + if (room.isSectionVisible(section)) { + do { + renderImageSection(_currentImage, section); + section = _currentImage->_section[section].next; + } while (section != 0); + } + } + } +} + +int Screen::textWidth(const uint16 key) { + char text[2]; + text[0] = key & 0xFF; + text[1] = 0; + return textWidth(text); +} + +int Screen::textWidth(const char *text) { + int charWidth = 0; + while (*text != '\0') { + byte c = *text++; + if (c < 32) { + continue; + } else if (c == 225) { + c = 35; + } + + for (uint i = 0; i < 5; ++i) { + if (font[c - 32][i] == 0xff) { + break; + } + ++charWidth; + } + ++charWidth; + } + + return charWidth; +} + +int Screen::textWidth(const Common::String &text) { + return Screen::textWidth(text.c_str()); +} + +void Screen::renderMessage(const char *text, MessagePosition position) { + Common::String t(text); + char *row[20]; + Common::String::iterator p = t.begin(); + uint numRows = 0; + int rowWidthMax = 0; + int x = 0; + int y = 0; + byte textColor = 0; + + while (*p != '\0') { + row[numRows] = p; + ++numRows; + while ((*p != '\0') && (*p != '|')) { + ++p; + } + if (*p == '|') { + *p = '\0'; + ++p; + } + } + for (uint i = 0; i < numRows; ++i) { + int rowWidth = textWidth(row[i]); + if (rowWidth > rowWidthMax) + rowWidthMax = rowWidth; + } + + switch (position) { + case kMessageNormal: + x = 160 - rowWidthMax / 2; + textColor = kColorWhite99; + break; + case kMessageTop: + x = 160 - rowWidthMax / 2; + textColor = kColorLightYellow; + break; + case kMessageCenter: + x = 160 - rowWidthMax / 2; + textColor = kColorLightRed; + break; + case kMessageLeft: + x = 3; + textColor = kColorLightYellow; + break; + case kMessageRight: + x = 317 - rowWidthMax; + textColor = kColorLightGreen; + break; + } + + if (position == kMessageNormal) { + y = 70 - ((numRows * 9) / 2); + } else if (position == kMessageTop) { + y = 5; + } else { + y = 142; + } + + int message_columns = x - 3; + int message_rows = y - 3; + int message_width = rowWidthMax + 6; + int message_height = numRows * 9 + 5; + saveScreen(message_columns, message_rows, message_width, message_height); + renderBox(message_columns, message_rows, message_width, message_height, kColorWhite35); + for (uint i = 0; i < numRows; ++i) { + renderText(row[i], x, y, textColor); + y += 9; + } + + _messageShown = true; + _gm->_messageDuration = (Common::strnlen(text, 512) + 20) * _vm->_textSpeed / 10; +} + +void Screen::removeMessage() { + if (_messageShown) { + restoreScreen(); + _messageShown = false; + } +} + +void Screen::renderBox(int x, int y, int width, int height, byte color) { + Graphics::Surface *screen = _vm->_system->lockScreen(); + screen->fillRect(Common::Rect(x, y, x + width, y + height), color); + _vm->_system->unlockScreen(); +} + +void Screen::renderBox(const GuiElement &guiElement) { + renderBox(guiElement.left, guiElement.top, guiElement.width(), + guiElement.height(), guiElement.getBackgroundColor()); +} + +void Screen::initPalette() { + g_system->getPaletteManager()->setPalette(initVGAPalette, 0, 256); +} + +void Screen::paletteBrightness() { + byte palette[768]; + + _vm->_system->getPaletteManager()->grabPalette(palette, 0, 255); + for (uint i = 0; i < 48; ++i) { + palette[i] = (initVGAPalette[i] * _guiBrightness) >> 8; + } + for (uint i = 0; i < 717; ++i) { + const byte *imagePalette; + if (_currentImage && _currentImage->getPalette()) { + imagePalette = _currentImage->getPalette(); + } else { + imagePalette = palette + 48; + } + palette[i + 48] = (imagePalette[i] * _viewportBrightness) >> 8; + } + _vm->_system->getPaletteManager()->setPalette(palette, 0, 255); +} + +void Screen::paletteFadeOut() { + while (_guiBrightness > 10) { + _guiBrightness -= 10; + if (_viewportBrightness > _guiBrightness) + _viewportBrightness = _guiBrightness; + paletteBrightness(); + _vm->_system->updateScreen(); + _vm->_system->delayMillis(_vm->_delay); + } + _guiBrightness = 0; + _viewportBrightness = 0; + paletteBrightness(); + _vm->_system->updateScreen(); +} + +void Screen::paletteFadeIn() { + while (_guiBrightness < 245) { + if (_viewportBrightness < _gm->_roomBrightness) + _viewportBrightness += 10; + _guiBrightness += 10; + paletteBrightness(); + _vm->_system->updateScreen(); + _vm->_system->delayMillis(_vm->_delay); + } + _guiBrightness = 255; + _viewportBrightness = _gm->_roomBrightness; + paletteBrightness(); + _vm->_system->updateScreen(); +} + +void Screen::setColor63(byte value) { + byte color[3] = {value, value, value}; + _vm->_system->getPaletteManager()->setPalette(color, 63, 1); +} + +} diff --git a/engines/supernova/screen.h b/engines/supernova/screen.h new file mode 100644 index 0000000000..d57fb53ed2 --- /dev/null +++ b/engines/supernova/screen.h @@ -0,0 +1,197 @@ +/* ScummVM - Graphic Adventure Engine +* +* ScummVM is the legal property of its developers, whose names +* are too numerous to list here. Please refer to the COPYRIGHT +* file distributed with this source distribution. +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 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 SUPERNOVA_SCREEN_H +#define SUPERNOVA_SCREEN_H + +#include "common/array.h" +#include "common/rect.h" +#include "common/scummsys.h" + +#include "supernova/imageid.h" +#include "supernova/msn_def.h" + +namespace Supernova { + +class SupernovaEngine; +class GameManager; +class ResourceManager; +class GuiElement; +class Room; +class MSNImage; +class Screen; + +const int kScreenWidth = 320; +const int kScreenHeight = 200; +const int kFontWidth = 5; +const int kFontHeight = 8; + +enum Color { + kColorBlack = 0, + kColorWhite25 = 1, + kColorWhite35 = 2, + kColorWhite44 = 3, + kColorWhite99 = 4, + kColorDarkGreen = 5, + kColorGreen = 6, + kColorDarkRed = 7, + kColorRed = 8, + kColorDarkBlue = 9, + kColorBlue = 10, + kColorWhite63 = 11, + kColorLightBlue = 12, + kColorLightGreen = 13, + kColorLightYellow = 14, + kColorLightRed = 15, + kColorCursorTransparent = kColorWhite25 +}; + +class ScreenBuffer { + friend class ScreenBufferStack; + +public: + ScreenBuffer(); + +private: + byte *_pixels; + int _x; + int _y; + int _width; + int _height; +}; + +class ScreenBufferStack { +public: + ScreenBufferStack(); + + void push(int x, int y, int width, int height); + void restore(); + +private: + ScreenBuffer _buffer[8]; + ScreenBuffer *_last; +}; + +class Marquee { +public: + enum MarqueeId { + kMarqueeIntro, + kMarqueeOutro + }; + + Marquee(Screen *screen, MarqueeId id, const char *text); + + void renderCharacter(); + +private: + void clearText(); + + Screen *_screen; + const char *const _textBegin; + const char *_text; + bool _loop; + int _delay; + int _color; + int _x; + int _y; + int _textWidth; +}; + +class Screen { + friend class Marquee; + +public: + struct ImageInfo { + int filenumber; + int section; + }; + +public: + static void initPalette(); + static int textWidth(const uint16 key); + static int textWidth(const char *text); + static int textWidth(const Common::String &text); + +public: + Screen(SupernovaEngine *vm, GameManager *gm, ResourceManager *resMan); + + int getViewportBrightness() const; + void setViewportBrightness(int brightness); + int getGuiBrightness() const; + void setGuiBrightness(int brightness); + const MSNImage *getCurrentImage() const; + bool isMessageShown() const; + void paletteFadeIn(); + void paletteFadeOut(); + void paletteBrightness(); + void renderImage(ImageId id, bool removeImage = false); + void renderImage(int section); + bool setCurrentImage(int filenumber); + void saveScreen(int x, int y, int width, int height); + void saveScreen(const GuiElement &guiElement); + void restoreScreen(); + void renderRoom(Room &room); + void renderMessage(const char *text, MessagePosition position = kMessageNormal); + void renderMessage(const Common::String &text, MessagePosition position = kMessageNormal); + void renderMessage(StringId stringId, MessagePosition position = kMessageNormal, + Common::String var1 = "", Common::String var2 = ""); + void removeMessage(); + void renderText(const uint16 character); + void renderText(const char *text); + void renderText(const Common::String &text); + void renderText(StringId stringId); + void renderText(const uint16 character, int x, int y, byte color); + void renderText(const char *text, int x, int y, byte color); + void renderText(const Common::String &text, int x, int y, byte color); + void renderText(StringId stringId, int x, int y, byte color); + void renderText(const GuiElement &guiElement); + void renderBox(int x, int y, int width, int height, byte color); + void renderBox(const GuiElement &guiElement); + void setColor63(byte value); + Common::Point getTextCursorPos(); + void setTextCursorPos(int x, int y); + byte getTextCursorColor(); + void setTextCursorColor(byte color); + void update(); + +private: + void renderImageSection(const MSNImage *image, int section); + +private: + SupernovaEngine *_vm; + GameManager *_gm; + ResourceManager *_resMan; + const MSNImage *_currentImage; + ScreenBufferStack _screenBuffer; + int _screenWidth; + int _screenHeight; + int _textCursorX; + int _textCursorY; + int _textColor; + byte _viewportBrightness; + byte _guiBrightness; + bool _messageShown; +}; + +} + +#endif diff --git a/engines/supernova/screenstatic.cpp b/engines/supernova/screenstatic.cpp new file mode 100644 index 0000000000..db987bf2fb --- /dev/null +++ b/engines/supernova/screenstatic.cpp @@ -0,0 +1,328 @@ +/* ScummVM - Graphic Adventure Engine +* +* ScummVM is the legal property of its developers, whose names +* are too numerous to list here. Please refer to the COPYRIGHT +* file distributed with this source distribution. +* +* This program is free software; you can 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. +* +*/ + +namespace Supernova { + +static const Screen::ImageInfo imageInfo[] = { + { 0, 0}, { 0, 1}, { 0, 2}, + { 1, 0}, + { 2, 0}, + { 3, 0}, { 3, 1}, { 3, 2}, { 3, 3}, { 3, 4}, { 3, 5}, + { 3, 6}, { 3, 7}, { 3, 8}, { 3, 9}, { 3, 10}, { 3, 11}, + { 4, 0}, { 4, 1}, { 4, 2}, { 4, 3}, + { 5, 0}, { 5, 1}, { 5, 2}, { 5, 3}, { 5, 4}, { 5, 5}, + { 5, 6}, { 5, 7}, { 5, 8}, { 5, 9}, + { 6, 0}, { 6, 1}, { 6, 2}, { 6, 3}, { 6, 4}, { 6, 5}, + { 6, 6}, { 6, 7}, { 6, 8}, { 6, 9}, { 6, 10}, { 6, 11}, + { 6, 12}, { 6, 13}, { 6, 14}, { 6, 15}, { 6, 16}, { 6, 17}, + { 6, 18}, { 6, 19}, { 6, 20}, { 6, 21}, + { 7, 0}, { 7, 1}, { 7, 2}, { 7, 3}, { 7, 4}, { 7, 5}, + { 7, 6}, { 7, 7}, { 7, 8}, { 7, 9}, { 7, 10}, { 7, 11}, + { 7, 12}, { 7, 13}, { 7, 14}, { 7, 15}, { 7, 16}, { 7, 17}, + { 7, 18}, { 7, 19}, { 7, 20}, { 7, 21}, { 7, 22}, + { 8, 0}, { 8, 1}, { 8, 2}, { 8, 3}, { 8, 4}, { 8, 5}, + { 8, 6}, { 8, 7}, { 8, 8}, { 8, 9}, { 8, 10}, { 8, 11}, + { 8, 12}, + { 9, 0}, { 9, 1}, { 9, 2}, { 9, 3}, { 9, 4}, { 9, 5}, + { 9, 6}, { 9, 7}, { 9, 8}, { 9, 9}, { 9, 10}, { 9, 11}, + { 9, 12}, { 9, 13}, { 9, 14}, { 9, 15}, { 9, 16}, { 9, 17}, + { 9, 18}, { 9, 19}, { 9, 20}, { 9, 21}, { 9, 22}, { 9, 23}, + {10, 0}, {10, 1}, {10, 2}, {10, 3}, {10, 4}, {10, 5}, + {10, 6}, {10, 7}, {10, 8}, {10, 9}, {10, 10}, {10, 11}, + {10, 12}, {10, 13}, {10, 14}, {10, 15}, {10, 16}, + {11, 0}, + {12, 0}, {12, 1}, {12, 2}, {12, 3}, + {13, 0}, {13, 1}, {13, 2}, {13, 3}, {13, 4}, {13, 5}, + {13, 6}, {13, 7}, {13, 8}, {13, 9}, {13, 10}, {13, 11}, + {13, 12}, {13, 13}, + {14, 0}, {14, 1}, {14, 2}, {14, 3}, {14, 4}, {14, 5}, + {14, 6}, {14, 7}, {14, 8}, {14, 9}, {14, 10}, {14, 11}, + {14, 12}, {14, 13}, {14, 14}, {14, 15}, {14, 16}, {14, 17}, + {14, 18}, {14, 19}, + {15, 0}, {15, 1}, {15, 2}, {15, 3}, {15, 4}, {15, 5}, + {16, 0}, {16, 1}, {16, 2}, {16, 3}, {16, 4}, {16, 5}, + {16, 6}, {16, 7}, {16, 8}, {16, 9}, {16, 10}, {16, 11}, + {16, 12}, {16, 13}, {16, 14}, {16, 15}, {16, 16}, {16, 17}, + {16, 18}, {16, 19}, {16, 20}, {16, 21}, {16, 22}, {16, 23}, + {16, 24}, {16, 25}, {16, 26}, {16, 27}, {16, 28}, {16, 29}, + {16, 30}, {16, 31}, {16, 32}, {16, 33}, + {17, 0}, {17, 1}, {17, 2}, {17, 3}, {17, 4}, {17, 5}, + {17, 6}, {17, 7}, {17, 8}, {17, 9}, {17, 10}, {17, 11}, + {17, 12}, {17, 13}, {17, 14}, {17, 15}, + {18, 0}, {18, 1}, {18, 2}, {18, 3}, {18, 4}, {18, 5}, + {18, 6}, {18, 7}, {18, 8}, {18, 9}, {18, 10}, {18, 11}, + {18, 12}, {18, 13}, {18, 14}, {18, 15}, {18, 16}, {18, 17}, + {18, 18}, {18, 19}, {18, 20}, {18, 21}, + {19, 0}, {19, 1}, {19, 2}, {19, 3}, {19, 4}, {19, 5}, + {19, 6}, {19, 7}, {19, 8}, {19, 9}, {19, 10}, {19, 11}, + {19, 12}, {19, 13}, {19, 14}, {19, 15}, {19, 16}, {19, 17}, + {19, 18}, {19, 19}, {19, 20}, {19, 21}, {19, 22}, {19, 23}, + {19, 24}, {19, 25}, {19, 26}, {19, 27}, {19, 28}, {19, 29}, + {19, 30}, {19, 31}, {19, 32}, {19, 33}, {19, 34}, {19, 35}, + {19, 36}, {19, 37}, {19, 38}, {19, 39}, {19, 40}, + {20, 0}, + {21, 0}, {21, 1}, {21, 2}, {21, 3}, {21, 4}, {21, 5}, + {21, 6}, {21, 7}, {21, 8}, {21, 9}, {21, 10}, {21, 11}, + {21, 12}, {21, 13}, {21, 14}, {21, 15}, {21, 16}, {21, 17}, + {21, 18}, {21, 19}, {21, 20}, {21, 21}, {21, 22}, {21, 23}, + {21, 24}, {21, 25}, {21, 26}, {21, 27}, {21, 28}, {21, 29}, + {21, 30}, {21, 31}, {21, 32}, {21, 33}, {21, 34}, {21, 35}, + {21, 36}, {21, 37}, {21, 38}, + {22, 0}, {22, 1}, {22, 2}, {22, 3}, {22, 4}, {22, 5}, + {22, 6}, {22, 7}, {22, 8}, {22, 9}, {22, 10}, {22, 11}, + {22, 12}, {22, 13}, {22, 14}, {22, 15}, {22, 16}, {22, 17}, + {22, 18}, + {23, 0}, + {24, 0}, {24, 1}, {24, 2}, {24, 3}, {24, 4}, {24, 5}, + {24, 6}, {24, 7}, {24, 8}, + {25, 0}, {25, 1}, {25, 2}, {25, 3}, {25, 4}, {25, 5}, + {25, 6}, {25, 7}, {25, 8}, {25, 9}, {25, 10}, {25, 11}, + {25, 12}, {25, 13}, {25, 14}, {25, 15}, {25, 16}, {25, 17}, + {25, 18}, + {26, 0}, + {27, 0}, + {28, 0}, {28, 1}, {28, 2}, {28, 3}, {28, 4}, {28, 5}, + {28, 6}, {28, 7}, {28, 8}, {28, 9}, {28, 10}, {28, 11}, + {28, 12}, {28, 13}, {28, 14}, {28, 15}, {28, 16}, {28, 17}, + {28, 18}, {28, 19}, {28, 20}, {28, 21}, {28, 22}, + {29, 0}, {29, 1}, {29, 2}, {29, 3}, {29, 4}, {29, 5}, + {29, 6}, {29, 7}, {29, 8}, {29, 9}, {29, 10}, {29, 11}, + {29, 12}, {29, 13}, + {30, 0}, {30, 1}, + {31, 0}, {31, 1}, {31, 2}, {31, 3}, {31, 4}, {31, 5}, + {31, 6}, + {32, 0}, {32, 1}, {32, 2}, {32, 3}, + {33, 0}, {33, 1}, {33, 2}, {33, 3}, {33, 4}, {33, 5}, + {34, 0}, {34, 1}, {34, 2}, {34, 3}, {34, 4}, {34, 5}, + {34, 6}, {34, 7}, {34, 8}, {34, 9}, {34, 10}, {34, 11}, + {34, 12}, {34, 13}, {34, 14}, {34, 15}, {34, 16}, {34, 17}, + {35, 0}, {35, 1}, {35, 2}, {35, 3}, {35, 4}, {35, 5}, + {35, 6}, {35, 7}, {35, 8}, {35, 9}, {35, 10}, {35, 11}, + {35, 12}, {35, 13}, {35, 14}, {35, 15}, {35, 16}, {35, 17}, + {35, 18}, {35, 19}, {35, 20}, {35, 21}, + {36, 0}, {36, 1}, {36, 2}, {36, 3}, {36, 4}, {36, 5}, + {37, 0}, {37, 1}, {37, 2}, {37, 3}, {37, 4}, {37, 5}, + {37, 6}, {37, 7}, {37, 8}, {37, 9}, {37, 10}, {37, 11}, + {37, 12}, {37, 13}, {37, 14}, {37, 15}, {37, 16}, {37, 17}, + {37, 18}, {37, 19}, {37, 20}, {37, 21}, {37, 22}, {37, 23}, + {37, 24}, {37, 25}, {37, 26}, + {38, 0}, {38, 1}, {38, 2}, {38, 3}, {38, 4}, {38, 5}, + {38, 6}, {38, 7}, {38, 8}, {38, 9}, {38, 10}, {38, 11}, + {38, 12}, + {39, 0}, + {40, 0}, {40, 1}, {40, 2}, {40, 3}, {40, 4}, {40, 5}, + {40, 6}, {40, 7}, + {41, 0}, {41, 1}, {41, 2}, {41, 3}, + {42, 0}, {42, 1}, {42, 2}, {42, 3}, {42, 4}, {42, 5}, + {42, 6}, {42, 7}, {42, 8}, {42, 9}, {42, 10}, {42, 11}, + {43, 0}, {43, 1}, {43, 2}, {43, 3}, {43, 4}, {43, 5}, + {43, 6}, {43, 7}, {43, 8}, {43, 9}, {43, 10}, {43, 11}, + {43, 12}, {43, 13}, {43, 14}, {43, 15}, {43, 16}, {43, 17}, + {43, 18}, {43, 19}, {43, 20}, {43, 21}, {43, 22}, {43, 23}, + {43, 24}, {43, 25}, {43, 26}, {43, 27}, {43, 28}, {43, 29}, + {43, 30}, {43, 31} +}; + +// Default palette +static const byte initVGAPalette[768] = { + 0x00, 0x00, 0x00, 0x40, 0x40, 0x40, 0x58, 0x58, 0x58, 0x70, 0x70, 0x70, 0xfc, 0xfc, 0xfc, 0x00, 0xd0, 0x00, + 0x00, 0xfc, 0x00, 0xd8, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0xb0, 0xa0, 0xa0, 0xa0, + 0x50, 0xc8, 0xfc, 0x28, 0xfc, 0x28, 0xf0, 0xf0, 0x00, 0xfc, 0x28, 0x28, 0x00, 0x00, 0x00, 0x14, 0x14, 0x14, + 0x20, 0x20, 0x20, 0x2c, 0x2c, 0x2c, 0x38, 0x38, 0x38, 0x44, 0x44, 0x44, 0x50, 0x50, 0x50, 0x60, 0x60, 0x60, + 0x70, 0x70, 0x70, 0x80, 0x80, 0x80, 0x90, 0x90, 0x90, 0xa0, 0xa0, 0xa0, 0xb4, 0xb4, 0xb4, 0xc8, 0xc8, 0xc8, + 0xe0, 0xe0, 0xe0, 0xfc, 0xfc, 0xfc, 0x00, 0x00, 0xfc, 0x40, 0x00, 0xfc, 0x7c, 0x00, 0xfc, 0xbc, 0x00, 0xfc, + 0xfc, 0x00, 0xfc, 0xfc, 0x00, 0xbc, 0xfc, 0x00, 0x7c, 0xfc, 0x00, 0x40, 0xfc, 0x00, 0x00, 0xfc, 0x40, 0x00, + 0xfc, 0x7c, 0x00, 0xfc, 0xbc, 0x00, 0xfc, 0xfc, 0x00, 0xbc, 0xfc, 0x00, 0x7c, 0xfc, 0x00, 0x40, 0xfc, 0x00, + 0x00, 0xfc, 0x00, 0x00, 0xfc, 0x40, 0x00, 0xfc, 0x7c, 0x00, 0xfc, 0xbc, 0x00, 0xfc, 0xfc, 0x00, 0xbc, 0xfc, + 0x00, 0x7c, 0xfc, 0x00, 0x40, 0xfc, 0x7c, 0x7c, 0xfc, 0x9c, 0x7c, 0xfc, 0xbc, 0x7c, 0xfc, 0xdc, 0x7c, 0xfc, + 0xfc, 0x7c, 0xfc, 0xfc, 0x7c, 0xdc, 0xfc, 0x7c, 0xbc, 0xfc, 0x7c, 0x9c, 0xfc, 0x7c, 0x7c, 0xfc, 0x9c, 0x7c, + 0xfc, 0xbc, 0x7c, 0xfc, 0xdc, 0x7c, 0xfc, 0xfc, 0x7c, 0xdc, 0xfc, 0x7c, 0xbc, 0xfc, 0x7c, 0x9c, 0xfc, 0x7c, + 0x7c, 0xfc, 0x7c, 0x7c, 0xfc, 0x9c, 0x7c, 0xfc, 0xbc, 0x7c, 0xfc, 0xdc, 0x7c, 0xfc, 0xfc, 0x7c, 0xdc, 0xfc, + 0x7c, 0xbc, 0xfc, 0x7c, 0x9c, 0xfc, 0xb4, 0xb4, 0xfc, 0xc4, 0xb4, 0xfc, 0xd8, 0xb4, 0xfc, 0xe8, 0xb4, 0xfc, + 0xfc, 0xb4, 0xfc, 0xfc, 0xb4, 0xe8, 0xfc, 0xb4, 0xd8, 0xfc, 0xb4, 0xc4, 0xfc, 0xb4, 0xb4, 0xfc, 0xc4, 0xb4, + 0xfc, 0xd8, 0xb4, 0xfc, 0xe8, 0xb4, 0xfc, 0xfc, 0xb4, 0xe8, 0xfc, 0xb4, 0xd8, 0xfc, 0xb4, 0xc4, 0xfc, 0xb4, + 0xb4, 0xfc, 0xb4, 0xb4, 0xfc, 0xc4, 0xb4, 0xfc, 0xd8, 0xb4, 0xfc, 0xe8, 0xb4, 0xfc, 0xfc, 0xb4, 0xe8, 0xfc, + 0xb4, 0xd8, 0xfc, 0xb4, 0xc4, 0xfc, 0x00, 0x00, 0x70, 0x1c, 0x00, 0x70, 0x38, 0x00, 0x70, 0x54, 0x00, 0x70, + 0x70, 0x00, 0x70, 0x70, 0x00, 0x54, 0x70, 0x00, 0x38, 0x70, 0x00, 0x1c, 0x70, 0x00, 0x00, 0x70, 0x1c, 0x00, + 0x70, 0x38, 0x00, 0x70, 0x54, 0x00, 0x70, 0x70, 0x00, 0x54, 0x70, 0x00, 0x38, 0x70, 0x00, 0x1c, 0x70, 0x00, + 0x00, 0x70, 0x00, 0x00, 0x70, 0x1c, 0x00, 0x70, 0x38, 0x00, 0x70, 0x54, 0x00, 0x70, 0x70, 0x00, 0x54, 0x70, + 0x00, 0x38, 0x70, 0x00, 0x1c, 0x70, 0x38, 0x38, 0x70, 0x44, 0x38, 0x70, 0x54, 0x38, 0x70, 0x60, 0x38, 0x70, + 0x70, 0x38, 0x70, 0x70, 0x38, 0x60, 0x70, 0x38, 0x54, 0x70, 0x38, 0x44, 0x70, 0x38, 0x38, 0x70, 0x44, 0x38, + 0x70, 0x54, 0x38, 0x70, 0x60, 0x38, 0x70, 0x70, 0x38, 0x60, 0x70, 0x38, 0x54, 0x70, 0x38, 0x44, 0x70, 0x38, + 0x38, 0x70, 0x38, 0x38, 0x70, 0x44, 0x38, 0x70, 0x54, 0x38, 0x70, 0x60, 0x38, 0x70, 0x70, 0x38, 0x60, 0x70, + 0x38, 0x54, 0x70, 0x38, 0x44, 0x70, 0x50, 0x50, 0x70, 0x58, 0x50, 0x70, 0x60, 0x50, 0x70, 0x68, 0x50, 0x70, + 0x70, 0x50, 0x70, 0x70, 0x50, 0x68, 0x70, 0x50, 0x60, 0x70, 0x50, 0x58, 0x70, 0x50, 0x50, 0x70, 0x58, 0x50, + 0x70, 0x60, 0x50, 0x70, 0x68, 0x50, 0x70, 0x70, 0x50, 0x68, 0x70, 0x50, 0x60, 0x70, 0x50, 0x58, 0x70, 0x50, + 0x50, 0x70, 0x50, 0x50, 0x70, 0x58, 0x50, 0x70, 0x60, 0x50, 0x70, 0x68, 0x50, 0x70, 0x70, 0x50, 0x68, 0x70, + 0x50, 0x60, 0x70, 0x50, 0x58, 0x70, 0x00, 0x00, 0x40, 0x10, 0x00, 0x40, 0x20, 0x00, 0x40, 0x30, 0x00, 0x40, + 0x40, 0x00, 0x40, 0x40, 0x00, 0x30, 0x40, 0x00, 0x20, 0x40, 0x00, 0x10, 0x40, 0x00, 0x00, 0x40, 0x10, 0x00, + 0x40, 0x20, 0x00, 0x40, 0x30, 0x00, 0x40, 0x40, 0x00, 0x30, 0x40, 0x00, 0x20, 0x40, 0x00, 0x10, 0x40, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x40, 0x10, 0x00, 0x40, 0x20, 0x00, 0x40, 0x30, 0x00, 0x40, 0x40, 0x00, 0x30, 0x40, + 0x00, 0x20, 0x40, 0x00, 0x10, 0x40, 0x20, 0x20, 0x40, 0x28, 0x20, 0x40, 0x30, 0x20, 0x40, 0x38, 0x20, 0x40, + 0x40, 0x20, 0x40, 0x40, 0x20, 0x38, 0x40, 0x20, 0x30, 0x40, 0x20, 0x28, 0x40, 0x20, 0x20, 0x40, 0x28, 0x20, + 0x40, 0x30, 0x20, 0x40, 0x38, 0x20, 0x40, 0x40, 0x20, 0x38, 0x40, 0x20, 0x30, 0x40, 0x20, 0x28, 0x40, 0x20, + 0x20, 0x40, 0x20, 0x20, 0x40, 0x28, 0x20, 0x40, 0x30, 0x20, 0x40, 0x38, 0x20, 0x40, 0x40, 0x20, 0x38, 0x40, + 0x20, 0x30, 0x40, 0x20, 0x28, 0x40, 0x2c, 0x2c, 0x40, 0x30, 0x2c, 0x40, 0x34, 0x2c, 0x40, 0x3c, 0x2c, 0x40, + 0x40, 0x2c, 0x40, 0x40, 0x2c, 0x3c, 0x40, 0x2c, 0x34, 0x40, 0x2c, 0x30, 0x40, 0x2c, 0x2c, 0x40, 0x30, 0x2c, + 0x40, 0x34, 0x2c, 0x40, 0x3c, 0x2c, 0x40, 0x40, 0x2c, 0x3c, 0x40, 0x2c, 0x34, 0x40, 0x2c, 0x30, 0x40, 0x2c, + 0x2c, 0x40, 0x2c, 0x2c, 0x40, 0x30, 0x2c, 0x40, 0x34, 0x2c, 0x40, 0x3c, 0x2c, 0x40, 0x40, 0x2c, 0x3c, 0x40, + 0x2c, 0x34, 0x40, 0x2c, 0x30, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const byte font[][5] = { + {0x00,0x00,0x00,0xff,0x00}, + {0x5f,0xff,0x00,0x00,0x00}, + {0x03,0x00,0x03,0xff,0x00}, + {0x14,0x7f,0x14,0x7f,0x14}, + {0x24,0x2a,0x7f,0x2a,0x12}, + {0x61,0x10,0x08,0x04,0x43}, + {0x38,0x4e,0x59,0x26,0x50}, + {0x03,0xff,0x00,0x00,0x00}, + {0x3e,0x41,0xff,0x00,0x00}, + {0x41,0x3e,0xff,0x00,0x00}, + {0x10,0x54,0x38,0x54,0x10}, + {0x10,0x10,0x7c,0x10,0x10}, + {0x80,0x40,0xff,0x00,0x00}, + {0x10,0x10,0x10,0x10,0x10}, + {0x40,0xff,0x00,0x00,0x00}, + {0x60,0x10,0x08,0x04,0x03}, + + {0x3e,0x41,0x41,0x41,0x3e}, /* digits */ + {0x04,0x02,0x7f,0xff,0x00}, + {0x42,0x61,0x51,0x49,0x46}, + {0x22,0x41,0x49,0x49,0x36}, + {0x18,0x14,0x12,0x7f,0x10}, + {0x27,0x45,0x45,0x45,0x39}, + {0x3e,0x49,0x49,0x49,0x32}, + {0x01,0x61,0x19,0x07,0x01}, + {0x36,0x49,0x49,0x49,0x36}, + {0x26,0x49,0x49,0x49,0x3e}, + + {0x44,0xff,0x00,0x00,0x00}, + {0x80,0x44,0xff,0x00,0x00}, + {0x10,0x28,0x44,0xff,0x00}, + {0x28,0x28,0x28,0x28,0x28}, + {0x44,0x28,0x10,0xff,0x00}, + {0x02,0x01,0x51,0x09,0x06}, + {0x3e,0x41,0x5d,0x5d,0x1e}, + + {0x7c,0x12,0x11,0x12,0x7c}, /* uppercase letters*/ + {0x7f,0x49,0x49,0x49,0x36}, + {0x3e,0x41,0x41,0x41,0x22}, + {0x7f,0x41,0x41,0x22,0x1c}, + {0x7f,0x49,0x49,0x49,0xff}, + {0x7f,0x09,0x09,0x09,0xff}, + {0x3e,0x41,0x41,0x49,0x3a}, + {0x7f,0x08,0x08,0x08,0x7f}, + {0x41,0x7f,0x41,0xff,0x00}, + {0x20,0x40,0x40,0x3f,0xff}, + {0x7f,0x08,0x14,0x22,0x41}, + {0x7f,0x40,0x40,0x40,0xff}, + {0x7f,0x02,0x04,0x02,0x7f}, + {0x7f,0x02,0x0c,0x10,0x7f}, + {0x3e,0x41,0x41,0x41,0x3e}, + {0x7f,0x09,0x09,0x09,0x06}, + {0x3e,0x41,0x51,0x21,0x5e}, + {0x7f,0x09,0x19,0x29,0x46}, + {0x26,0x49,0x49,0x49,0x32}, + {0x01,0x01,0x7f,0x01,0x01}, + {0x3f,0x40,0x40,0x40,0x3f}, + {0x07,0x18,0x60,0x18,0x07}, + {0x1f,0x60,0x18,0x60,0x1f}, + {0x63,0x14,0x08,0x14,0x63}, + {0x03,0x04,0x78,0x04,0x03}, + {0x61,0x51,0x49,0x45,0x43}, + + {0x7f,0x41,0x41,0xff,0x00}, + {0x03,0x04,0x08,0x10,0x60}, + {0x41,0x41,0x7f,0xff,0x00}, + {0x02,0x01,0x02,0xff,0x00}, + {0x80,0x80,0x80,0x80,0x80}, + {0x01,0x02,0xff,0x00,0x00}, + + {0x38,0x44,0x44,0x44,0x7c}, /* lowercase letters */ + {0x7f,0x44,0x44,0x44,0x38}, + {0x38,0x44,0x44,0x44,0x44}, + {0x38,0x44,0x44,0x44,0x7f}, + {0x38,0x54,0x54,0x54,0x58}, + {0x04,0x7e,0x05,0x01,0xff}, + {0x98,0xa4,0xa4,0xa4,0x7c}, + {0x7f,0x04,0x04,0x04,0x78}, + {0x7d,0xff,0x00,0x00,0x00}, + {0x80,0x80,0x7d,0xff,0x00}, + {0x7f,0x10,0x28,0x44,0xff}, + {0x7f,0xff,0x00,0x00,0x00}, + {0x7c,0x04,0x7c,0x04,0x78}, + {0x7c,0x04,0x04,0x04,0x78}, + {0x38,0x44,0x44,0x44,0x38}, + {0xfc,0x24,0x24,0x24,0x18}, + {0x18,0x24,0x24,0x24,0xfc}, + {0x7c,0x08,0x04,0x04,0xff}, + {0x48,0x54,0x54,0x54,0x24}, + {0x04,0x3e,0x44,0x40,0xff}, + {0x7c,0x40,0x40,0x40,0x3c}, + {0x0c,0x30,0x40,0x30,0x0c}, + {0x3c,0x40,0x3c,0x40,0x3c}, + {0x44,0x28,0x10,0x28,0x44}, + {0x9c,0xa0,0xa0,0xa0,0x7c}, + {0x44,0x64,0x54,0x4c,0x44}, + + {0x08,0x36,0x41,0xff,0x00}, + {0x77,0xff,0x00,0x00,0x00}, + {0x41,0x36,0x08,0xff,0x00}, + {0x02,0x01,0x02,0x01,0xff}, + {0xff,0x00,0x00,0x00,0x00}, + + {0xfe,0x49,0x49,0x4e,0x30}, /* sharp S */ + + {0x7c,0x41,0x40,0x41,0x3c}, /* umlauts */ + + {0x04,0x06,0x7f,0x06,0x04}, /* arrows */ + {0x20,0x60,0xfe,0x60,0x20}, + + {0x38,0x45,0x44,0x45,0x7c}, /* umlauts */ + {0xff,0x00,0x00,0x00,0x00}, + {0xff,0x00,0x00,0x00,0x00}, + {0xff,0x00,0x00,0x00,0x00}, + {0xff,0x00,0x00,0x00,0x00}, + {0xff,0x00,0x00,0x00,0x00}, + {0xff,0x00,0x00,0x00,0x00}, + {0xff,0x00,0x00,0x00,0x00}, + {0xff,0x00,0x00,0x00,0x00}, + {0xff,0x00,0x00,0x00,0x00}, + {0x79,0x14,0x12,0x14,0x79}, + {0xff,0x00,0x00,0x00,0x00}, + {0xff,0x00,0x00,0x00,0x00}, + {0xff,0x00,0x00,0x00,0x00}, + {0xff,0x00,0x00,0x00,0x00}, + {0xff,0x00,0x00,0x00,0x00}, + {0x38,0x45,0x44,0x45,0x38}, + {0xff,0x00,0x00,0x00,0x00}, + {0xff,0x00,0x00,0x00,0x00}, + {0xff,0x00,0x00,0x00,0x00}, + {0xff,0x00,0x00,0x00,0x00}, + {0x3d,0x42,0x42,0x42,0x3d}, + {0x3d,0x40,0x40,0x40,0x3d}, +}; + +} diff --git a/engines/supernova/sound.cpp b/engines/supernova/sound.cpp new file mode 100644 index 0000000000..e7f3ce83bd --- /dev/null +++ b/engines/supernova/sound.cpp @@ -0,0 +1,65 @@ +/* ScummVM - Graphic Adventure Engine +* +* ScummVM is the legal property of its developers, whose names +* are too numerous to list here. Please refer to the COPYRIGHT +* file distributed with this source distribution. +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +* +*/ + +#include "audio/audiostream.h" +#include "audio/mixer.h" +#include "audio/decoders/raw.h" +#include "audio/mods/protracker.h" +#include "common/system.h" + +#include "supernova/resman.h" +#include "supernova/sound.h" +#include "supernova/supernova.h" + +namespace Supernova { + +Sound::Sound(Audio::Mixer *mixer, ResourceManager *resMan) + : _mixer(mixer) + , _resMan(resMan) { +} + +void Sound::play(AudioId index) { + Audio::AudioStream *stream = _resMan->getSoundStream(index); + + stop(); + _mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, stream, + -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO); +} + +void Sound::play(MusicId index) { + Audio::AudioStream *stream = _resMan->getSoundStream(index); + + stop(); + _mixer->playStream(Audio::Mixer::kMusicSoundType, &_soundHandle, stream, + -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO); +} + +bool Sound::isPlaying() { + return _mixer->isSoundHandleActive(_soundHandle); +} + +void Sound::stop() { + if (_mixer->isSoundHandleActive(_soundHandle)) + _mixer->stopHandle(_soundHandle); +} + +} diff --git a/engines/supernova/sound.h b/engines/supernova/sound.h new file mode 100644 index 0000000000..100c9a372b --- /dev/null +++ b/engines/supernova/sound.h @@ -0,0 +1,80 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can 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 SUPERNOVA_SOUND_H +#define SUPERNOVA_SOUND_H + +#include "audio/mixer.h" + +namespace Supernova { + +class SupernovaEngine; +class ResourceManager; + +enum AudioId { + kAudioFoundLocation, // 44|0 + kAudioCrash, // 45|0 + kAudioVoiceHalt, // 46|0 + kAudioGunShot, // 46|2510 + kAudioSmash, // 46|4020 + kAudioVoiceSupernova, // 47|0 + kAudioVoiceYeah, // 47|24010 + kAudioRobotShock, // 48|0 + kAudioRobotBreaks, // 48|2510 + kAudioShock, // 48|10520 + kAudioTurntable, // 48|13530 + kAudioSiren, // 50|0 + kAudioSnoring, // 50|12786 + kAudioRocks, // 51|0 + kAudioDeath, // 53|0 + kAudioAlarm, // 54|0 + kAudioSuccess, // 54|8010 + kAudioSlideDoor, // 54|24020 + kAudioDoorOpen, // 54|30030 + kAudioDoorClose, // 54|31040 + kAudioNumSamples +}; + +enum MusicId { + kMusicIntro = 49, + kMusicOutro = 52 +}; + +class Sound { +public: + +public: + Sound(Audio::Mixer *mixer, ResourceManager *resMan); + + void play(AudioId index); + void play(MusicId index); + void stop(); + bool isPlaying(); +private: + Audio::Mixer *_mixer; + ResourceManager *_resMan; + Audio::SoundHandle _soundHandle; +}; + +} + +#endif diff --git a/engines/supernova/state.cpp b/engines/supernova/state.cpp index e41edbf1d5..f50d665000 100644 --- a/engines/supernova/state.cpp +++ b/engines/supernova/state.cpp @@ -21,11 +21,13 @@ */ #include "common/system.h" +#include "graphics/cursorman.h" #include "graphics/palette.h" #include "gui/message.h" + +#include "supernova/screen.h" #include "supernova/supernova.h" #include "supernova/state.h" -#include "graphics/cursorman.h" namespace Supernova { @@ -136,13 +138,13 @@ bool GameManager::deserialize(Common::ReadStream *in, int version) { _inventoryScroll = in->readSint32LE(); _inventory.clear(); for (int i = 0; i < inventorySize; ++i) { - RoomID objectRoom = static_cast<RoomID>(in->readSint32LE()); + RoomId objectRoom = static_cast<RoomId>(in->readSint32LE()); int objectIndex = in->readSint32LE(); _inventory.add(*_rooms[objectRoom]->getObject(objectIndex)); } // Rooms - RoomID curRoomId = static_cast<RoomID>(in->readByte()); + RoomId curRoomId = static_cast<RoomId>(in->readByte()); for (int i = 0; i < NUMROOMS; ++i) { _rooms[i]->deserialize(in, version); } @@ -194,16 +196,16 @@ Object *Inventory::get(int index) const { if (index < _numObjects) return _inventory[index]; - return const_cast<Object *>(&Object::nullObject); + return _nullObject; } -Object *Inventory::get(ObjectID id) const { +Object *Inventory::get(ObjectId id) const { for (int i = 0; i < _numObjects; ++i) { if (_inventory[i]->_id == id) return _inventory[i]; } - return const_cast<Object *>(&Object::nullObject); + return _nullObject; } @@ -275,19 +277,20 @@ static Common::String timeToString(int msec) { return Common::String(s); } -StringID GameManager::guiCommands[] = { +StringId GameManager::guiCommands[] = { kStringCommandGo, kStringCommandLook, kStringCommandTake, kStringCommandOpen, kStringCommandClose, kStringCommandPress, kStringCommandPull, kStringCommandUse, kStringCommandTalk, kStringCommandGive }; -StringID GameManager::guiStatusCommands[] = { +StringId GameManager::guiStatusCommands[] = { kStringStatusCommandGo, kStringStatusCommandLook, kStringStatusCommandTake, kStringStatusCommandOpen, kStringStatusCommandClose, kStringStatusCommandPress, kStringStatusCommandPull, kStringStatusCommandUse, kStringStatusCommandTalk, kStringStatusCommandGive }; -GameManager::GameManager(SupernovaEngine *vm) - : _inventory(_inventoryScroll) - , _vm(vm) { +GameManager::GameManager(SupernovaEngine *vm, Sound *sound) + : _inventory(&_nullObject, _inventoryScroll) + , _vm(vm) + , _sound(sound) { initRooms(); changeRoom(INTRO); initState(); @@ -351,11 +354,10 @@ void GameManager::destroyRooms() { delete _rooms[OUTRO]; } - void GameManager::initState() { - Object::setObjectNull(_currentInputObject); - Object::setObjectNull(_inputObject[0]); - Object::setObjectNull(_inputObject[1]); + _currentInputObject = &_nullObject; + _inputObject[0] = &_nullObject; + _inputObject[1] = &_nullObject; _inputVerb = ACTION_WALK; _processInput = false; _guiEnabled = true; @@ -370,7 +372,7 @@ void GameManager::initState() { _oldTime = g_system->getMillis(); _timerPaused = 0; _timePaused = false; - _timer1 = 0; + _messageDuration = 0; _animationTimer = 0; _currentSentence = -1; @@ -466,7 +468,7 @@ void GameManager::initGui() { int cmdAvailableSpace = 320 - (cmdCount - 1) * 2; for (int i = 0; i < cmdCount; ++i) { const Common::String &text = _vm->getGameString(guiCommands[i]); - cmdAvailableSpace -= _vm->textWidth(text); + cmdAvailableSpace -= Screen::textWidth(text); } int commandButtonX = 0; @@ -476,7 +478,7 @@ void GameManager::initGui() { if (i < cmdCount - 1) { int space = cmdAvailableSpace / (cmdCount - i); cmdAvailableSpace -= space; - width = _vm->textWidth(text) + space; + width = Screen::textWidth(text) + space; } else width = 320 - commandButtonX; @@ -503,6 +505,75 @@ void GameManager::initGui() { _guiInventoryArrow[1].setTextPosition(273, 186); } +void GameManager::updateEvents() { + handleTime(); + if (_animationEnabled && !_vm->_screen->isMessageShown() && _animationTimer == 0) + _currentRoom->animation(); + + if (_state._eventCallback != kNoFn && _state._time >= _state._eventTime) { + _vm->_allowLoadGame = false; + _vm->_allowSaveGame = false; + _state._eventTime = kMaxTimerValue; + EventFunction fn = _state._eventCallback; + _state._eventCallback = kNoFn; + switch (fn) { + case kNoFn: + break; + case kSupernovaFn: + supernovaEvent(); + break; + case kGuardReturnedFn: + guardReturnedEvent(); + break; + case kGuardWalkFn: + guardWalkEvent(); + break; + case kTaxiFn: + taxiEvent(); + break; + case kSearchStartFn: + searchStartEvent(); + break; + } + _vm->_allowLoadGame = true; + _vm->_allowSaveGame = true; + return; + } + + if (_state._alarmOn && _state._timeAlarm <= _state._time) { + _state._alarmOn = false; + alarm(); + return; + } + + _mouseClicked = false; + _keyPressed = false; + Common::Event event; + while (g_system->getEventManager()->pollEvent(event)) { + switch (event.type) { + case Common::EVENT_KEYDOWN: + _keyPressed = true; + processInput(event.kbd); + break; + case Common::EVENT_LBUTTONUP: + // fallthrough + case Common::EVENT_RBUTTONUP: + if (_currentRoom->getId() != INTRO && _sound->isPlaying()) + return; + _mouseClicked = true; + // fallthrough + case Common::EVENT_MOUSEMOVE: + _mouseClickType = event.type; + _mouseX = event.mouse.x; + _mouseY = event.mouse.y; + if (_guiEnabled) + processInput(); + break; + default: + break; + } + } +} void GameManager::processInput(Common::KeyState &state) { _key = state; @@ -529,14 +600,18 @@ void GameManager::processInput(Common::KeyState &state) { _vm->quitGame(); } break; + case Common::KEYCODE_d: + if (state.flags & Common::KBD_CTRL) + _vm->_console->attach(); + break; default: break; } } void GameManager::resetInputState() { - Object::setObjectNull(_inputObject[0]); - Object::setObjectNull(_inputObject[1]); + setObjectNull(_inputObject[0]); + setObjectNull(_inputObject[1]); _inputVerb = ACTION_WALK; _processInput = false; _mouseClicked = false; @@ -571,7 +646,7 @@ void GameManager::processInput() { mouseLocation = onNone; if (_mouseClickType == Common::EVENT_LBUTTONUP) { - if (_vm->_messageDisplayed) { + if (_vm->_screen->isMessageShown()) { // Hide the message and consume the event _vm->removeMessage(); if (mouseLocation != onCmdButton) @@ -583,7 +658,7 @@ void GameManager::processInput() { case onInventory: // Fallthrough if (_inputVerb == ACTION_GIVE || _inputVerb == ACTION_USE) { - if (Object::isNullObject(_inputObject[0])) { + if (isNullObject(_inputObject[0])) { _inputObject[0] = _currentInputObject; if (!_inputObject[0]->hasProperty(COMBINABLE)) _processInput = true; @@ -593,7 +668,7 @@ void GameManager::processInput() { } } else { _inputObject[0] = _currentInputObject; - if (!Object::isNullObject(_currentInputObject)) + if (!isNullObject(_currentInputObject)) _processInput = true; } break; @@ -614,13 +689,13 @@ void GameManager::processInput() { } } else if (_mouseClickType == Common::EVENT_RBUTTONUP) { - if (_vm->_messageDisplayed) { + if (_vm->_screen->isMessageShown()) { // Hide the message and consume the event _vm->removeMessage(); return; } - if (Object::isNullObject(_currentInputObject)) + if (isNullObject(_currentInputObject)) return; if (mouseLocation == onObject || mouseLocation == onInventory) { @@ -666,8 +741,9 @@ void GameManager::processInput() { for (int i = 0; (_currentRoom->getObject(i)->_id != INVALIDOBJECT) && (field == -1) && i < kMaxObject; i++) { click = _currentRoom->getObject(i)->_click; - if (click != 255 && _vm->_currentImage) { - MSNImageDecoder::ClickField *clickField = _vm->_currentImage->_clickField; + const MSNImage *image = _vm->_screen->getCurrentImage(); + if (click != 255 && image) { + const MSNImage::ClickField *clickField = image->_clickField; do { if ((_mouseX >= clickField[click].x1) && (_mouseX <= clickField[click].x2) && (_mouseY >= clickField[click].y1) && (_mouseY <= clickField[click].y2)) @@ -698,7 +774,7 @@ void GameManager::processInput() { break; } - Object::setObjectNull(_currentInputObject); + setObjectNull(_currentInputObject); _mouseField = field; if (_mouseField >= 0 && _mouseField < 256) @@ -737,6 +813,14 @@ void GameManager::processInput() { } } +void GameManager::setObjectNull(Object *&obj) { + obj = &_nullObject; +} + +bool GameManager::isNullObject(Object *obj) { + return obj == &_nullObject; +} + void GameManager::corridorOnEntrance() { if (_state._corridorSearch) busted(0); @@ -761,7 +845,7 @@ void GameManager::telomat(int nr) { "Alga Hurz Li" }; - StringID dial1[4]; + StringId dial1[4]; dial1[0] = kStringTelomat1; dial1[1] = kNoString; dial1[2] = kStringTelomat3; @@ -769,7 +853,7 @@ void GameManager::telomat(int nr) { static byte rows1[3] = {1, 2, 1}; - StringID dial2[4]; + StringId dial2[4]; dial2[0] = kStringTelomat4; dial2[1] = kStringTelomat5; dial2[2] = kStringTelomat6; @@ -792,9 +876,9 @@ void GameManager::telomat(int nr) { _vm->renderBox(0, 0, 320, 200, kColorDarkBlue); _vm->renderText(kStringTelomat12, 50, 80, kColorGreen); _vm->renderText(kStringTelomat13, 50, 91, kColorGreen); - do + do { edit(input, 50, 105, 30); - while ((_key.keycode != Common::KEYCODE_RETURN) && (_key.keycode != Common::KEYCODE_ESCAPE)); + } while ((_key.keycode != Common::KEYCODE_RETURN) && (_key.keycode != Common::KEYCODE_ESCAPE)); if (_key.keycode == Common::KEYCODE_ESCAPE) { _vm->renderBox(0, 0, 320, 200, kColorBlack); @@ -813,7 +897,7 @@ void GameManager::telomat(int nr) { i >>= 1; if (i == 4) { _vm->renderText(kStringTelomat14, 50, 120, kColorGreen); - wait2(10); + wait(10); _vm->renderBox(0, 0, 320, 200, kColorBlack); _vm->renderRoom(*_currentRoom); _vm->paletteBrightness(); @@ -824,7 +908,7 @@ void GameManager::telomat(int nr) { if ((i == nr) || _rooms[BCORRIDOR]->getObject(4 + i)->hasProperty(CAUGHT)) { _vm->renderText(kStringTelomat15, 50, 120, kColorGreen); - wait2(10); + wait(10); _vm->renderBox(0, 0, 320, 200, kColorBlack); _vm->renderRoom(*_currentRoom); _vm->paletteBrightness(); @@ -834,12 +918,12 @@ void GameManager::telomat(int nr) { } _vm->renderText(kStringTelomat16, 50, 120, kColorGreen); - wait2(10); + wait(10); _vm->renderBox(0, 0, 320, 200, kColorBlack); _vm->renderRoom(*_currentRoom); _vm->paletteBrightness(); _vm->renderMessage(kStringTelomat17, kMessageTop, name2[i]); - waitOnInput(_timer1); + waitOnInput(_messageDuration); _vm->removeMessage(); if (_state._nameSeen[nr]) { Common::String string = _vm->getGameString(kStringTelomat2); @@ -851,7 +935,7 @@ void GameManager::telomat(int nr) { switch (dialog(3, rows1, dial1, 1)) { case 1: _vm->renderMessage(kStringTelomat18, kMessageTop); - waitOnInput(_timer1); + waitOnInput(_messageDuration); _vm->removeMessage(); if ((_state._destination == 255) && !_rooms[BCORRIDOR]->isSectionVisible(7)) { _state._eventTime = _state._time + ticksToMsec(150); @@ -861,10 +945,10 @@ void GameManager::telomat(int nr) { } break; case 0: _vm->renderMessage(kStringTelomat19, kMessageTop); - waitOnInput(_timer1); + waitOnInput(_messageDuration); _vm->removeMessage(); if (dialog(4, rows2, dial2, 0) != 3) { - wait2(10); + wait(10); say(kStringTelomat20); } _rooms[BCORRIDOR]->setSectionVisible(7, true); @@ -882,13 +966,13 @@ void GameManager::telomat(int nr) { _vm->renderBox(0, 0, 320, 200, kColorDarkBlue); _vm->renderText(kStringTelomat21, 100, 90, kColorGreen); input = ""; - do + do { edit(input, 100, 105, 30); - while ((_key.keycode != Common::KEYCODE_RETURN) && (_key.keycode != Common::KEYCODE_ESCAPE)); + } while ((_key.keycode != Common::KEYCODE_RETURN) && (_key.keycode != Common::KEYCODE_ESCAPE)); if (_key.keycode == Common::KEYCODE_RETURN) { _vm->renderText(kStringShipSleepCabin9, 100, 120, kColorGreen); - wait2(10); + wait(10); } // fallthrough case Common::KEYCODE_ESCAPE: @@ -926,7 +1010,7 @@ void GameManager::guardNoticed() { _vm->paletteFadeIn(); _vm->renderImage(2); reply(kStringGuardNoticed1, 2, 5); - wait2(2); + wait(2); reply(kStringGuardNoticed2, 2, 5); _vm->paletteFadeOut(); _currentRoom->setSectionVisible(2, false); @@ -947,19 +1031,19 @@ void GameManager::busted(int i) { i = 5; if (!_currentRoom->getObject(0)->hasProperty(OPENED)) { _vm->renderImage(i - 1); - _vm->playSound(kAudioDoorOpen); - wait2(2); + _sound->play(kAudioDoorOpen); + wait(2); } _vm->renderImage(i); - wait2(3); + wait(3); _vm->renderImage(i + 3); - _vm->playSound(kAudioVoiceHalt); + _sound->play(kAudioVoiceHalt); _vm->renderImage(i); - wait2(5); + wait(5); if (_currentRoom->getId() == OFFICE_L2) i = 13; _vm->renderImage(i + 1); - wait2(3); + wait(3); _vm->renderImage(i + 2); shot(0, 0); } else if (_currentRoom->getId() == BCORRIDOR) @@ -973,8 +1057,8 @@ void GameManager::busted(int i) { else _vm->renderImage(33); // above } - _vm->playSound(kAudioVoiceHalt); - wait2(3); + _sound->play(kAudioVoiceHalt); + wait(3); shot(0, 0); } @@ -1025,7 +1109,7 @@ void GameManager::supernovaEvent() { CursorMan.showMouse(false); if (_currentRoom->getId() <= CAVE) { _vm->renderMessage(kStringSupernova1); - waitOnInput(_timer1); + waitOnInput(_messageDuration); _vm->removeMessage(); _vm->paletteFadeOut(); changeRoom(MEETUP); @@ -1038,7 +1122,7 @@ void GameManager::supernovaEvent() { _vm->paletteFadeIn(); } _vm->renderMessage(kStringSupernova2); - waitOnInput(_timer1); + waitOnInput(_messageDuration); _vm->removeMessage(); _vm->setCurrentImage(26); _vm->renderImage(0); @@ -1046,28 +1130,28 @@ void GameManager::supernovaEvent() { novaScroll(); _vm->paletteFadeOut(); _vm->renderBox(0, 0, 320, 200, kColorBlack); - _vm->_menuBrightness = 255; + _vm->_screen->setGuiBrightness(255); _vm->paletteBrightness(); if (_currentRoom->getId() == GLIDER) { _vm->renderMessage(kStringSupernova3); - waitOnInput(_timer1); + waitOnInput(_messageDuration); _vm->removeMessage(); - _vm->_menuBrightness = 0; + _vm->_screen->setGuiBrightness(0); _vm->paletteBrightness(); _vm->renderRoom(*_currentRoom); _vm->paletteFadeIn(); _vm->renderMessage(kStringSupernova4, kMessageTop); - waitOnInput(_timer1); + waitOnInput(_messageDuration); _vm->removeMessage(); _vm->renderMessage(kStringSupernova5, kMessageTop); - waitOnInput(_timer1); + waitOnInput(_messageDuration); _vm->removeMessage(); _vm->renderMessage(kStringSupernova6, kMessageTop); - waitOnInput(_timer1); + waitOnInput(_messageDuration); _vm->removeMessage(); _vm->renderMessage(kStringSupernova7, kMessageTop); - waitOnInput(_timer1); + waitOnInput(_messageDuration); _vm->removeMessage(); changeRoom(MEETUP2); _rooms[MEETUP2]->setSectionVisible(1, true); @@ -1077,9 +1161,9 @@ void GameManager::supernovaEvent() { _inventory.remove(*(_rooms[ROGER]->getObject(8))); } else { _vm->renderMessage(kStringSupernova8); - waitOnInput(_timer1); + waitOnInput(_messageDuration); _vm->removeMessage(); - _vm->_menuBrightness = 0; + _vm->_screen->setGuiBrightness(0); _vm->paletteBrightness(); changeRoom(MEETUP2); if (_rooms[ROGER]->getObject(3)->hasProperty(CARRIED) && !_rooms[GLIDER]->isSectionVisible(5)) { @@ -1121,7 +1205,7 @@ void GameManager::walk(int imgId) { _vm->renderImage(_prevImgId + 128); _vm->renderImage(imgId); _prevImgId = imgId; - wait2(3); + wait(3); } void GameManager::guardWalkEvent() { @@ -1130,14 +1214,14 @@ void GameManager::guardWalkEvent() { _rooms[BCORRIDOR]->getObject(_state._origin + 4)->hasProperty(OPENED)); _rooms[BCORRIDOR]->getObject(_state._origin + 4)->disableProperty(OCCUPIED); if (_currentRoom == _rooms[BCORRIDOR]) { - if (_vm->_messageDisplayed) + if (_vm->_screen->isMessageShown()) _vm->removeMessage(); if (!behind) { _vm->renderImage(_state._origin + 1); _prevImgId = _state._origin + 1; - _vm->playSound(kAudioDoorOpen); - wait2(3); + _sound->play(kAudioDoorOpen); + wait(3); } int imgId; @@ -1158,13 +1242,13 @@ void GameManager::guardWalkEvent() { } _vm->renderImage(imgId); if (!behind) { - wait2(3); + wait(3); _vm->renderImage(_prevImgId + 128); - _vm->playSound(kAudioDoorClose); + _sound->play(kAudioDoorClose); } _prevImgId = imgId; - wait2(3); + wait(3); switch (_state._origin) { case 0: walk(12); @@ -1221,12 +1305,12 @@ void GameManager::guardWalkEvent() { if (behind) { _vm->renderImage(_state._destination + 1); - _vm->playSound(kAudioDoorOpen); - wait2(3); + _sound->play(kAudioDoorOpen); + wait(3); _vm->renderImage(_prevImgId + 128); - wait2(3); + wait(3); _vm->renderImage(_state._destination + 1 + 128); - _vm->playSound(kAudioDoorClose); + _sound->play(kAudioDoorClose); _rooms[BCORRIDOR]->getObject(_state._destination + 4)->setProperty(OCCUPIED); _state._destination = 255; } else if (_rooms[BCORRIDOR]->isSectionVisible(_state._destination + 1)) { @@ -1236,7 +1320,7 @@ void GameManager::guardWalkEvent() { _state._eventTime = _state._time + ticksToMsec(60); _state._eventCallback = kGuardWalkFn; } else { - wait2(18); + wait(18); SWAP(_state._origin, _state._destination); _state._eventCallback = kGuardWalkFn; } @@ -1266,7 +1350,7 @@ void GameManager::taxiEvent() { _vm->renderImage(1); _vm->renderImage(2); - _vm->playSound(kAudioRocks); + _sound->play(kAudioRocks); screenShake(); _vm->renderImage(9); _currentRoom->getObject(1)->setProperty(OPENED); @@ -1274,7 +1358,7 @@ void GameManager::taxiEvent() { _currentRoom->setSectionVisible(2, false); _vm->renderImage(3); for (int i = 4; i <= 8; i++) { - wait2(2); + wait(2); _vm->renderImage(invertSection(i - 1)); _vm->renderImage(i); } @@ -1292,7 +1376,7 @@ void GameManager::great(uint number) { if (number && (_state._greatFlag & (1 << number))) return; - _vm->playSound(kAudioSuccess); + _sound->play(kAudioSuccess); _state._greatFlag |= 1 << number; } @@ -1321,7 +1405,7 @@ void GameManager::sentence(int number, bool brightness) { } } -void GameManager::say(StringID textId) { +void GameManager::say(StringId textId) { Common::String str = _vm->getGameString(textId); if (!str.empty()) say(str.c_str()); @@ -1351,7 +1435,7 @@ void GameManager::say(const char *text) { _vm->renderBox(0, 138, 320, 62, kColorBlack); } -void GameManager::reply(StringID textId, int aus1, int aus2) { +void GameManager::reply(StringId textId, int aus1, int aus2) { Common::String str = _vm->getGameString(textId); if (!str.empty()) reply(str.c_str(), aus1, aus2); @@ -1375,7 +1459,7 @@ void GameManager::reply(const char *text, int aus1, int aus2) { _vm->removeMessage(); } -int GameManager::dialog(int num, byte rowLength[6], StringID text[6], int number) { +int GameManager::dialog(int num, byte rowLength[6], StringId text[6], int number) { _vm->_allowLoadGame = false; _guiEnabled = false; @@ -1407,7 +1491,12 @@ int GameManager::dialog(int num, byte rowLength[6], StringID text[6], int number _currentSentence = -1; do { - mouseInput3(); + do { + updateEvents(); + mousePosDialog(_mouseX, _mouseY); + g_system->updateScreen(); + g_system->delayMillis(_vm->_delay); + } while (!_mouseClicked && !_vm->shouldQuit()); } while (_currentSentence == -1 && !_vm->shouldQuit()); _vm->renderBox(0, 138, 320, 62, kColorBlack); @@ -1443,7 +1532,7 @@ void GameManager::turnOn() { return; _state._powerOff = false; - _vm->_brightness = 255; + _vm->_screen->setViewportBrightness(255); _rooms[SLEEP]->setSectionVisible(1, false); _rooms[SLEEP]->setSectionVisible(2, false); _rooms[COCKPIT]->setSectionVisible(22, false); @@ -1462,7 +1551,7 @@ void GameManager::takeObject(Object &obj) { void GameManager::drawCommandBox() { for (int i = 0; i < ARRAYSIZE(_guiCommandButton); ++i) { _vm->renderBox(_guiCommandButton[i]); - int space = (_guiCommandButton[i].width() - _vm->textWidth(_guiCommandButton[i].getText())) / 2; + int space = (_guiCommandButton[i].width() - Screen::textWidth(_guiCommandButton[i].getText())) / 2; _vm->renderText(_guiCommandButton[i].getText(), _guiCommandButton[i].getTextPos().x + space, _guiCommandButton[i].getTextPos().y, @@ -1491,7 +1580,7 @@ void GameManager::drawInventory() { uint16 GameManager::getKeyInput(bool blockForPrintChar) { while (!_vm->shouldQuit()) { - _vm->updateEvents(); + updateEvents(); if (_keyPressed) { if (blockForPrintChar) { if (Common::isPrint(_key.keycode) || @@ -1521,7 +1610,7 @@ uint16 GameManager::getKeyInput(bool blockForPrintChar) { Common::EventType GameManager::getMouseInput() { while (!_vm->shouldQuit()) { - _vm->updateEvents(); + updateEvents(); if (_mouseClicked) return _mouseClickType; g_system->updateScreen(); @@ -1532,7 +1621,7 @@ Common::EventType GameManager::getMouseInput() { void GameManager::getInput() { while (!_vm->shouldQuit()) { - _vm->updateEvents(); + updateEvents(); if (_mouseClicked || _keyPressed) break; g_system->updateScreen(); @@ -1540,15 +1629,6 @@ void GameManager::getInput() { } } -void GameManager::mouseInput3() { - do { - _vm->updateEvents(); - mousePosDialog(_mouseX, _mouseY); - g_system->updateScreen(); - g_system->delayMillis(_vm->_delay); - } while (!_mouseClicked && !_vm->shouldQuit()); -} - void GameManager::roomBrightness() { _roomBrightness = 255; if ((_currentRoom->getId() != OUTSIDE) && (_currentRoom->getId() < ROCKS) && _state._powerOff) @@ -1558,22 +1638,22 @@ void GameManager::roomBrightness() { else if ((_currentRoom->getId() == GUARD3) && _state._powerOff) _roomBrightness = 0; - if (_vm->_brightness != 0) - _vm->_brightness = _roomBrightness; + if (_vm->_screen->getViewportBrightness() != 0) + _vm->_screen->setViewportBrightness(_roomBrightness); _vm->paletteBrightness(); } -void GameManager::changeRoom(RoomID id) { +void GameManager::changeRoom(RoomId id) { _currentRoom = _rooms[id]; _newRoom = true; } -void GameManager::wait2(int ticks) { +void GameManager::wait(int ticks) { int32 end = _state._time + ticksToMsec(ticks); do { g_system->delayMillis(_vm->_delay); - _vm->updateEvents(); + updateEvents(); g_system->updateScreen(); } while (_state._time < end && !_vm->shouldQuit()); } @@ -1582,7 +1662,7 @@ void GameManager::waitOnInput(int ticks) { int32 end = _state._time + ticksToMsec(ticks); do { g_system->delayMillis(_vm->_delay); - _vm->updateEvents(); + updateEvents(); g_system->updateScreen(); } while (_state._time < end && !_vm->shouldQuit() && !_keyPressed && !_mouseClicked); } @@ -1592,7 +1672,7 @@ bool GameManager::waitOnInput(int ticks, Common::KeyCode &keycode) { int32 end = _state._time + ticksToMsec(ticks); do { g_system->delayMillis(_vm->_delay); - _vm->updateEvents(); + updateEvents(); g_system->updateScreen(); if (_keyPressed) { keycode = _key.keycode; @@ -1652,14 +1732,14 @@ void GameManager::saveTime() { void GameManager::screenShake() { for (int i = 0; i < 12; ++i) { _vm->_system->setShakePos(8); - wait2(1); + wait(1); _vm->_system->setShakePos(0); - wait2(1); + wait(1); } } void GameManager::shock() { - _vm->playSound(kAudioShock); + _sound->play(kAudioShock); dead(kStringShock); } @@ -1704,24 +1784,24 @@ void GameManager::edit(Common::String &input, int x, int y, uint length) { kScreenWidth - x : (length + 1) * (kFontWidth + 2); while (isEditing) { - _vm->_textCursorX = x; - _vm->_textCursorY = y; - _vm->_textColor = kColorWhite99; + _vm->_screen->setTextCursorPos(x, y); + _vm->_screen->setTextCursorColor(kColorWhite99); _vm->renderBox(x, y - 1, overdrawWidth, 9, kColorDarkBlue); for (uint i = 0; i < input.size(); ++i) { // Draw char highlight depending on cursor position if (i == cursorIndex) { - _vm->renderBox(_vm->_textCursorX, y - 1, _vm->textWidth(input[i]), 9, kColorWhite99); - _vm->_textColor = kColorDarkBlue; + _vm->renderBox(_vm->_screen->getTextCursorPos().x, y - 1, + Screen::textWidth(input[i]), 9, kColorWhite99); + _vm->_screen->setTextCursorColor(kColorDarkBlue); _vm->renderText(input[i]); - _vm->_textColor = kColorWhite99; + _vm->_screen->setTextCursorColor(kColorWhite99); } else _vm->renderText(input[i]); } if (cursorIndex == input.size()) { - _vm->renderBox(_vm->_textCursorX + 1, y - 1, 6, 9, kColorDarkBlue); - _vm->renderBox(_vm->_textCursorX , y - 1, 1, 9, kColorWhite99); + _vm->renderBox(_vm->_screen->getTextCursorPos().x + 1, y - 1, 6, 9, kColorDarkBlue); + _vm->renderBox(_vm->_screen->getTextCursorPos().x, y - 1, 1, 9, kColorWhite99); } getKeyInput(true); @@ -1767,15 +1847,15 @@ void GameManager::edit(Common::String &input, int x, int y, uint length) { void GameManager::shot(int a, int b) { if (a) _vm->renderImage(a); - _vm->playSound(kAudioGunShot); - wait2(2); + _sound->play(kAudioGunShot); + wait(2); if (b) _vm->renderImage(b); - wait2(2); + wait(2); if (a) _vm->renderImage(a); - _vm->playSound(kAudioGunShot); - wait2(2); + _sound->play(kAudioGunShot); + wait(2); if (b) _vm->renderImage(b); @@ -1801,7 +1881,7 @@ void GameManager::drawStatus() { _vm->renderBox(0, 140, 320, 9, kColorWhite25); _vm->renderText(_vm->getGameString(guiStatusCommands[index]), 1, 141, kColorDarkGreen); - if (Object::isNullObject(_inputObject[0])) + if (isNullObject(_inputObject[0])) _vm->renderText(_currentInputObject->_name); else { _vm->renderText(_inputObject[0]->_name); @@ -1832,19 +1912,18 @@ void GameManager::closeLocker(const Room *room, Object *obj, Object *lock, int s } } -void GameManager::dead(StringID messageId) { +void GameManager::dead(StringId messageId) { _vm->paletteFadeOut(); _guiEnabled = false; _vm->setCurrentImage(11); _vm->renderImage(0); _vm->renderMessage(messageId); - _vm->playSound(kAudioDeath); + _sound->play(kAudioDeath); _vm->paletteFadeIn(); getInput(); _vm->paletteFadeOut(); _vm->removeMessage(); - // TODO: Load screen destroyRooms(); initRooms(); initState(); @@ -1926,10 +2005,10 @@ bool GameManager::genericInteract(Action verb, Object &obj1, Object &obj2) { } } else if ((verb == ACTION_LOOK) && (obj1._id == NEWSPAPER)) { _vm->renderMessage(kStringGenericInteract_10); - waitOnInput(_timer1); + waitOnInput(_messageDuration); _vm->removeMessage(); _vm->renderMessage(kStringGenericInteract_11); - waitOnInput(_timer1); + waitOnInput(_messageDuration); _vm->removeMessage(); _vm->setCurrentImage(2); _vm->renderImage(0); @@ -2127,7 +2206,7 @@ bool GameManager::genericInteract(Action verb, Object &obj1, Object &obj2) { _vm->renderMessage(kStringGenericInteract_30); else if ((verb == ACTION_LOOK) && (obj1._id == BOOK2)) { _vm->renderMessage(kStringGenericInteract_31); - waitOnInput(_timer1); + waitOnInput(_messageDuration); _vm->removeMessage(); _vm->renderMessage(kStringGenericInteract_32); } else @@ -2192,7 +2271,7 @@ void GameManager::handleInput() { byte i = _inputObject[0]->_click; _inputObject[0]->_click = _inputObject[0]->_click2; _inputObject[0]->_click2 = i; - _vm->playSound(kAudioDoorOpen); + _sound->play(kAudioDoorOpen); } break; @@ -2211,7 +2290,7 @@ void GameManager::handleInput() { byte i = _inputObject[0]->_click; _inputObject[0]->_click = _inputObject[0]->_click2; _inputObject[0]->_click2 = i; - _vm->playSound(kAudioDoorClose); + _sound->play(kAudioDoorClose); } break; @@ -2230,7 +2309,7 @@ void GameManager::handleInput() { } void GameManager::executeRoom() { - if (_processInput && !_vm->_messageDisplayed && _guiEnabled) { + if (_processInput && !_vm->_screen->isMessageShown() && _guiEnabled) { handleInput(); if (_mouseClicked) { Common::Event event; @@ -2246,7 +2325,7 @@ void GameManager::executeRoom() { } if (_guiEnabled) { - if (!_vm->_messageDisplayed) { + if (!_vm->_screen->isMessageShown()) { g_system->fillScreen(kColorBlack); _vm->renderRoom(*_currentRoom); } @@ -2257,7 +2336,7 @@ void GameManager::executeRoom() { } roomBrightness(); - if (_vm->_brightness == 0) + if (_vm->_screen->getViewportBrightness() == 0) _vm->paletteFadeIn(); if (!_currentRoom->hasSeen() && _newRoom) { @@ -2269,31 +2348,31 @@ void GameManager::executeRoom() { void GameManager::guardShot() { _vm->renderImage(2); _vm->renderImage(5); - wait2(3); + wait(3); _vm->renderImage(2); - _vm->playSound(kAudioVoiceHalt); - while (_vm->_mixer->isSoundHandleActive(_vm->_soundHandle)) - wait2(1); + _sound->play(kAudioVoiceHalt); + while (_sound->isPlaying()) + wait(1); _vm->renderImage(5); - wait2(5); + wait(5); _vm->renderImage(3); - wait2(3); + wait(3); shot(4, 3); } void GameManager::guard3Shot() { _vm->renderImage(1); - wait2(3); - _vm->playSound(kAudioVoiceHalt); // 46/0 - while (_vm->_mixer->isSoundHandleActive(_vm->_soundHandle)) - wait2(1); + wait(3); + _sound->play(kAudioVoiceHalt); // 46/0 + while (_sound->isPlaying()) + wait(1); - wait2(5); + wait(5); _vm->renderImage(2); - wait2(3); + wait(3); shot(3,2); } @@ -2337,12 +2416,12 @@ void GameManager::alarmSound() { _vm->removeMessage(); _vm->renderMessage(kStringAlarm); - int32 end = _state._time + ticksToMsec(_timer1); + int32 end = _state._time + ticksToMsec(_messageDuration); do { - _vm->playSound(kAudioAlarm); - while (_vm->_mixer->isSoundHandleActive(_vm->_soundHandle)) { + _sound->play(kAudioAlarm); + while (_sound->isPlaying()) { g_system->delayMillis(_vm->_delay); - _vm->updateEvents(); + updateEvents(); g_system->updateScreen(); } } while (_state._time < end && !_vm->shouldQuit()); diff --git a/engines/supernova/state.h b/engines/supernova/state.h index ab21842296..7d7b2284ad 100644 --- a/engines/supernova/state.h +++ b/engines/supernova/state.h @@ -23,9 +23,11 @@ #ifndef SUPERNOVA_STATE_H #define SUPERNOVA_STATE_H +#include "common/events.h" #include "common/rect.h" #include "common/keyboard.h" #include "supernova/rooms.h" +#include "supernova/sound.h" namespace Supernova { @@ -63,8 +65,9 @@ struct GameState { class Inventory { public: - Inventory(int &inventoryScroll) + Inventory(Object *nullObject, int &inventoryScroll) : _numObjects(0) + , _nullObject(nullObject) , _inventoryScroll(inventoryScroll) {} @@ -72,11 +75,12 @@ public: void remove(Object &obj); void clear(); Object *get(int index) const; - Object *get(ObjectID id) const; + Object *get(ObjectId id) const; int getSize() const { return _numObjects; } private: Object *_inventory[kMaxCarry]; + Object *_nullObject; int &_inventoryScroll; int _numObjects; }; @@ -121,18 +125,20 @@ private: class GameManager { public: - GameManager(SupernovaEngine *vm); + GameManager(SupernovaEngine *vm, Sound *sound); ~GameManager(); + void updateEvents(); void processInput(Common::KeyState &state); void processInput(); void executeRoom(); bool serialize(Common::WriteStream *out); bool deserialize(Common::ReadStream *in, int version); - static StringID guiCommands[]; - static StringID guiStatusCommands[]; + static StringId guiCommands[]; + static StringId guiStatusCommands[]; SupernovaEngine *_vm; + Sound *_sound; Common::KeyState _key; Common::EventType _mouseClickType; bool _mouseClicked; @@ -150,13 +156,13 @@ public: bool _animationEnabled; byte _roomBrightness; Action _inputVerb; + Object _nullObject; Object *_currentInputObject; Object *_inputObject[2]; - bool _waitEvent; int32 _oldTime; uint _timePaused; bool _timerPaused; - int32 _timer1; + int32 _messageDuration; int32 _animationTimer; int _inventoryScroll; int _exitList[25]; @@ -166,11 +172,13 @@ public: // Dialog int _currentSentence; int _sentenceNumber[6]; - StringID _texts[6]; + StringId _texts[6]; byte _rows[6]; byte _rowsStart[6]; void takeObject(Object &obj); + void setObjectNull(Object *&obj); + bool isNullObject(Object *obj); void initState(); void initRooms(); @@ -184,8 +192,7 @@ public: Common::EventType getMouseInput(); uint16 getKeyInput(bool blockForPrintChar = false); void getInput(); - void mouseInput3(); - void wait2(int ticks); + void wait(int ticks); void waitOnInput(int ticks); bool waitOnInput(int ticks, Common::KeyCode &keycode); void turnOff(); @@ -203,7 +210,7 @@ public: void drawStatus(); void drawCommandBox(); void drawInventory(); - void changeRoom(RoomID id); + void changeRoom(RoomId id); void resetInputState(); void handleInput(); void handleTime(); @@ -211,12 +218,12 @@ public: void loadTime(); void saveTime(); void setAnimationTimer(int ticks); - void dead(StringID messageId); - int dialog(int num, byte rowLength[6], StringID text[6], int number); + void dead(StringId messageId); + int dialog(int num, byte rowLength[6], StringId text[6], int number); void sentence(int number, bool brightness); - void say(StringID textId); + void say(StringId textId); void say(const char *text); - void reply(StringID textId, int aus1, int aus2); + void reply(StringId textId, int aus1, int aus2); void reply(const char *text, int aus1, int aus2); void mousePosDialog(int x, int y); void shot(int a, int b); diff --git a/engines/supernova/supernova.cpp b/engines/supernova/supernova.cpp index 50731ae52f..c47e476de7 100644 --- a/engines/supernova/supernova.cpp +++ b/engines/supernova/supernova.cpp @@ -20,7 +20,6 @@ * */ -#include "audio/mods/protracker.h" #include "common/config-manager.h" #include "common/debug.h" #include "common/debug-channels.h" @@ -42,36 +41,14 @@ #include "graphics/thumbnail.h" #include "gui/saveload.h" +#include "supernova/resman.h" +#include "supernova/screen.h" +#include "supernova/sound.h" #include "supernova/supernova.h" #include "supernova/state.h" namespace Supernova { -const AudioInfo audioInfo[kAudioNumSamples] = { - {44, 0, -1}, - {45, 0, -1}, - {46, 0, 2510}, - {46, 2510, 4020}, - {46, 4020, -1}, - {47, 0, 24010}, - {47, 24010, -1}, - {48, 0, 2510}, - {48, 2510, 10520}, - {48, 10520, 13530}, - {48, 13530, -1}, - {50, 0, 12786}, - {50, 12786, -1}, - {51, 0, -1}, - {53, 0, -1}, - {54, 0, 8010}, - {54, 8010, 24020}, - {54, 24020, 30030}, - {54, 30030, 31040}, - {54, 31040, -1} -}; - -const Object Object::nullObject; - ObjectType operator|(ObjectType a, ObjectType b) { return static_cast<ObjectType>(+a | +b); } @@ -98,74 +75,37 @@ ObjectType &operator^=(ObjectType &a, ObjectType b) { SupernovaEngine::SupernovaEngine(OSystem *syst) : Engine(syst) - , _console(NULL) - , _gm(NULL) - , _currentImage(NULL) - , _soundMusicIntro(NULL) - , _soundMusicOutro(NULL) - , _rnd("supernova") - , _brightness(255) - , _menuBrightness(255) + , _console(nullptr) + , _gm(nullptr) + , _sound(nullptr) + , _resMan(nullptr) + , _screen(nullptr) , _delay(33) , _textSpeed(kTextSpeed[2]) - , _screenWidth(320) - , _screenHeight(200) - , _messageDisplayed(false) , _allowLoadGame(true) - , _allowSaveGame(true) -{ -// const Common::FSNode gameDataDir(ConfMan.get("path")); -// SearchMan.addSubDirectoryMatching(gameDataDir, "sound"); - + , _allowSaveGame(true) { if (ConfMan.hasKey("textspeed")) _textSpeed = ConfMan.getInt("textspeed"); - // setup engine specific debug channels DebugMan.addDebugChannel(kDebugGeneral, "general", "Supernova general debug channel"); } SupernovaEngine::~SupernovaEngine() { DebugMan.clearAllDebugChannels(); - delete _currentImage; delete _console; delete _gm; - delete _soundMusicIntro; - delete _soundMusicOutro; + delete _sound; + delete _resMan; + delete _screen; } Common::Error SupernovaEngine::run() { - Graphics::ModeList modes; - modes.push_back(Graphics::Mode(320, 200)); - modes.push_back(Graphics::Mode(640, 480)); - initGraphicsModes(modes); - initGraphics(_screenWidth, _screenHeight); - - Common::Error status = loadGameStrings(); - if (status.getCode() != Common::kNoError) - return status; - - _gm = new GameManager(this); - _console = new Console(this, _gm); - - initData(); - initPalette(); - - CursorMan.replaceCursor(_mouseNormal, 16, 16, 0, 0, kColorCursorTransparent); - CursorMan.replaceCursorPalette(initVGAPalette, 0, 16); - CursorMan.showMouse(true); - - setTotalPlayTime(0); - - int saveSlot = ConfMan.getInt("save_slot"); - if (saveSlot >= 0) { - if (loadGameState(saveSlot).getCode() != Common::kNoError) - error("Failed to load save game from slot %i", saveSlot); - } + init(); while (!shouldQuit()) { uint32 start = _system->getMillis(); - updateEvents(); + _gm->updateEvents(); _gm->executeRoom(); _console->onFrame(); _system->updateScreen(); @@ -174,89 +114,34 @@ Common::Error SupernovaEngine::run() { _system->delayMillis(end); } - stopSound(); + _mixer->stopAll(); return Common::kNoError; } -void SupernovaEngine::updateEvents() { - _gm->handleTime(); - if (_gm->_animationEnabled && !_messageDisplayed && _gm->_animationTimer == 0) - _gm->_currentRoom->animation(); - - if (_gm->_state._eventCallback != kNoFn && _gm->_state._time >= _gm->_state._eventTime) { - _allowLoadGame = false; - _allowSaveGame = false; - _gm->_state._eventTime = kMaxTimerValue; - EventFunction fn = _gm->_state._eventCallback; - _gm->_state._eventCallback = kNoFn; - switch (fn) { - case kNoFn: - break; - case kSupernovaFn: - _gm->supernovaEvent(); - break; - case kGuardReturnedFn: - _gm->guardReturnedEvent(); - break; - case kGuardWalkFn: - _gm->guardWalkEvent(); - break; - case kTaxiFn: - _gm->taxiEvent(); - break; - case kSearchStartFn: - _gm->searchStartEvent(); - break; - } - _allowLoadGame = true; - _allowSaveGame = true; - return; - } - - if (_gm->_state._alarmOn && _gm->_state._timeAlarm <= _gm->_state._time) { - _gm->_state._alarmOn = false; - _gm->alarm(); - return; - } +void SupernovaEngine::init() { + Graphics::ModeList modes; + modes.push_back(Graphics::Mode(320, 200)); + modes.push_back(Graphics::Mode(640, 480)); + initGraphicsModes(modes); + initGraphics(320, 200); - _gm->_mouseClicked = false; - _gm->_keyPressed = false; - Common::Event event; - while (g_system->getEventManager()->pollEvent(event)) { - switch (event.type) { - case Common::EVENT_KEYDOWN: - _gm->_keyPressed = true; - if (event.kbd.keycode == Common::KEYCODE_d && - (event.kbd.flags & Common::KBD_CTRL)) { - _console->attach(); - } - if (event.kbd.keycode == Common::KEYCODE_x && - (event.kbd.flags & Common::KBD_CTRL)) { - // TODO: Draw exit box - } + Common::Error status = loadGameStrings(); + if (status.getCode() != Common::kNoError) + error("Failed reading game strings"); - _gm->processInput(event.kbd); - break; + _resMan = new ResourceManager(); + _sound = new Sound(_mixer, _resMan); + _gm = new GameManager(this, _sound); + _screen = new Screen(this, _gm, _resMan); + _console = new Console(this, _gm); - case Common::EVENT_LBUTTONUP: - // fallthrough - case Common::EVENT_RBUTTONUP: - if (_gm->_currentRoom->getId() != INTRO && _mixer->isSoundHandleActive(_soundHandle)) - return; - _gm->_mouseClicked = true; - // fallthrough - case Common::EVENT_MOUSEMOVE: - _gm->_mouseClickType = event.type; - _gm->_mouseX = event.mouse.x; - _gm->_mouseY = event.mouse.y; - if (_gm->_guiEnabled) - _gm->processInput(); - break; + setTotalPlayTime(0); - default: - break; - } + int saveSlot = ConfMan.getInt("save_slot"); + if (saveSlot >= 0) { + if (loadGameState(saveSlot).getCode() != Common::kNoError) + error("Failed to load save game from slot %i", saveSlot); } } @@ -337,440 +222,133 @@ Common::Error SupernovaEngine::loadGameStrings() { return Common::kReadingFailed; } -void SupernovaEngine::initData() { - // Sound - // Note: - // - samples start with a header of 6 bytes: 01 SS SS 00 AD 00 - // where SS SS (LE uint16) is the size of the sound sample + 2 - // - samples end with a footer of 4 bytes: 00 00 - // Skip those in the buffer - Common::File file; - - for (int i = 0; i < kAudioNumSamples; ++i) { - if (!file.open(Common::String::format("msn_data.%03d", audioInfo[i]._filenumber))) { - error("File %s could not be read!", file.getName()); - } - - if (audioInfo[i]._offsetEnd == -1) { - file.seek(0, SEEK_END); - _soundSamples[i]._length = file.pos() - audioInfo[i]._offsetStart - 10; - } else { - _soundSamples[i]._length = audioInfo[i]._offsetEnd - audioInfo[i]._offsetStart - 10; - } - _soundSamples[i]._buffer = new byte[_soundSamples[i]._length]; - file.seek(audioInfo[i]._offsetStart + 6); - file.read(_soundSamples[i]._buffer, _soundSamples[i]._length); - file.close(); - } - - _soundMusicIntro = convertToMod("msn_data.049"); - _soundMusicOutro = convertToMod("msn_data.052"); - - // Cursor - const uint16 *bufferNormal = reinterpret_cast<const uint16 *>(mouseNormal); - const uint16 *bufferWait = reinterpret_cast<const uint16 *>(mouseWait); - for (uint i = 0; i < sizeof(mouseNormal) / 4; ++i) { - for (uint bit = 0; bit < 16; ++bit) { - uint mask = 0x8000 >> bit; - uint bitIndex = i * 16 + bit; - - _mouseNormal[bitIndex] = (READ_LE_UINT16(bufferNormal + i) & mask) ? kColorCursorTransparent : kColorBlack; - if (READ_LE_UINT16(bufferNormal + i + 16) & mask) - _mouseNormal[bitIndex] = kColorLightRed; - _mouseWait[bitIndex] = (READ_LE_UINT16(bufferWait + i) & mask) ? kColorCursorTransparent : kColorBlack; - if (READ_LE_UINT16(bufferWait + i + 16) & mask) - _mouseWait[bitIndex] = kColorLightRed; - } - } +const Common::String &SupernovaEngine::getGameString(int idx) const { + if (idx < 0 || idx >= (int)_gameStrings.size()) + return _nullString; + return _gameStrings[idx]; } -void SupernovaEngine::initPalette() { - _system->getPaletteManager()->setPalette(initVGAPalette, 0, 256); -} - -void SupernovaEngine::playSound(AudioIndex sample) { - if (sample > kAudioNumSamples - 1) +void SupernovaEngine::setGameString(int idx, const Common::String &string) { + if (idx < 0) return; - - Audio::SeekableAudioStream *audioStream = Audio::makeRawStream( - _soundSamples[sample]._buffer, _soundSamples[sample]._length, - 11931, Audio::FLAG_UNSIGNED | Audio::FLAG_LITTLE_ENDIAN, DisposeAfterUse::NO); - stopSound(); - _mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, audioStream); -} - -void SupernovaEngine::stopSound() { - if (_mixer->isSoundHandleActive(_soundHandle)) - _mixer->stopHandle(_soundHandle); + while ((int)_gameStrings.size() <= idx) + _gameStrings.push_back(Common::String()); + _gameStrings[idx] = string; } -void SupernovaEngine::playSoundMod(int filenumber) -{ - Audio::AudioStream *audioStream; - if (filenumber == 49) - audioStream = Audio::makeProtrackerStream(_soundMusicIntro); - else if (filenumber == 52) - audioStream = Audio::makeProtrackerStream(_soundMusicOutro); - else - return; - - stopSound(); - _mixer->playStream(Audio::Mixer::kMusicSoundType, &_soundHandle, audioStream, - -1, Audio::Mixer::kMaxChannelVolume, 0); +void SupernovaEngine::playSound(AudioId sample) { + _sound->play(sample); } -void SupernovaEngine::renderImageSection(int section) { - // Note: inverting means we are removing the section. So we should get the rect for that - // section but draw the background (section 0) instead. - bool invert = false; - if (section > 128) { - section -= 128; - invert = true; - } - if (!_currentImage || section > _currentImage->_numSections - 1) - return; - - Common::Rect sectionRect(_currentImage->_section[section].x1, - _currentImage->_section[section].y1, - _currentImage->_section[section].x2 + 1, - _currentImage->_section[section].y2 + 1) ; - if (_currentImage->_filenumber == 1 || _currentImage->_filenumber == 2) { - sectionRect.setWidth(640); - sectionRect.setHeight(480); - if (_screenWidth != 640) { - _screenWidth = 640; - _screenHeight = 480; - initGraphics(_screenWidth, _screenHeight); - } - } else { - if (_screenWidth != 320) { - _screenWidth = 320; - _screenHeight = 200; - initGraphics(_screenWidth, _screenHeight); - } - } - - uint offset = 0; - int pitch = sectionRect.width(); - if (invert) { - pitch = _currentImage->_pitch; - offset = _currentImage->_section[section].y1 * pitch + _currentImage->_section[section].x1; - section = 0; - } - - _system->copyRectToScreen(static_cast<const byte *>(_currentImage->_sectionSurfaces[section]->getPixels()) + offset, - pitch, - sectionRect.left, sectionRect.top, - sectionRect.width(), sectionRect.height()); +void SupernovaEngine::playSound(MusicId index) { + _sound->play(index); } void SupernovaEngine::renderImage(int section) { - if (!_currentImage) - return; - - bool sectionVisible = true; - - if (section > 128) { - sectionVisible = false; - section -= 128; - } - - _gm->_currentRoom->setSectionVisible(section, sectionVisible); - - do { - if (sectionVisible) - renderImageSection(section); - else - renderImageSection(section + 128); - section = _currentImage->_section[section].next; - } while (section != 0); + _screen->renderImage(section); } bool SupernovaEngine::setCurrentImage(int filenumber) { - if (_currentImage && _currentImage->_filenumber == filenumber) - return true; - - delete _currentImage; - _currentImage = new MSNImageDecoder(); - if (!_currentImage->init(filenumber)) { - delete _currentImage; - _currentImage = NULL; - return false; - } - - _system->getPaletteManager()->setPalette(_currentImage->getPalette(), 16, 239); - paletteBrightness(); - return true; + return _screen->setCurrentImage(filenumber); } void SupernovaEngine::saveScreen(int x, int y, int width, int height) { - _screenBuffer.push(x, y, width, height); + _screen->saveScreen(x, y, width, height); } void SupernovaEngine::saveScreen(const GuiElement &guiElement) { - saveScreen(guiElement.left, guiElement.top, guiElement.width(), guiElement.height()); + _screen->saveScreen(guiElement); } void SupernovaEngine::restoreScreen() { - _screenBuffer.restore(); + _screen->restoreScreen(); } void SupernovaEngine::renderRoom(Room &room) { - if (room.getId() == INTRO) - return; - - if (setCurrentImage(room.getFileNumber())) { - for (int i = 0; i < _currentImage->_numSections; ++i) { - int section = i; - if (room.isSectionVisible(section)) { - do { - renderImageSection(section); - section = _currentImage->_section[section].next; - } while (section != 0); - } - } - } + _screen->renderRoom(room); } -int SupernovaEngine::textWidth(const uint16 key) { - char text[2]; - text[0] = key & 0xFF; - text[1] = 0; - return textWidth(text); +void SupernovaEngine::renderMessage(const char *text, MessagePosition position) { + _screen->renderMessage(text, position); } -int SupernovaEngine::textWidth(const char *text) { - int charWidth = 0; - while (*text != '\0') { - byte c = *text++; - if (c < 32) { - continue; - } else if (c == 225) { - c = 35; - } - - for (uint i = 0; i < 5; ++i) { - if (font[c - 32][i] == 0xff) { - break; - } - ++charWidth; - } - ++charWidth; - } - - return charWidth; +void SupernovaEngine::renderMessage(const Common::String &text, MessagePosition position) { + _screen->renderMessage(text, position); } -void SupernovaEngine::renderMessage(const char *text, MessagePosition position) { - Common::String t(text); - char *row[20]; - Common::String::iterator p = t.begin(); - uint numRows = 0; - int rowWidthMax = 0; - int x = 0; - int y = 0; - byte textColor = 0; - - while (*p != '\0') { - row[numRows] = p; - ++numRows; - while ((*p != '\0') && (*p != '|')) { - ++p; - } - if (*p == '|') { - *p = '\0'; - ++p; - } - } - for (uint i = 0; i < numRows; ++i) { - int rowWidth = textWidth(row[i]); - if (rowWidth > rowWidthMax) - rowWidthMax = rowWidth; - } - - switch (position) { - case kMessageNormal: - x = 160 - rowWidthMax / 2; - textColor = kColorWhite99; - break; - case kMessageTop: - x = 160 - rowWidthMax / 2; - textColor = kColorLightYellow; - break; - case kMessageCenter: - x = 160 - rowWidthMax / 2; - textColor = kColorLightRed; - break; - case kMessageLeft: - x = 3; - textColor = kColorLightYellow; - break; - case kMessageRight: - x = 317 - rowWidthMax; - textColor = kColorLightGreen; - break; - } - - if (position == kMessageNormal) { - y = 70 - ((numRows * 9) / 2); - } else if (position == kMessageTop) { - y = 5; - } else { - y = 142; - } - - int message_columns = x - 3; - int message_rows = y - 3; - int message_width = rowWidthMax + 6; - int message_height = numRows * 9 + 5; - saveScreen(message_columns, message_rows, message_width, message_height); - renderBox(message_columns, message_rows, message_width, message_height, kColorWhite35); - for (uint i = 0; i < numRows; ++i) { - renderText(row[i], x, y, textColor); - y += 9; - } - - _messageDisplayed = true; - _gm->_timer1 = (Common::strnlen(text, 512) + 20) * _textSpeed / 10; +void SupernovaEngine::renderMessage(StringId stringId, MessagePosition position, Common::String var1, Common::String var2) { + _screen->renderMessage(stringId, position, var1, var2); } void SupernovaEngine::removeMessage() { - if (_messageDisplayed) { - restoreScreen(); - _messageDisplayed = false; - } + _screen->removeMessage(); } -void SupernovaEngine::renderText(const char *text, int x, int y, byte color) { - Graphics::Surface *screen = _system->lockScreen(); - byte *cursor = static_cast<byte *>(screen->getBasePtr(x, y)); - const byte *basePtr = cursor; - - byte c; - while ((c = *text++) != '\0') { - if (c < 32) { - continue; - } else if (c == 225) { - c = 128; - } +void SupernovaEngine::renderText(const uint16 character) { + _screen->renderText(character); +} - for (uint i = 0; i < 5; ++i) { - if (font[c - 32][i] == 0xff) { - break; - } +void SupernovaEngine::renderText(const char *text) { + _screen->renderText(text); +} - byte *ascentLine = cursor; - for (byte j = font[c - 32][i]; j != 0; j >>= 1) { - if (j & 1) { - *cursor = color; - } - cursor += kScreenWidth; - } - cursor = ++ascentLine; - } - ++cursor; - } - _system->unlockScreen(); +void SupernovaEngine::renderText(const Common::String &text) { + _screen->renderText(text); +} - uint numChars = cursor - basePtr; - uint absPosition = y * kScreenWidth + x + numChars; - _textCursorX = absPosition % kScreenWidth; - _textCursorY = absPosition / kScreenWidth; - _textColor = color; +void SupernovaEngine::renderText(StringId stringId) { + _screen->renderText(stringId); +} + +void SupernovaEngine::renderText(const GuiElement &guiElement) { + _screen->renderText(guiElement); } void SupernovaEngine::renderText(const uint16 character, int x, int y, byte color) { - char text[2]; - text[0] = character & 0xFF; - text[1] = 0; - renderText(text, x, y, color); + _screen->renderText(character, x, y, color); } -void SupernovaEngine::renderText(const char *text) { - renderText(text, _textCursorX, _textCursorY, _textColor); +void SupernovaEngine::renderText(const char *text, int x, int y, byte color) { + _screen->renderText(text, x, y, color); } -void SupernovaEngine::renderText(const uint16 character) { - char text[2]; - text[0] = character & 0xFF; - text[1] = 0; - renderText(text, _textCursorX, _textCursorY, _textColor); +void SupernovaEngine::renderText(const Common::String &text, int x, int y, byte color) { + _screen->renderText(text, x, y, color); } -void SupernovaEngine::renderText(const GuiElement &guiElement) { - renderText(guiElement.getText(), guiElement.getTextPos().x, - guiElement.getTextPos().y, guiElement.getTextColor()); + +void SupernovaEngine::renderText(StringId stringId, int x, int y, byte color) { + _screen->renderText(stringId, x, y, color); } void SupernovaEngine::renderBox(int x, int y, int width, int height, byte color) { - Graphics::Surface *screen = _system->lockScreen(); - screen->fillRect(Common::Rect(x, y, x + width, y + height), color); - _system->unlockScreen(); + _screen->renderBox(x, y, width, height, color); } void SupernovaEngine::renderBox(const GuiElement &guiElement) { - renderBox(guiElement.left, guiElement.top, guiElement.width(), - guiElement.height(), guiElement.getBackgroundColor()); + _screen->renderBox(guiElement); } void SupernovaEngine::paletteBrightness() { - byte palette[768]; - - _system->getPaletteManager()->grabPalette(palette, 0, 255); - for (uint i = 0; i < 48; ++i) { - palette[i] = (initVGAPalette[i] * _menuBrightness) >> 8; - } - for (uint i = 0; i < 717; ++i) { - const byte *imagePalette; - if (_currentImage && _currentImage->getPalette()) { - imagePalette = _currentImage->getPalette(); - } else { - imagePalette = palette + 48; - } - palette[i + 48] = (imagePalette[i] * _brightness) >> 8; - } - _system->getPaletteManager()->setPalette(palette, 0, 255); + _screen->paletteBrightness(); } void SupernovaEngine::paletteFadeOut() { - while (_menuBrightness > 10) { - _menuBrightness -= 10; - if (_brightness > _menuBrightness) - _brightness = _menuBrightness; - paletteBrightness(); - _system->updateScreen(); - _system->delayMillis(_delay); - } - _menuBrightness = 0; - _brightness = 0; - paletteBrightness(); - _system->updateScreen(); + _screen->paletteFadeOut(); } void SupernovaEngine::paletteFadeIn() { - while (_menuBrightness < 245) { - if (_brightness < _gm->_roomBrightness) - _brightness += 10; - _menuBrightness += 10; - paletteBrightness(); - _system->updateScreen(); - _system->delayMillis(_delay); - } - _menuBrightness = 255; - _brightness = _gm->_roomBrightness; - paletteBrightness(); - _system->updateScreen(); + _screen->paletteFadeIn(); } void SupernovaEngine::setColor63(byte value) { - byte color[3] = {value, value, value}; - _system->getPaletteManager()->setPalette(color, 63, 1); + _screen->setColor63(value); } void SupernovaEngine::setTextSpeed() { - const Common::String& textSpeedString = getGameString(kStringTextSpeed); - int stringWidth = textWidth(textSpeedString); - int textX = (320 - stringWidth) / 2; + const Common::String &textSpeedString = getGameString(kStringTextSpeed); + int stringWidth = Screen::textWidth(textSpeedString); + int textX = (kScreenWidth - stringWidth) / 2; int textY = 100; stringWidth += 4; - int boxX = stringWidth > 110 ? (320 - stringWidth) / 2 : 105; + int boxX = stringWidth > 110 ? (kScreenWidth - stringWidth) / 2 : 105; int boxY = 97; int boxWidth = stringWidth > 110 ? stringWidth : 110; int boxHeight = 27; @@ -878,191 +456,6 @@ bool SupernovaEngine::quitGameDialog() { return quit; } -Common::MemoryReadStream *SupernovaEngine::convertToMod(const char *filename, int version) { - // MSN format - struct { - uint16 seg; - uint16 start; - uint16 end; - uint16 loopStart; - uint16 loopEnd; - char volume; - char dummy[5]; - } instr2[22]; - int nbInstr2; // 22 for version1, 15 for version 2 - int16 songLength; - char arrangement[128]; - int16 patternNumber; - int32 note2[28][64][4]; - - nbInstr2 = ((version == 1) ? 22 : 15); - - Common::File msnFile; - msnFile.open(filename); - if (!msnFile.isOpen()) { - warning("Data file '%s' not found", msnFile.getName()); - return NULL; - } - - for (int i = 0 ; i < nbInstr2 ; ++i) { - instr2[i].seg = msnFile.readUint16LE(); - instr2[i].start = msnFile.readUint16LE(); - instr2[i].end = msnFile.readUint16LE(); - instr2[i].loopStart = msnFile.readUint16LE(); - instr2[i].loopEnd = msnFile.readUint16LE(); - instr2[i].volume = msnFile.readByte(); - msnFile.read(instr2[i].dummy, 5); - } - songLength = msnFile.readSint16LE(); - msnFile.read(arrangement, 128); - patternNumber = msnFile.readSint16LE(); - for (int p = 0 ; p < patternNumber ; ++p) { - for (int n = 0 ; n < 64 ; ++n) { - for (int k = 0 ; k < 4 ; ++k) { - note2[p][n][k] = msnFile.readSint32LE(); - } - } - } - - /* MOD format */ - struct { - char iname[22]; - uint16 length; - char finetune; - char volume; - uint16 loopStart; - uint16 loopLength; - } instr[31]; - int32 note[28][64][4]; - - // We can't recover some MOD effects since several of them are mapped to 0. - // Assume the MSN effect of value 0 is Arpeggio (MOD effect of value 0). - const char invConvEff[8] = {0, 1, 2, 3, 10, 12, 13 ,15}; - - // Reminder from convertToMsn - // 31 30 29 28 27 26 25 24 - 23 22 21 20 19 18 17 16 - 15 14 13 12 11 10 09 08 - 07 06 05 04 03 02 01 00 - // h h h h g g g g f f f f e e e e d d d d c c c c b b b b a a a a - // - // MSN: - // hhhh (4 bits) Cleared to 0 - // dddd c (5 bits) Sample index | after mapping through convInstr - // ccc (3 bits) Effect type | after mapping through convEff - // bbbb aaaa (8 bits) Effect value | unmodified - // gggg ffff eeee (12 bits) Sample period | unmodified - // - // MS2: - // hhhh (4 bits) Cleared to 0 - // dddd (4 bits) Sample index | after mapping through convInstr - // cccc (4 bits) Effect type | unmodified - // bbbb aaaa (8 bits) Effect value | unmodified - // gggg ffff eeee (12 bits) Sample period | transformed (0xE000 / p) - 256 - // - // MOD: - // hhhh dddd (8 bits) Sample index - // cccc (4 bits) Effect type for this channel/division - // bbbb aaaa (8 bits) Effect value - // gggg ffff eeee (12 bits) Sample period - - // Can we recover the instruments mapping? I don't think so as part of the original instrument index is cleared. - // And it doesn't really matter as long as we are consistent. - // However we need to make sure 31 (or 15 in MS2) is mapped to 0 in MOD. - // We just add 1 to all other values, and this means a 1 <-> 1 mapping for the instruments - for (int p = 0; p < patternNumber; ++p) { - for (int n = 0; n < 64; ++n) { - for (int k = 0; k < 4; ++k) { - int32* l = &(note[p][n][k]); - *l = note2[p][n][k]; - int32 i = 0; - if (nbInstr2 == 22) { // version 1 - i = ((*l & 0xF800) >> 11); - int32 e = ((*l & 0x0700) >> 8); - int32 e1 = invConvEff[e]; - *l &= 0x0FFF00FF; - *l |= (e1 << 8); - } else { // version 2 - int32 h = (*l >> 16); - i = ((*l & 0xF000) >> 12); - *l &= 0x00000FFF; - if (h) - h = 0xE000 / (h + 256); - *l |= (h << 16); - if (i == 15) - i = 31; - } - - // Add back index in note - if (i != 31) { - ++i; - *l |= ((i & 0x0F) << 12); - *l |= ((i & 0xF0) << 24); - } - } - } - } - - for (int i = 0; i < 31; ++i) { - // iname is not stored in the mod file. Just set it to 'instrument#' - // finetune is not stored either. Assume 0. - memset(instr[i].iname, 0, 22); - sprintf(instr[i].iname, "instrument%d", i+1); - instr[i].length = 0; - instr[i].finetune = 0; - instr[i].volume = 0; - instr[i].loopStart = 0; - instr[i].loopLength = 0; - - if (i < nbInstr2) { - instr[i].length = ((instr2[i].end - instr2[i].start) >> 1); - instr[i].loopStart = ((instr2[i].loopStart - instr2[i].start) >> 1); - instr[i].loopLength = (( instr2[i].loopEnd - instr2[i].loopStart) >> 1); - instr[i].volume = instr2[i].volume; - } - } - - // The ciaaSpeed is kind of useless and not present in the MSN file. - // Traditionally 0x78 in SoundTracker. Was used in NoiseTracker as a restart point. - // ProTracker uses 0x7F. FastTracker uses it as a restart point, whereas ScreamTracker 3 uses 0x7F like ProTracker. - // You can use this to roughly detect which tracker made a MOD, and detection gets more accurate for more obscure MOD types. - char ciaaSpeed = 0x7F; - - // The mark cannot be recovered either. Since we have 4 channels and 31 instrument it can be either ID='M.K.' or ID='4CHN'. - // Assume 'M.K.' - const char mark[4] = { 'M', '.', 'K', '.' }; - - Common::MemoryWriteStreamDynamic buffer(DisposeAfterUse::NO); - - buffer.write(msnFile.getName(), 19); - buffer.writeByte(0); - - for (int i = 0 ; i < 31 ; ++i) { - buffer.write(instr[i].iname, 22); - buffer.writeUint16BE(instr[i].length); - buffer.writeByte(instr[i].finetune); - buffer.writeByte(instr[i].volume); - buffer.writeUint16BE(instr[i].loopStart); - buffer.writeUint16BE(instr[i].loopLength); - } - buffer.writeByte((char)songLength); - buffer.writeByte(ciaaSpeed); - buffer.write(arrangement, 128); - buffer.write(mark, 4); - - for (int p = 0 ; p < patternNumber ; ++p) { - for (int n = 0 ; n < 64 ; ++n) { - for (int k = 0 ; k < 4 ; ++k) { -// buffer.writeUint32BE(*((uint32*)(note[p][n]+k))); - buffer.writeSint32BE(note[p][n][k]); - } - } - } - - uint nb; - char buf[4096]; - while ((nb = msnFile.read(buf, 4096)) > 0) - buffer.write(buf, nb); - - return new Common::MemoryReadStream(buffer.getData(), buffer.size(), DisposeAfterUse::YES); -} bool SupernovaEngine::canLoadGameStateCurrently() { return _allowLoadGame; @@ -1117,10 +510,11 @@ bool SupernovaEngine::loadGame(int slot) { _gm->deserialize(savefile, saveVersion); if (saveVersion >= 5) { - _menuBrightness = savefile->readByte(); - _brightness = savefile->readByte(); + _screen->setGuiBrightness(savefile->readByte()); + _screen->setViewportBrightness(savefile->readByte()); } else { - _menuBrightness = _brightness = 255; + _screen->setGuiBrightness(255); + _screen->setViewportBrightness(255); } delete savefile; @@ -1153,8 +547,8 @@ bool SupernovaEngine::saveGame(int slot, const Common::String &description) { Graphics::saveThumbnail(*savefile); _gm->serialize(savefile); - savefile->writeByte(_menuBrightness); - savefile->writeByte(_brightness); + savefile->writeByte(_screen->getGuiBrightness()); + savefile->writeByte(_screen->getViewportBrightness()); savefile->finalize(); delete savefile; @@ -1169,59 +563,5 @@ void SupernovaEngine::errorTempSave(bool saving) { error("Unrecoverable error"); } -ScreenBufferStack::ScreenBufferStack() - : _last(_buffer) { -} - -void ScreenBufferStack::push(int x, int y, int width, int height) { - if (_last == ARRAYEND(_buffer)) - return; - - Graphics::Surface* screenSurface = g_system->lockScreen(); - - if (x < 0) { - width += x; - x = 0; - } - if (x + width > screenSurface->w) - width = screenSurface->w - x; - - if (y < 0) { - height += y; - y = 0; - } - if (y + height > screenSurface->h) - height = screenSurface->h - y; - - _last->_pixels = new byte[width * height]; - byte *pixels = _last->_pixels; - const byte *screen = static_cast<const byte *>(screenSurface->getBasePtr(x, y)); - for (int i = 0; i < height; ++i) { - Common::copy(screen, screen + width, pixels); - screen += screenSurface->pitch; - pixels += width; - } - g_system->unlockScreen(); - - _last->_x = x; - _last->_y = y; - _last->_width = width; - _last->_height = height; - - ++_last; -} - -void ScreenBufferStack::restore() { - if (_last == _buffer) - return; - - --_last; - g_system->lockScreen()->copyRectToSurface( - _last->_pixels, _last->_width, _last->_x, _last->_y, - _last->_width, _last->_height); - g_system->unlockScreen(); - - delete[] _last->_pixels; -} } diff --git a/engines/supernova/supernova.h b/engines/supernova/supernova.h index e01fb778a5..132e25deeb 100644 --- a/engines/supernova/supernova.h +++ b/engines/supernova/supernova.h @@ -23,9 +23,6 @@ #ifndef SUPERNOVA_SUPERNOVA_H #define SUPERNOVA_SUPERNOVA_H -#include "audio/audiostream.h" -#include "audio/mixer.h" -#include "audio/decoders/raw.h" #include "common/array.h" #include "common/events.h" #include "common/random.h" @@ -38,6 +35,7 @@ #include "supernova/graphics.h" #include "supernova/msn_def.h" #include "supernova/rooms.h" +#include "supernova/sound.h" namespace Supernova { @@ -48,96 +46,55 @@ namespace Supernova { #define SUPERNOVA_DAT "supernova.dat" #define SUPERNOVA_DAT_VERSION 1 - -struct ScreenBuffer { - ScreenBuffer() - : _x(0) - , _y(0) - , _width(0) - , _height(0) - , _pixels(NULL) - {} - - byte *_pixels; - int _x; - int _y; - int _width; - int _height; -}; -class ScreenBufferStack { -public: - ScreenBufferStack(); - - void push(int x, int y, int width, int height); - void restore(); - -private: - ScreenBuffer _buffer[8]; - ScreenBuffer *_last; -}; - -struct SoundSample { - SoundSample() - : _buffer(NULL) - , _length(0) - {} - - ~SoundSample() { - delete[] _buffer; - } - - byte *_buffer; - int _length; -}; - class GuiElement; +class ResourceManager; +class Sound; +class console; +class GameManager; +class Screen; + class SupernovaEngine : public Engine { public: explicit SupernovaEngine(OSystem *syst); ~SupernovaEngine(); virtual Common::Error run(); + virtual Common::Error loadGameState(int slot); + virtual bool canLoadGameStateCurrently(); + virtual Common::Error saveGameState(int slot, const Common::String &desc); + virtual bool canSaveGameStateCurrently(); + virtual bool hasFeature(EngineFeature f) const; + virtual void pauseEngineIntern(bool pause); - Common::RandomSource _rnd; GameManager *_gm; Console *_console; - Audio::SoundHandle _soundHandle; - ScreenBufferStack _screenBuffer; - byte _mouseNormal[256]; - byte _mouseWait[256]; - MSNImageDecoder *_currentImage; - SoundSample _soundSamples[kAudioNumSamples]; - Common::MemoryReadStream *_soundMusicIntro; - Common::MemoryReadStream *_soundMusicOutro; - int _screenWidth; - int _screenHeight; + Sound *_sound; + ResourceManager *_resMan; + Screen *_screen; bool _allowLoadGame; bool _allowSaveGame; Common::StringArray _gameStrings; Common::String _nullString; - byte _menuBrightness; - byte _brightness; uint _delay; - bool _messageDisplayed; int _textSpeed; - int _textCursorX; - int _textCursorY; - int _textColor; - int textWidth(const char *text); - int textWidth(const uint16 key); Common::Error loadGameStrings(); - void initData(); - void initPalette(); + void init(); + bool loadGame(int slot); + bool saveGame(int slot, const Common::String &description); + bool quitGameDialog(); + void errorTempSave(bool saving); + void setTextSpeed(); + const Common::String &getGameString(int idx) const; + void setGameString(int idx, const Common::String &string); + + // forwarding calls + void playSound(AudioId sample); + void playSound(MusicId index); void paletteFadeIn(); void paletteFadeOut(); void paletteBrightness(); - void updateEvents(); - void playSound(AudioIndex sample); - void playSoundMod(int filenumber); - void stopSound(); - void renderImageSection(int section); void renderImage(int section); bool setCurrentImage(int filenumber); void saveScreen(int x, int y, int width, int height); @@ -145,77 +102,22 @@ public: void restoreScreen(); void renderRoom(Room &room); void renderMessage(const char *text, MessagePosition position = kMessageNormal); + void renderMessage(const Common::String &text, MessagePosition position = kMessageNormal); + void renderMessage(StringId stringId, MessagePosition position = kMessageNormal, + Common::String var1 = "", Common::String var2 = ""); void removeMessage(); - void renderText(const char *text, int x, int y, byte color); - void renderText(const uint16 character, int x, int y, byte color); - void renderText(const char *text); void renderText(const uint16 character); + void renderText(const char *text); + void renderText(const Common::String &text); + void renderText(StringId stringId); + void renderText(const uint16 character, int x, int y, byte color); + void renderText(const char *text, int x, int y, byte color); + void renderText(const Common::String &text, int x, int y, byte color); + void renderText(StringId stringId, int x, int y, byte color); void renderText(const GuiElement &guiElement); void renderBox(int x, int y, int width, int height, byte color); void renderBox(const GuiElement &guiElement); void setColor63(byte value); - bool loadGame(int slot); - bool saveGame(int slot, const Common::String &description); - bool quitGameDialog(); - void errorTempSave(bool saving); - void setTextSpeed(); - - const Common::String &getGameString(int idx) const { - if (idx < 0 || idx >= (int)_gameStrings.size()) - return _nullString; - return _gameStrings[idx]; - } - - void setGameString(int idx, const Common::String &string) { - if (idx < 0) - return; - while ((int)_gameStrings.size() <= idx) - _gameStrings.push_back(Common::String()); - _gameStrings[idx] = string; - } - - int textWidth(const Common::String &text) { - if (text.empty()) - return 0; - return textWidth(text.c_str()); - } - void renderMessage(StringID stringId, MessagePosition position = kMessageNormal, Common::String var1 = "", Common::String var2 = "") { - Common::String text = getGameString(stringId); - if (!var1.empty()) { - if (!var2.empty()) - text = Common::String::format(text.c_str(), var1.c_str(), var2.c_str()); - else - text = Common::String::format(text.c_str(), var1.c_str()); - } - renderMessage(text, position); - } - void renderMessage(const Common::String &text, MessagePosition position = kMessageNormal) { - if (!text.empty()) - renderMessage(text.c_str(), position); - } - void renderText(StringID stringId, int x, int y, byte color) { - renderText(getGameString(stringId), x, y, color); - } - void renderText(const Common::String &text, int x, int y, byte color) { - if (!text.empty()) - renderText(text.c_str(), x, y, color); - } - void renderText(StringID stringId) { - renderText(getGameString(stringId)); - } - void renderText(const Common::String &text) { - if (!text.empty()) - renderText(text.c_str()); - } - - Common::MemoryReadStream *convertToMod(const char *filename, int version = 1); - - virtual Common::Error loadGameState(int slot); - virtual bool canLoadGameStateCurrently(); - virtual Common::Error saveGameState(int slot, const Common::String &desc); - virtual bool canSaveGameStateCurrently(); - virtual bool hasFeature(EngineFeature f) const; - virtual void pauseEngineIntern(bool pause); }; } diff --git a/engines/sword1/detection.cpp b/engines/sword1/detection.cpp index d4343c8a9f..52394cec41 100644 --- a/engines/sword1/detection.cpp +++ b/engines/sword1/detection.cpp @@ -87,9 +87,9 @@ public: } virtual bool hasFeature(MetaEngineFeature f) const; - virtual GameList getSupportedGames() const; - virtual GameDescriptor findGame(const char *gameid) const; - virtual GameList detectGames(const Common::FSList &fslist) const; + PlainGameList getSupportedGames() const override; + PlainGameDescriptor findGame(const char *gameId) const override; + DetectedGames detectGames(const Common::FSList &fslist) const override; virtual SaveStateList listSaves(const char *target) const; virtual int getMaximumSaveSlot() const; virtual void removeSaveState(const char *target, int slot) const; @@ -116,31 +116,31 @@ bool Sword1::SwordEngine::hasFeature(EngineFeature f) const { (f == kSupportsLoadingDuringRuntime); } -GameList SwordMetaEngine::getSupportedGames() const { - GameList games; - games.push_back(GameDescriptor(sword1FullSettings, GUIO_NOMIDI)); - games.push_back(GameDescriptor(sword1DemoSettings, GUIO_NOMIDI)); - games.push_back(GameDescriptor(sword1MacFullSettings, GUIO_NOMIDI)); - games.push_back(GameDescriptor(sword1MacDemoSettings, GUIO_NOMIDI)); - games.push_back(GameDescriptor(sword1PSXSettings, GUIO_NOMIDI)); - games.push_back(GameDescriptor(sword1PSXDemoSettings, GUIO_NOMIDI)); +PlainGameList SwordMetaEngine::getSupportedGames() const { + PlainGameList games; + games.push_back(sword1FullSettings); + games.push_back(sword1DemoSettings); + games.push_back(sword1MacFullSettings); + games.push_back(sword1MacDemoSettings); + games.push_back(sword1PSXSettings); + games.push_back(sword1PSXDemoSettings); return games; } -GameDescriptor SwordMetaEngine::findGame(const char *gameid) const { - if (0 == scumm_stricmp(gameid, sword1FullSettings.gameId)) +PlainGameDescriptor SwordMetaEngine::findGame(const char *gameId) const { + if (0 == scumm_stricmp(gameId, sword1FullSettings.gameId)) return sword1FullSettings; - if (0 == scumm_stricmp(gameid, sword1DemoSettings.gameId)) + if (0 == scumm_stricmp(gameId, sword1DemoSettings.gameId)) return sword1DemoSettings; - if (0 == scumm_stricmp(gameid, sword1MacFullSettings.gameId)) + if (0 == scumm_stricmp(gameId, sword1MacFullSettings.gameId)) return sword1MacFullSettings; - if (0 == scumm_stricmp(gameid, sword1MacDemoSettings.gameId)) + if (0 == scumm_stricmp(gameId, sword1MacDemoSettings.gameId)) return sword1MacDemoSettings; - if (0 == scumm_stricmp(gameid, sword1PSXSettings.gameId)) + if (0 == scumm_stricmp(gameId, sword1PSXSettings.gameId)) return sword1PSXSettings; - if (0 == scumm_stricmp(gameid, sword1PSXDemoSettings.gameId)) + if (0 == scumm_stricmp(gameId, sword1PSXDemoSettings.gameId)) return sword1PSXDemoSettings; - return GameDescriptor(); + return PlainGameDescriptor::empty(); } void Sword1CheckDirectory(const Common::FSList &fslist, bool *filesFound, bool recursion = false) { @@ -175,9 +175,9 @@ void Sword1CheckDirectory(const Common::FSList &fslist, bool *filesFound, bool r } } -GameList SwordMetaEngine::detectGames(const Common::FSList &fslist) const { +DetectedGames SwordMetaEngine::detectGames(const Common::FSList &fslist) const { int i, j; - GameList detectedGames; + DetectedGames detectedGames; bool filesFound[NUM_FILES_TO_CHECK]; for (i = 0; i < NUM_FILES_TO_CHECK; i++) filesFound[i] = false; @@ -212,31 +212,33 @@ GameList SwordMetaEngine::detectGames(const Common::FSList &fslist) const { if (!filesFound[i] || psxFilesFound) psxDemoFilesFound = false; - GameDescriptor gd; + DetectedGame game; if (mainFilesFound && pcFilesFound && demoFilesFound) - gd = GameDescriptor(sword1DemoSettings, GUIO2(GUIO_NOMIDI, GUIO_NOASPECT)); + game = DetectedGame(sword1DemoSettings); else if (mainFilesFound && pcFilesFound && psxFilesFound) - gd = GameDescriptor(sword1PSXSettings, GUIO2(GUIO_NOMIDI, GUIO_NOASPECT)); + game = DetectedGame(sword1PSXSettings); else if (mainFilesFound && pcFilesFound && psxDemoFilesFound) - gd = GameDescriptor(sword1PSXDemoSettings, GUIO2(GUIO_NOMIDI, GUIO_NOASPECT)); + game = DetectedGame(sword1PSXDemoSettings); else if (mainFilesFound && pcFilesFound && !psxFilesFound) - gd = GameDescriptor(sword1FullSettings, GUIO2(GUIO_NOMIDI, GUIO_NOASPECT)); + game = DetectedGame(sword1FullSettings); else if (mainFilesFound && macFilesFound) - gd = GameDescriptor(sword1MacFullSettings, GUIO2(GUIO_NOMIDI, GUIO_NOASPECT)); + game = DetectedGame(sword1MacFullSettings); else if (mainFilesFound && macDemoFilesFound) - gd = GameDescriptor(sword1MacDemoSettings, GUIO2(GUIO_NOMIDI, GUIO_NOASPECT)); + game = DetectedGame(sword1MacDemoSettings); else return detectedGames; - gd.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::EN_ANY)); - gd.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::DE_DEU)); - gd.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::FR_FRA)); - gd.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::IT_ITA)); - gd.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::ES_ESP)); - gd.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::PT_BRA)); - gd.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::CZ_CZE)); + game.setGUIOptions(GUIO2(GUIO_NOMIDI, GUIO_NOASPECT)); - detectedGames.push_back(gd); + game.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::EN_ANY)); + game.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::DE_DEU)); + game.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::FR_FRA)); + game.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::IT_ITA)); + game.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::ES_ESP)); + game.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::PT_BRA)); + game.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::CZ_CZE)); + + detectedGames.push_back(game); return detectedGames; } @@ -300,7 +302,11 @@ SaveStateDescriptor SwordMetaEngine::querySaveMetaInfos(const char *target, int in->skip(1); if (Graphics::checkThumbnailHeader(*in)) { - Graphics::Surface *const thumbnail = Graphics::loadThumbnail(*in); + Graphics::Surface *thumbnail; + if (!Graphics::loadThumbnail(*in, thumbnail)) { + delete in; + return SaveStateDescriptor(); + } desc.setThumbnail(thumbnail); } diff --git a/engines/sword2/sword2.cpp b/engines/sword2/sword2.cpp index a2761eb5ce..4d8399e630 100644 --- a/engines/sword2/sword2.cpp +++ b/engines/sword2/sword2.cpp @@ -92,10 +92,10 @@ public: } virtual bool hasFeature(MetaEngineFeature f) const; - virtual GameList getSupportedGames() const; + PlainGameList getSupportedGames() const override; virtual const ExtraGuiOptions getExtraGuiOptions(const Common::String &target) const; - virtual GameDescriptor findGame(const char *gameid) const; - virtual GameList detectGames(const Common::FSList &fslist) const; + PlainGameDescriptor findGame(const char *gameid) const override; + virtual DetectedGames detectGames(const Common::FSList &fslist) const; virtual SaveStateList listSaves(const char *target) const; virtual int getMaximumSaveSlot() const; virtual void removeSaveState(const char *target, int slot) const; @@ -119,11 +119,11 @@ bool Sword2::Sword2Engine::hasFeature(EngineFeature f) const { (f == kSupportsLoadingDuringRuntime); } -GameList Sword2MetaEngine::getSupportedGames() const { +PlainGameList Sword2MetaEngine::getSupportedGames() const { const Sword2::GameSettings *g = Sword2::sword2_settings; - GameList games; + PlainGameList games; while (g->gameid) { - games.push_back(GameDescriptor(g->gameid, g->description)); + games.push_back(PlainGameDescriptor::of(g->gameid, g->description)); g++; } return games; @@ -135,20 +135,20 @@ const ExtraGuiOptions Sword2MetaEngine::getExtraGuiOptions(const Common::String return options; } -GameDescriptor Sword2MetaEngine::findGame(const char *gameid) const { +PlainGameDescriptor Sword2MetaEngine::findGame(const char *gameid) const { const Sword2::GameSettings *g = Sword2::sword2_settings; while (g->gameid) { if (0 == scumm_stricmp(gameid, g->gameid)) break; g++; } - return GameDescriptor(g->gameid, g->description); + return PlainGameDescriptor::of(g->gameid, g->description); } bool isFullGame(const Common::FSList &fslist) { Common::FSList::const_iterator file; - // We distinguish between the two versions by the presense of paris.clu + // We distinguish between the two versions by the presence of paris.clu for (file = fslist.begin(); file != fslist.end(); ++file) { if (!file->isDirectory()) { if (file->getName().equalsIgnoreCase("paris.clu")) @@ -159,8 +159,8 @@ bool isFullGame(const Common::FSList &fslist) { return false; } -GameList detectGamesImpl(const Common::FSList &fslist, bool recursion = false) { - GameList detectedGames; +DetectedGames detectGamesImpl(const Common::FSList &fslist, bool recursion = false) { + DetectedGames detectedGames; const Sword2::GameSettings *g; Common::FSList::const_iterator file; bool isFullVersion = isFullGame(fslist); @@ -192,7 +192,10 @@ GameList detectGamesImpl(const Common::FSList &fslist, bool recursion = false) { continue; // Match found, add to list of candidates, then abort inner loop. - detectedGames.push_back(GameDescriptor(g->gameid, g->description, Common::UNK_LANG, Common::kPlatformUnknown, GUIO2(GUIO_NOMIDI, GUIO_NOASPECT))); + DetectedGame game = DetectedGame(g->gameid, g->description); + game.setGUIOptions(GUIO2(GUIO_NOMIDI, GUIO_NOASPECT)); + + detectedGames.push_back(game); break; } } @@ -208,7 +211,7 @@ GameList detectGamesImpl(const Common::FSList &fslist, bool recursion = false) { if (file->getName().equalsIgnoreCase("clusters")) { Common::FSList recList; if (file->getChildren(recList, Common::FSNode::kListAll)) { - GameList recGames(detectGamesImpl(recList, true)); + DetectedGames recGames = detectGamesImpl(recList, true); if (!recGames.empty()) { detectedGames.push_back(recGames); break; @@ -223,7 +226,7 @@ GameList detectGamesImpl(const Common::FSList &fslist, bool recursion = false) { return detectedGames; } -GameList Sword2MetaEngine::detectGames(const Common::FSList &fslist) const { +DetectedGames Sword2MetaEngine::detectGames(const Common::FSList &fslist) const { return detectGamesImpl(fslist); } @@ -278,10 +281,10 @@ Common::Error Sword2MetaEngine::createInstance(OSystem *syst, Engine **engine) c // Invoke the detector Common::String gameid = ConfMan.get("gameid"); - GameList detectedGames = detectGames(fslist); + DetectedGames detectedGames = detectGames(fslist); for (uint i = 0; i < detectedGames.size(); i++) { - if (detectedGames[i].gameid() == gameid) { + if (detectedGames[i].gameId == gameid) { *engine = new Sword2::Sword2Engine(syst); return Common::kNoError; } diff --git a/engines/sword25/gfx/graphicengine.cpp b/engines/sword25/gfx/graphicengine.cpp index fd3b63aeee..ca1f37c9d3 100644 --- a/engines/sword25/gfx/graphicengine.cpp +++ b/engines/sword25/gfx/graphicengine.cpp @@ -362,7 +362,7 @@ void GraphicEngine::updateLastFrameDuration() { } bool GraphicEngine::saveThumbnailScreenshot(const Common::String &filename) { - // Note: In ScumMVM, rather than saivng the thumbnail to a file, we store it in memory + // Note: In ScummVM, rather than saving the thumbnail to a file, we store it in memory // until needed when creating savegame files delete _thumbnail; @@ -373,10 +373,10 @@ bool GraphicEngine::saveThumbnailScreenshot(const Common::String &filename) { void GraphicEngine::ARGBColorToLuaColor(lua_State *L, uint color) { lua_Number components[4] = { - (lua_Number)((color >> 16) & 0xff), // Red - (lua_Number)((color >> 8) & 0xff), // Green - (lua_Number)(color & 0xff), // Blue - (lua_Number)(color >> 24), // Alpha + (lua_Number)((color >> 16) & 0xff), // Red + (lua_Number)((color >> 8) & 0xff), // Green + (lua_Number)( color & 0xff), // Blue + (lua_Number)( color >> 24), // Alpha }; lua_newtable(L); diff --git a/engines/sword25/sfx/soundengine.cpp b/engines/sword25/sfx/soundengine.cpp index 8bc20180af..2e7b41c431 100644 --- a/engines/sword25/sfx/soundengine.cpp +++ b/engines/sword25/sfx/soundengine.cpp @@ -203,8 +203,8 @@ bool SoundEngine::playSound(const Common::String &fileName, SOUND_TYPES type, fl } uint SoundEngine::playSoundEx(const Common::String &fileName, SOUND_TYPES type, float volume, float pan, bool loop, int loopStart, int loopEnd, uint layer, uint handleId) { - Common::SeekableReadStream *in = Kernel::getInstance()->getPackage()->getStream(fileName); #ifdef USE_VORBIS + Common::SeekableReadStream *in = Kernel::getInstance()->getPackage()->getStream(fileName); Audio::SeekableAudioStream *stream = Audio::makeVorbisStream(in, DisposeAfterUse::YES); #endif uint id = handleId; diff --git a/engines/sword25/util/lua/llex.cpp b/engines/sword25/util/lua/llex.cpp index 423f0285ca..ebda9ffbdf 100644 --- a/engines/sword25/util/lua/llex.cpp +++ b/engines/sword25/util/lua/llex.cpp @@ -377,8 +377,10 @@ static int llex (LexState *ls, SemInfo *seminfo) { read_long_string(ls, seminfo, sep); return TK_STRING; } - else if (sep == -1) return '['; - else luaX_lexerror(ls, "invalid long string delimiter", TK_STRING); + else if (sep == -1) + return '['; + luaX_lexerror(ls, "invalid long string delimiter", TK_STRING); + break; } case '=': { next(ls); diff --git a/engines/sword25/util/lua/lstrlib.cpp b/engines/sword25/util/lua/lstrlib.cpp index 5da45e1fea..78122030f9 100644 --- a/engines/sword25/util/lua/lstrlib.cpp +++ b/engines/sword25/util/lua/lstrlib.cpp @@ -799,10 +799,8 @@ static int str_format (lua_State *L) { luaL_addvalue(&b); continue; /* skip the `addsize' at the end */ } - else { - sprintf(buff, form, s); - break; - } + sprintf(buff, form, s); + break; } default: { /* also treat cases `pnLlh' */ return luaL_error(L, "invalid option " LUA_QL("%%%c") " to " diff --git a/engines/teenagent/detection.cpp b/engines/teenagent/detection.cpp index caa7bdbec9..e35e7033d5 100644 --- a/engines/teenagent/detection.cpp +++ b/engines/teenagent/detection.cpp @@ -178,8 +178,11 @@ public: SaveStateDescriptor ssd(slot, desc); //checking for the thumbnail - if (Graphics::Surface *const thumb = Graphics::loadThumbnail(*in)) - ssd.setThumbnail(thumb); + Graphics::Surface *thumbnail; + if (!Graphics::loadThumbnail(*in, thumbnail)) { + return SaveStateDescriptor(); + } + ssd.setThumbnail(thumbnail); return ssd; } diff --git a/engines/testbed/graphics.cpp b/engines/testbed/graphics.cpp index 1b5af76ee7..65833d1de8 100644 --- a/engines/testbed/graphics.cpp +++ b/engines/testbed/graphics.cpp @@ -464,7 +464,7 @@ TestExitStatus GFXtests::fullScreenMode() { } g_system->beginGFXTransaction(); - g_system->setFeatureState(OSystem::kFeatureFullscreenMode, !isFeatureEnabled); + g_system->setFeatureState(OSystem::kFeatureFullscreenMode, !isFeatureEnabled); g_system->endGFXTransaction(); // Current state should be now !isFeatureEnabled @@ -482,7 +482,7 @@ TestExitStatus GFXtests::fullScreenMode() { } g_system->beginGFXTransaction(); - g_system->setFeatureState(OSystem::kFeatureFullscreenMode, !isFeatureEnabled); + g_system->setFeatureState(OSystem::kFeatureFullscreenMode, !isFeatureEnabled); g_system->endGFXTransaction(); g_system->delayMillis(1000); @@ -536,7 +536,7 @@ TestExitStatus GFXtests::filteringMode() { if (g_system->hasFeature(OSystem::kFeatureFullscreenMode) && !g_system->getFeatureState(OSystem::kFeatureFullscreenMode)) { fullScreenToggled = true; g_system->beginGFXTransaction(); - g_system->setFeatureState(OSystem::kFeatureFullscreenMode, true); + g_system->setFeatureState(OSystem::kFeatureFullscreenMode, true); g_system->endGFXTransaction(); } @@ -557,7 +557,7 @@ TestExitStatus GFXtests::filteringMode() { } g_system->beginGFXTransaction(); - g_system->setFeatureState(OSystem::kFeatureFilteringMode, !isFeatureEnabled); + g_system->setFeatureState(OSystem::kFeatureFilteringMode, !isFeatureEnabled); g_system->endGFXTransaction(); // Current state should be now !isFeatureEnabled @@ -575,7 +575,7 @@ TestExitStatus GFXtests::filteringMode() { } g_system->beginGFXTransaction(); - g_system->setFeatureState(OSystem::kFeatureFilteringMode, !isFeatureEnabled); + g_system->setFeatureState(OSystem::kFeatureFilteringMode, !isFeatureEnabled); g_system->endGFXTransaction(); g_system->delayMillis(1000); @@ -591,7 +591,7 @@ TestExitStatus GFXtests::filteringMode() { // Restore fullscreen state if (fullScreenToggled) { g_system->beginGFXTransaction(); - g_system->setFeatureState(OSystem::kFeatureFullscreenMode, false); + g_system->setFeatureState(OSystem::kFeatureFullscreenMode, false); g_system->endGFXTransaction(); } @@ -601,7 +601,7 @@ TestExitStatus GFXtests::filteringMode() { return passed; } - + /** * Tests the aspect ratio correction by: drawing an ellipse, when corrected the ellipse should render to a circle */ @@ -639,7 +639,7 @@ TestExitStatus GFXtests::aspectRatio() { } g_system->beginGFXTransaction(); - g_system->setFeatureState(OSystem::kFeatureAspectRatioCorrection, !isFeatureEnabled); + g_system->setFeatureState(OSystem::kFeatureAspectRatioCorrection, !isFeatureEnabled); g_system->endGFXTransaction(); g_system->delayMillis(1000); @@ -653,7 +653,7 @@ TestExitStatus GFXtests::aspectRatio() { } g_system->beginGFXTransaction(); - g_system->setFeatureState(OSystem::kFeatureAspectRatioCorrection, isFeatureEnabled); + g_system->setFeatureState(OSystem::kFeatureAspectRatioCorrection, isFeatureEnabled); g_system->endGFXTransaction(); } else { Testsuite::displayMessage("feature not supported"); @@ -835,13 +835,13 @@ TestExitStatus GFXtests::iconifyWindow() { // Toggle g_system->beginGFXTransaction(); - g_system->setFeatureState(OSystem::kFeatureIconifyWindow, !isFeatureEnabled); + g_system->setFeatureState(OSystem::kFeatureIconifyWindow, !isFeatureEnabled); g_system->endGFXTransaction(); g_system->delayMillis(1000); g_system->beginGFXTransaction(); - g_system->setFeatureState(OSystem::kFeatureIconifyWindow, isFeatureEnabled); + g_system->setFeatureState(OSystem::kFeatureIconifyWindow, isFeatureEnabled); g_system->endGFXTransaction(); } else { Testsuite::displayMessage("feature not supported"); @@ -884,7 +884,7 @@ TestExitStatus GFXtests::scaledCursors() { if (isAspectRatioCorrected) { g_system->beginGFXTransaction(); - g_system->setFeatureState(OSystem::kFeatureAspectRatioCorrection, false); + g_system->setFeatureState(OSystem::kFeatureAspectRatioCorrection, false); g_system->endGFXTransaction(); } @@ -911,8 +911,8 @@ TestExitStatus GFXtests::scaledCursors() { g_system->beginGFXTransaction(); - bool isGFXModeSet = g_system->setGraphicsMode(gfxMode->id); - g_system->initSize(320, 200); + bool isGFXModeSet = g_system->setGraphicsMode(gfxMode->id); + g_system->initSize(320, 200); OSystem::TransactionError gfxError = g_system->endGFXTransaction(); @@ -947,12 +947,13 @@ TestExitStatus GFXtests::scaledCursors() { // Restore Original State g_system->beginGFXTransaction(); - bool isGFXModeSet = g_system->setGraphicsMode(currGFXMode); - g_system->initSize(320, 200); - if (isAspectRatioCorrected) { - g_system->setFeatureState(OSystem::kFeatureAspectRatioCorrection, true); - } + bool isGFXModeSet = g_system->setGraphicsMode(currGFXMode); + g_system->initSize(320, 200); + + if (isAspectRatioCorrected) { + g_system->setFeatureState(OSystem::kFeatureAspectRatioCorrection, true); + } OSystem::TransactionError gfxError = g_system->endGFXTransaction(); @@ -1226,7 +1227,7 @@ TestExitStatus GFXtests::pixelFormats() { // Switch to that pixel Format g_system->beginGFXTransaction(); - g_system->initSize(320, 200, &(*iter)); + g_system->initSize(320, 200, &(*iter)); g_system->endGFXTransaction(); Testsuite::clearScreen(true); @@ -1272,7 +1273,7 @@ TestExitStatus GFXtests::pixelFormats() { // Revert back to 8bpp g_system->beginGFXTransaction(); - g_system->initSize(320, 200); + g_system->initSize(320, 200); g_system->endGFXTransaction(); GFXTestSuite::setCustomColor(255, 0, 0); initMousePalette(); diff --git a/engines/tinsel/cursor.cpp b/engines/tinsel/cursor.cpp index e69031d572..c23e4f2845 100644 --- a/engines/tinsel/cursor.cpp +++ b/engines/tinsel/cursor.cpp @@ -375,6 +375,10 @@ void SetAuxCursor(SCNHANDLE hFilm) { DelAuxCursor(); // Get rid of previous + // WORKAROUND: There's no palette when loading a DW1 savegame with a held item, so exit if so + if (!BgPal()) + return; + GetCursorXY(&x, &y, false); // Note: also waits for cursor to appear pim = GetImageFromFilm(hFilm, 0, &pfr, &pmi, &pfilm);// Get pointer to image diff --git a/engines/tinsel/detection.cpp b/engines/tinsel/detection.cpp index d6bcfe5ea0..1c60c5eb8a 100644 --- a/engines/tinsel/detection.cpp +++ b/engines/tinsel/detection.cpp @@ -97,7 +97,7 @@ public: } virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const; - const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const; + ADDetectedGame fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const override; virtual bool hasFeature(MetaEngineFeature f) const; virtual SaveStateList listSaves(const char *target) const; @@ -185,7 +185,7 @@ typedef Common::Array<const ADGameDescription *> ADGameDescList; * Fallback detection scans the list of Discworld 2 targets to see if it can detect an installation * where the files haven't been renamed (i.e. don't have the '1' just before the extension) */ -const ADGameDescription *TinselMetaEngine::fallbackDetect(const FileMap &allFilesXXX, const Common::FSList &fslist) const { +ADDetectedGame TinselMetaEngine::fallbackDetect(const FileMap &allFilesXXX, const Common::FSList &fslist) const { Common::String extra; FileMap allFiles; SizeMD5Map filesSizeMD5; @@ -194,7 +194,7 @@ const ADGameDescription *TinselMetaEngine::fallbackDetect(const FileMap &allFile const Tinsel::TinselGameDescription *g; if (fslist.empty()) - return NULL; + return ADDetectedGame(); // TODO: The following code is essentially a slightly modified copy of the // complete code of function detectGame() in engines/advancedDetector.cpp. @@ -262,7 +262,7 @@ const ADGameDescription *TinselMetaEngine::fallbackDetect(const FileMap &allFile } } - ADGameDescList matched; + ADDetectedGame matched; int maxFilesMatched = 0; // MD5 based matching @@ -310,22 +310,15 @@ const ADGameDescription *TinselMetaEngine::fallbackDetect(const FileMap &allFile for (fileDesc = g->desc.filesDescriptions; fileDesc->fileName; fileDesc++) curFilesMatched++; - if (curFilesMatched > maxFilesMatched) { + if (curFilesMatched >= maxFilesMatched) { maxFilesMatched = curFilesMatched; - matched.clear(); // Remove any prior, lower ranked matches. - matched.push_back((const ADGameDescription *)g); - } else if (curFilesMatched == maxFilesMatched) { - matched.push_back((const ADGameDescription *)g); + matched = ADDetectedGame(&g->desc); } } } - // We didn't find a match - if (matched.empty()) - return NULL; - - return *matched.begin(); + return matched; } int TinselMetaEngine::getMaximumSaveSlot() const { return 99; } diff --git a/engines/tinsel/dialogs.cpp b/engines/tinsel/dialogs.cpp index b5d090ec15..755b9275c6 100644 --- a/engines/tinsel/dialogs.cpp +++ b/engines/tinsel/dialogs.cpp @@ -1907,6 +1907,11 @@ extern void HoldItem(int item, bool bKeepFilm) { invObj = GetInvObject(item); SetAuxCursor(invObj->hIconFilm); // and is aux. cursor } + + // WORKAROUND: If a held item is being removed that's not in either inventory (i.e. it was picked up + // but never put in them), then when removing it from being held, drop it in the luggage + if (g_heldItem != INV_NOICON && InventoryPos(g_heldItem) == INV_HELDNOTIN) + AddToInventory(INV_1, g_heldItem); } g_heldItem = item; // Item held diff --git a/engines/tinsel/multiobj.cpp b/engines/tinsel/multiobj.cpp index 75894aea89..2cd46ae90b 100644 --- a/engines/tinsel/multiobj.cpp +++ b/engines/tinsel/multiobj.cpp @@ -122,8 +122,7 @@ void MultiDeleteObject(OBJECT **pObjList, OBJECT *pMultiObj) { // next obj in list pMultiObj = pMultiObj->pSlave; - } - while (pMultiObj != NULL); + } while (pMultiObj != NULL); } /** @@ -180,8 +179,7 @@ void MultiVerticalFlip(OBJECT *pFlipObj) { // next obj in list pFlipObj = pFlipObj->pSlave; - } - while (pFlipObj != NULL); + } while (pFlipObj != NULL); } /** @@ -351,8 +349,7 @@ void MultiSetZPosition(OBJECT *pMultiObj, int newZ) { // next obj in list pMultiObj = pMultiObj->pSlave; - } - while (pMultiObj != NULL); + } while (pMultiObj != NULL); } /** diff --git a/engines/tinsel/pcode.cpp b/engines/tinsel/pcode.cpp index dc19f39405..8899eea65b 100644 --- a/engines/tinsel/pcode.cpp +++ b/engines/tinsel/pcode.cpp @@ -156,6 +156,7 @@ 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) }; +static const byte fragment15[] = { OP_JMPFALSE | OPSIZE16, FRAGMENT_WORD(154) }; #undef FRAGMENT_WORD @@ -226,6 +227,9 @@ const WorkaroundEntry workaroundList[] = { // quitting the game when no user input happens for a while {TINSEL_V1, true, true, Common::kPlatformPSX, 0, 2186, sizeof(fragment14), fragment14}, + // DW1-GRA: Fixes hang in Temple, when trying to use items on the big hammer + {TINSEL_V1, false, false, Common::kPlatformUnknown, 276915849, 0x98, sizeof(fragment15), fragment15}, + {TINSEL_V0, false, false, Common::kPlatformUnknown, 0, 0, 0, NULL} }; diff --git a/engines/titanic/continue_save_dialog.cpp b/engines/titanic/continue_save_dialog.cpp index 6de267e9fe..0aee87328b 100644 --- a/engines/titanic/continue_save_dialog.cpp +++ b/engines/titanic/continue_save_dialog.cpp @@ -185,7 +185,7 @@ void CContinueSaveDialog::mouseMove(const Point &mousePos) { void CContinueSaveDialog::leftButtonDown(const Point &mousePos) { Rect eye1(188, 190, 192, 195), eye2(209, 192, 213, 197); - if (g_vm->_events->isSpecialPressed(MK_SHIFT) && + if (g_vm->_events->isSpecialPressed(MK_SHIFT) && (eye1.contains(mousePos) || eye2.contains(mousePos))) { // Show the Easter Egg "Evil Twin" _evilTwinShown = true; diff --git a/engines/titanic/core/game_object.cpp b/engines/titanic/core/game_object.cpp index af34526329..d75bca0a7e 100644 --- a/engines/titanic/core/game_object.cpp +++ b/engines/titanic/core/game_object.cpp @@ -697,6 +697,7 @@ void CGameObject::playClip(uint startFrame, uint endFrame) { CRoomItem *room = gameManager->getRoom(); gameManager->playClip(clip, room, room); + delete clip; } void CGameObject::playRandomClip(const char *const *names, uint flags) { diff --git a/engines/titanic/core/list.h b/engines/titanic/core/list.h index 09068fb06a..6da3a9224a 100644 --- a/engines/titanic/core/list.h +++ b/engines/titanic/core/list.h @@ -106,7 +106,7 @@ public: for (uint idx = 0; idx < count; ++idx) { // Validate the class start header - if (!file->IsClassStart()) + if (!file->isClassStart()) error("Unexpected class end"); // Get item's class name and use it to instantiate an item @@ -120,7 +120,7 @@ public: Common::List<T *>::push_back(newItem); // Validate the class end footer - if (file->IsClassStart()) + if (file->isClassStart()) error("Unexpected class start"); } } diff --git a/engines/titanic/core/project_item.cpp b/engines/titanic/core/project_item.cpp index b2bd5cd92b..99eec803ac 100644 --- a/engines/titanic/core/project_item.cpp +++ b/engines/titanic/core/project_item.cpp @@ -190,16 +190,12 @@ void CProjectItem::loadGame(int slotId) { // Load the savegame header in TitanicSavegameHeader header; readSavegameHeader(&file, header); - if (header._thumbnail) { - header._thumbnail->free(); - delete header._thumbnail; - } g_vm->_events->setTotalPlayTicks(header._totalFrames); // Load the contents in CProjectItem *newProject = loadData(&file); - file.IsClassStart(); + file.isClassStart(); getGameManager()->load(&file); file.close(); @@ -254,7 +250,7 @@ void CProjectItem::clear() { } CProjectItem *CProjectItem::loadData(SimpleFile *file) { - if (!file->IsClassStart()) + if (!file->isClassStart()) return nullptr; CProjectItem *root = nullptr; @@ -295,8 +291,8 @@ CProjectItem *CProjectItem::loadData(SimpleFile *file) { item->load(file); } - file->IsClassStart(); - } while (file->IsClassStart()); + file->isClassStart(); + } while (file->isClassStart()); return root; } @@ -488,13 +484,9 @@ SaveStateList CProjectItem::getSavegameList(const Common::String &target) { if (in) { SimpleFile f; f.open(in); - if (!readSavegameHeader(&f, header)) - continue; - - saveList.push_back(SaveStateDescriptor(slot, header._saveName)); + if (readSavegameHeader(&f, header)) + saveList.push_back(SaveStateDescriptor(slot, header._saveName)); - header._thumbnail->free(); - delete header._thumbnail; delete in; } } @@ -503,7 +495,7 @@ SaveStateList CProjectItem::getSavegameList(const Common::String &target) { return saveList; } -bool CProjectItem::readSavegameHeader(SimpleFile *file, TitanicSavegameHeader &header) { +WARN_UNUSED_RESULT bool CProjectItem::readSavegameHeader(SimpleFile *file, TitanicSavegameHeader &header, bool skipThumbnail) { char saveIdentBuffer[SAVEGAME_STR_SIZE + 1]; header._thumbnail = nullptr; header._totalFrames = 0; @@ -526,8 +518,7 @@ bool CProjectItem::readSavegameHeader(SimpleFile *file, TitanicSavegameHeader &h while ((ch = (char)file->readByte()) != '\0') header._saveName += ch; // Get the thumbnail - header._thumbnail = Graphics::loadThumbnail(*file); - if (!header._thumbnail) + if (!Graphics::loadThumbnail(*file, header._thumbnail, skipThumbnail)) return false; // Read in save date/time diff --git a/engines/titanic/core/project_item.h b/engines/titanic/core/project_item.h index c9fd6f97cb..1c5923fd0e 100644 --- a/engines/titanic/core/project_item.h +++ b/engines/titanic/core/project_item.h @@ -155,7 +155,7 @@ public: /** * Read in the header information for a savegame */ - static bool readSavegameHeader(SimpleFile *file, TitanicSavegameHeader &header); + WARN_UNUSED_RESULT static bool readSavegameHeader(SimpleFile *file, TitanicSavegameHeader &header, bool skipThumbnail = true); public: CLASSDEF; CProjectItem(); diff --git a/engines/titanic/debugger.cpp b/engines/titanic/debugger.cpp index 7438a0053b..01948dd4d3 100644 --- a/engines/titanic/debugger.cpp +++ b/engines/titanic/debugger.cpp @@ -357,7 +357,7 @@ bool Debugger::cmdFrame(int argc, const char **argv) { if (argc == 3) { CGameObject *obj = dynamic_cast<CGameObject *>( g_vm->_window->_project->findByName(argv[1])); - + if (obj) { obj->loadFrame(strToInt(argv[2])); return false; diff --git a/engines/titanic/detection.cpp b/engines/titanic/detection.cpp index b33ac51bed..c98fbbdade 100644 --- a/engines/titanic/detection.cpp +++ b/engines/titanic/detection.cpp @@ -128,11 +128,6 @@ SaveStateList TitanicMetaEngine::listSaves(const char *target) const { if (Titanic::CProjectItem::readSavegameHeader(&cf, header)) saveList.push_back(SaveStateDescriptor(slot, header._saveName)); - if (header._thumbnail) { - header._thumbnail->free(); - delete header._thumbnail; - } - cf.close(); } } @@ -161,7 +156,10 @@ SaveStateDescriptor TitanicMetaEngine::querySaveMetaInfos(const char *target, in file.open(f); Titanic::TitanicSavegameHeader header; - Titanic::CProjectItem::readSavegameHeader(&file, header); + if (!Titanic::CProjectItem::readSavegameHeader(&file, header, false)) { + file.close(); + return SaveStateDescriptor(); + } file.close(); diff --git a/engines/titanic/game/bomb.cpp b/engines/titanic/game/bomb.cpp index d9eb737c34..40651c0a88 100644 --- a/engines/titanic/game/bomb.cpp +++ b/engines/titanic/game/bomb.cpp @@ -374,7 +374,7 @@ bool CBomb::TimerMsg(CTimerMsg *msg) { addTimer(0, 100, 0); return true; } - + if (msg->_actionVal == 0) { addTimer(1, 1000, 0); } else { @@ -459,7 +459,7 @@ bool CBomb::TimerMsg(CTimerMsg *msg) { --_countdown; addTimer(0, 1000, 0); } - + return true; } diff --git a/engines/titanic/game/chicken_dispensor.cpp b/engines/titanic/game/chicken_dispensor.cpp index 8d16289647..ced0c78ef1 100644 --- a/engines/titanic/game/chicken_dispensor.cpp +++ b/engines/titanic/game/chicken_dispensor.cpp @@ -106,7 +106,7 @@ bool CChickenDispensor::StatusChangeMsg(CStatusChangeMsg *msg) { bool CChickenDispensor::MovieEndMsg(CMovieEndMsg *msg) { int movieFrame = msg->_endFrame; - + if (movieFrame == 16) { // Dispensed a chicken _cursorId = CURSOR_HAND; diff --git a/engines/titanic/npcs/deskbot.cpp b/engines/titanic/npcs/deskbot.cpp index ee639c908f..c23fefce91 100644 --- a/engines/titanic/npcs/deskbot.cpp +++ b/engines/titanic/npcs/deskbot.cpp @@ -301,7 +301,7 @@ bool CDeskbot::TrueTalkNotifySpeechEndedMsg(CTrueTalkNotifySpeechEndedMsg *msg) CTurnOff turnOff; CTrueTalkNPC::TrueTalkNotifySpeechEndedMsg(msg); - + if (g_language == Common::DE_DEU) { switch (msg->_dialogueId) { case 41701: diff --git a/engines/titanic/pet_control/pet_load_save.cpp b/engines/titanic/pet_control/pet_load_save.cpp index d918478fb1..72770b9eb2 100644 --- a/engines/titanic/pet_control/pet_load_save.cpp +++ b/engines/titanic/pet_control/pet_load_save.cpp @@ -135,11 +135,6 @@ void CPetLoadSave::resetSlots() { _slotNames[idx].setText(header._saveName); } - if (header._thumbnail) { - header._thumbnail->free(); - delete header._thumbnail; - } - file.close(); } } diff --git a/engines/titanic/sound/music_room_handler.cpp b/engines/titanic/sound/music_room_handler.cpp index 2265e46bb2..364023ea3a 100644 --- a/engines/titanic/sound/music_room_handler.cpp +++ b/engines/titanic/sound/music_room_handler.cpp @@ -230,7 +230,7 @@ void CMusicRoomHandler::updateAudio() { } } } - + _audioBuffer->push(audioData, size); delete[] audioData; } diff --git a/engines/titanic/sound/music_room_instrument.cpp b/engines/titanic/sound/music_room_instrument.cpp index 882325c08a..99ead2f8eb 100644 --- a/engines/titanic/sound/music_room_instrument.cpp +++ b/engines/titanic/sound/music_room_instrument.cpp @@ -77,7 +77,7 @@ CMusicRoomInstrument::CMusicRoomInstrument(CProjectItem *project, CSoundManager _gameObjects[0] = static_cast<CGameObject *>(_project->findByName("Tubular Bells")); _insStartTime = 0.4; break; - + case MV_SNAKE: _gameObjects[0] = static_cast<CGameObject *>(_project->findByName("Snake_Hammer")); _gameObjects[1] = static_cast<CGameObject *>(_project->findByName("Snake_Glass")); diff --git a/engines/titanic/sound/music_song.cpp b/engines/titanic/sound/music_song.cpp index ea5f29a536..1645004c0a 100644 --- a/engines/titanic/sound/music_song.cpp +++ b/engines/titanic/sound/music_song.cpp @@ -127,7 +127,7 @@ bool CSongParser::parse(CValuePair &r) { } else if (_currentChar == '^') { if (_flag) break; - + _flag = true; r._data = 0x7FFFFFFF; r._length = _field10; @@ -176,7 +176,7 @@ bool CSongParser::parse(CValuePair &r) { FETCH_CHAR; } } - + if (!_flag) return false; diff --git a/engines/titanic/star_control/base_stars.cpp b/engines/titanic/star_control/base_stars.cpp index 0fcf8a964f..00206b1a9d 100644 --- a/engines/titanic/star_control/base_stars.cpp +++ b/engines/titanic/star_control/base_stars.cpp @@ -462,7 +462,7 @@ void CBaseStars::draw4(CSurfaceArea *surfaceArea, CStarCamera *camera, CStarClos + vector._z * pose._row3._z + pose._vector._z; if (tempZ <= minVal) continue; - + tempY = vector._x * pose._row1._y + vector._y * pose._row2._y + vector._z * pose._row3._y + pose._vector._y; tempX = vector._x * pose._row1._x + vector._y * pose._row2._x + vector._z * pose._row3._x + pose._vector._x; total2 = tempY * tempY + tempX * tempX + tempZ * tempZ; diff --git a/engines/titanic/star_control/camera_auto_mover.cpp b/engines/titanic/star_control/camera_auto_mover.cpp index 71f7de85b2..d8808653eb 100644 --- a/engines/titanic/star_control/camera_auto_mover.cpp +++ b/engines/titanic/star_control/camera_auto_mover.cpp @@ -78,7 +78,7 @@ void CCameraAutoMover::calcSpeeds(int val1, int val2, float distance) { _field40 = nMoverTransitions-1; _field48 = nMoverTransitions-1; _field3C = (double)val2 * _field38; - + // Calculate the speeds for a graduated movement between stars double base = 0.0, total = 0.0, power = 4.0, baseInc = 0.03125; for (int idx = nMoverTransitions - 1; idx >= 0; --idx) { @@ -86,7 +86,7 @@ void CCameraAutoMover::calcSpeeds(int val1, int val2, float distance) { total += _speeds[idx]; base += baseInc; } - + for (int idx = 0; idx < nMoverTransitions; ++idx) { _speeds[idx] = _speeds[idx] * _field3C / total; } diff --git a/engines/titanic/star_control/camera_auto_mover.h b/engines/titanic/star_control/camera_auto_mover.h index db57627e33..d9b2888fc8 100644 --- a/engines/titanic/star_control/camera_auto_mover.h +++ b/engines/titanic/star_control/camera_auto_mover.h @@ -62,17 +62,17 @@ public: /** * Clear src and dest orientation and set some default values for other fields - */ + */ void clear(); /** * Setup a transition to from one position to another - */ + */ void setPath(const FVector &srcV, const FVector &destV); /** * Applys speeds to the mover. More than one application is usually done for several transitions - */ + */ virtual MoverState move(CErrorCode &errorCode, FVector &pos, FMatrix &orientation) { return DONE_MOVING; } /** * Given a distance to cover, determines a bunch of speeds for a gradual transition diff --git a/engines/titanic/star_control/frect.h b/engines/titanic/star_control/frect.h index 654c578cfd..d792eb69b4 100644 --- a/engines/titanic/star_control/frect.h +++ b/engines/titanic/star_control/frect.h @@ -41,7 +41,7 @@ public: * Returns true if the rects equal */ bool operator==(const FRect &p) const; - + /** * Returns true if the rects are not equal */ diff --git a/engines/titanic/star_control/marked_auto_mover.h b/engines/titanic/star_control/marked_auto_mover.h index ca7fbf3b7f..d5f714b3ae 100644 --- a/engines/titanic/star_control/marked_auto_mover.h +++ b/engines/titanic/star_control/marked_auto_mover.h @@ -46,7 +46,7 @@ public: /** * Applys speeds to the mover. More than one application is usually done for several transitions - */ + */ virtual MoverState move(CErrorCode &errorCode, FVector &pos, FMatrix &orientation); }; diff --git a/engines/titanic/star_control/star_camera.h b/engines/titanic/star_control/star_camera.h index f2d27212fe..9d0c954765 100644 --- a/engines/titanic/star_control/star_camera.h +++ b/engines/titanic/star_control/star_camera.h @@ -234,7 +234,7 @@ public: * Lock in the first matched star marker */ bool lockMarker1(FVector v1, FVector v2, FVector v3); - + /** * Lock in the second matched star marker */ diff --git a/engines/titanic/star_control/star_closeup.cpp b/engines/titanic/star_control/star_closeup.cpp index 6ec94bbf38..3bc3623d58 100644 --- a/engines/titanic/star_control/star_closeup.cpp +++ b/engines/titanic/star_control/star_closeup.cpp @@ -69,9 +69,9 @@ bool CStarCloseup::setup2(int val1, int val2) { const int VALUES1[] = { 0x800, 0xC00, 0x1000, 0x1400, 0x1800 }; const int VALUES2[] = { 0xF95BCD, 0xA505A0, 0xFFAD43, 0x98F4EB, 0xF3EFA5, 0, - 0xFFFFFF, 0x81EEF5, 0x5FFD3, 0x4EE4FA, 0x11C3FF, 0x28F3F4, - 0x36FCF2, 0x29F1FD, 0x29BCFD, 0x98E3F4, 0xBBF3D9, 0x8198F5, - 0x5BE4F9, 0x0D6E2, 0x74EEF6, 0x68DEF8 + 0xFFFFFF, 0x81EEF5, 0x5FFD3, 0x4EE4FA, 0x11C3FF, 0x28F3F4, + 0x36FCF2, 0x29F1FD, 0x29BCFD, 0x98E3F4, 0xBBF3D9, 0x8198F5, + 0x5BE4F9, 0x0D6E2, 0x74EEF6, 0x68DEF8 }; Entry *e = &_entries[0]; @@ -174,7 +174,7 @@ bool CStarCloseup::setup2(int val1, int val2) { e->_pixel2 = (val >> 8) & 0xff; e->_pixel3 = (val >> 16) & 0xff; e->_field8 = g_vm->getRandomNumber(3) + 3; - + e->_fieldC = g_vm->getRandomNumber(255); e->_field10 = FACTOR * (float)g_vm->getRandomNumber(15); e->_field14 = ((float)g_vm->getRandomNumber(0xfffffffe) @@ -293,7 +293,7 @@ void CStarCloseup::draw(const FPose &pose, const FVector &vector, const FVector } switch (starColor) { - case WHITE: + case WHITE: surfaceArea->setMode(SA_SOLID); surfaceArea->_pixel = MKTAG_BE(entryP->_pixel1, entryP->_pixel2, entryP->_pixel3, 0); @@ -424,7 +424,7 @@ void CStarCloseup::draw(const FPose &pose, const FVector &vector, const FVector surfaceArea->drawLine(FRect(grid1._position._x, grid1._position._y, grid2._position._x, grid2._position._y)); } - } + } break; case PINK: surfaceArea->setMode(SA_SOLID); diff --git a/engines/titanic/star_control/star_control.cpp b/engines/titanic/star_control/star_control.cpp index 8464262b31..7922a2f7f2 100644 --- a/engines/titanic/star_control/star_control.cpp +++ b/engines/titanic/star_control/star_control.cpp @@ -241,7 +241,7 @@ void CStarControl::doAction(StarControlAction action) { pet->starsSetReference(); break; } - + case STAR_FADE_IN: _view.fn3(true); break; diff --git a/engines/titanic/star_control/star_crosshairs.h b/engines/titanic/star_control/star_crosshairs.h index d60541c205..3d060fd91d 100644 --- a/engines/titanic/star_control/star_crosshairs.h +++ b/engines/titanic/star_control/star_crosshairs.h @@ -80,12 +80,12 @@ public: bool fn1(CStarField *starField, CSurfaceArea *surfaceArea, CStarCamera *camera); void fn2(CVideoSurface *surface, CStarField *starField, CStarMarkers *markers); - + /** * Increments the index for the number of matches */ void incMatches(); - + /** * Draw the crosshairs for a given star */ @@ -101,12 +101,12 @@ public: * Erase crosshairs for the most recently selected star */ void eraseCurrent(CSurfaceArea *surfaceArea); - + /** * Draw crosshairs at the given position */ void drawAt(const FPoint &pt, CSurfaceArea *surfaceArea); - + /** * Returns the position of the most recently selected star */ diff --git a/engines/titanic/star_control/star_field.h b/engines/titanic/star_control/star_field.h index bd3f8aecb6..3b1c3db2b5 100644 --- a/engines/titanic/star_control/star_field.h +++ b/engines/titanic/star_control/star_field.h @@ -78,17 +78,17 @@ public: void set2(int val); int get54() const; void set54(int val); - + /** * Gets the current display mode */ StarMode getMode() const; - + /** * Sets the display mode */ void setMode(StarMode mode); - + /** * Toggles whether the big box is visible */ diff --git a/engines/titanic/star_control/star_view.h b/engines/titanic/star_control/star_view.h index 553195b0c7..241efbcde5 100644 --- a/engines/titanic/star_control/star_view.h +++ b/engines/titanic/star_control/star_view.h @@ -148,14 +148,14 @@ public: * Toggles between starfield and photo modes */ void toggleMode(); - + void fn11(); /** * Toggles whether the viewpoint box is visible in the starfield */ void toggleBox(); - + void fn13(); void fn14(); @@ -163,7 +163,7 @@ public: * Called when the photograph is used on the navigation computer */ void setHasReference(); - + /** * Handles locking in a star */ diff --git a/engines/titanic/star_control/unmarked_camera_mover.cpp b/engines/titanic/star_control/unmarked_camera_mover.cpp index c879dc25e8..401f550bee 100644 --- a/engines/titanic/star_control/unmarked_camera_mover.cpp +++ b/engines/titanic/star_control/unmarked_camera_mover.cpp @@ -48,7 +48,7 @@ void CUnmarkedCameraMover::moveTo(const FVector &srcV, const FVector &destV, con void CUnmarkedCameraMover::transitionBetweenOrientations(const FVector &v1, const FVector &v2, const FVector &v3, const FMatrix &m) { if (isLocked()) decLockCount(); - + FVector vector1 = v1; FVector vector2 = v2; FPose matrix1 = vector2.getFrameTransform(vector1); diff --git a/engines/titanic/star_control/viewport.cpp b/engines/titanic/star_control/viewport.cpp index e368dfa317..d66ff423c6 100644 --- a/engines/titanic/star_control/viewport.cpp +++ b/engines/titanic/star_control/viewport.cpp @@ -178,7 +178,7 @@ void CViewport::randomizeOrientation() { FPose m1(X_AXIS, ranRotAngleX); FPose m2(Y_AXIS, ranRotAngleY); FPose m3(Z_AXIS, ranRotAngleZ); - + FPose s1(m1, m2); FPose s2(s1, m3); diff --git a/engines/titanic/star_control/viewport.h b/engines/titanic/star_control/viewport.h index 082d063233..ae42e84ea4 100644 --- a/engines/titanic/star_control/viewport.h +++ b/engines/titanic/star_control/viewport.h @@ -35,7 +35,7 @@ namespace Titanic { * For starview it should be white * For skyview it should be pink */ -enum StarColor { WHITE = 0, PINK = 2 }; +enum StarColor { WHITE = 0, PINK = 2 }; /** * Implements the viewport functionality for viewing the star field in @@ -111,14 +111,14 @@ public: * The view has changed between starview and skyview * Change the enum that tracks the color of the stars * Also change the X coordinate pixel offset used for star drawing - */ + */ void changeStarColorPixel(StarMode mode, double pixelOffSet); void reposition(double factor); /** * Applys a rotation matrix to the current * orientation - */ + */ void changeOrientation(const FMatrix &matrix); FPose getPose(); @@ -148,15 +148,15 @@ public: /** * Sets the center vector y angle * The actual center y value doesn't - * change untill reset is called - */ + * change untill reset is called + */ void setCenterYAngle(double angleDegrees); /** * Sets the center vector z angle * The actual center z value doesn't - * change untill reset is called - */ + * change untill reset is called + */ void setCenterZAngle(double angleDegrees); }; diff --git a/engines/titanic/support/avi_surface.cpp b/engines/titanic/support/avi_surface.cpp index 3b22a4fee2..ff439abe25 100644 --- a/engines/titanic/support/avi_surface.cpp +++ b/engines/titanic/support/avi_surface.cpp @@ -489,7 +489,7 @@ Graphics::ManagedSurface *AVISurface::duplicateTransparency() const { bool AVISurface::playCutscene(const Rect &r, uint startFrame, uint endFrame) { if (g_vm->shouldQuit()) return false; - + // TODO: Fixes slight "jumping back" when rotating in place in Top Of Well // balcony between two elevators. Need a more generalized fix at some point if (_movieName == "z48.avi") diff --git a/engines/titanic/support/simple_file.cpp b/engines/titanic/support/simple_file.cpp index 103f062ac6..83d731f7f2 100644 --- a/engines/titanic/support/simple_file.cpp +++ b/engines/titanic/support/simple_file.cpp @@ -388,7 +388,7 @@ void SimpleFile::writeIndent(uint indent) const { write("\t", 1); } -bool SimpleFile::IsClassStart() { +bool SimpleFile::isClassStart() { char c; do { diff --git a/engines/titanic/support/simple_file.h b/engines/titanic/support/simple_file.h index 01aaa86925..f71ef5b717 100644 --- a/engines/titanic/support/simple_file.h +++ b/engines/titanic/support/simple_file.h @@ -238,7 +238,7 @@ public: * an opening or closing squiggly bracket denoting a class * definition start or end. Returns true if it's a class start */ - bool IsClassStart(); + bool isClassStart(); /** * Write the starting header for a class definition diff --git a/engines/titanic/titanic.cpp b/engines/titanic/titanic.cpp index e0e4a07ce6..0931d91806 100644 --- a/engines/titanic/titanic.cpp +++ b/engines/titanic/titanic.cpp @@ -49,7 +49,6 @@ #include "common/translation.h" #include "engines/util.h" #include "graphics/scaler.h" -#include "graphics/thumbnail.h" #include "graphics/screen.h" #include "gui/saveload.h" @@ -191,7 +190,7 @@ void TitanicEngine::setRoomNames() { bool TitanicEngine::canLoadGameStateCurrently() { CGameManager *gameManager = _window->_gameManager; CScreenManager *screenMan = CScreenManager::_screenManagerPtr; - + if (!gameManager) // Allow loading from copyright screen and continue dialogs return true; @@ -256,11 +255,6 @@ CString TitanicEngine::getSavegameName(int slot) { TitanicSavegameHeader header; bool isValid = CProjectItem::readSavegameHeader(&file, header); - if (header._thumbnail) { - header._thumbnail->free(); - delete header._thumbnail; - } - file.close(); if (isValid) diff --git a/engines/titanic/true_talk/doorbot_script.cpp b/engines/titanic/true_talk/doorbot_script.cpp index d7389d1cb7..0b33704ba5 100644 --- a/engines/titanic/true_talk/doorbot_script.cpp +++ b/engines/titanic/true_talk/doorbot_script.cpp @@ -71,6 +71,8 @@ static const RoomDialogueId ROOM_DIALOGUES2_DE[] = { DoorbotScript::DoorbotScript(int val1, const char *charClass, int v2, const char *charName, int v3, int val2, int v4, int v5, int v6, int v7) : TTnpcScript(val1, charClass, v2, charName, v3, val2, v4, v5, v6, v7) { + _stateIndex = _doorbotState = 0; + loadRanges("Ranges/Doorbot"); loadResponses("Responses/Doorbot"); setupSentences(); @@ -108,7 +110,7 @@ void DoorbotScript::setupSentences() { int DoorbotScript::chooseResponse(const TTroomScript *roomScript, const TTsentence *sentence, uint tag) { if (tag == MKTAG('D', 'N', 'A', '1') || tag == MKTAG('H', 'H', 'G', 'Q') || tag == MKTAG('A', 'N', 'S', 'W') || tag == MKTAG('S', 'U', 'M', 'S')) { - if (_stateIndex > 9) + if (_stateIndex > 8) _stateIndex = 0; addResponse(TRANSLATE(STATE_ARRAY_EN[_stateIndex], STATE_ARRAY_DE[_stateIndex])); applyResponse(); diff --git a/engines/titanic/true_talk/tt_parser.cpp b/engines/titanic/true_talk/tt_parser.cpp index 5c48de4182..2caeef45dd 100644 --- a/engines/titanic/true_talk/tt_parser.cpp +++ b/engines/titanic/true_talk/tt_parser.cpp @@ -960,7 +960,7 @@ int TTparser::considerRequests(TTword *word) { case WC_ABSTRACT: if (word->_id != 300) { status = processModifiers(3, word); - } else if (!_conceptP->findByWordClass(WC_THING)) { + } else if (!_conceptP || !_conceptP->findByWordClass(WC_THING)) { status = processModifiers(3, word); } else { word->_id = atoi(word->_text.c_str()); @@ -1760,7 +1760,7 @@ void TTparser::preprocessGerman(TTstring &line) { continue; const char *wordEndP = p + _replacements4[idx].size(); - + for (int sIdx = 0; sIdx < 12; ++sIdx) { const char *suffixP = SUFFIXES[sIdx]; if (!strncmp(wordEndP, suffixP, strlen(suffixP))) { diff --git a/engines/titanic/true_talk/tt_talker.h b/engines/titanic/true_talk/tt_talker.h index 4f0b59c044..68b77d8b75 100644 --- a/engines/titanic/true_talk/tt_talker.h +++ b/engines/titanic/true_talk/tt_talker.h @@ -54,7 +54,7 @@ public: * End the speech */ void endSpeech(int val); - + /** * Called when a speech is finished, to signal to the associated character * that the speech is over diff --git a/engines/titanic/true_talk/tt_vocab.cpp b/engines/titanic/true_talk/tt_vocab.cpp index e9fc098749..0269e71cee 100644 --- a/engines/titanic/true_talk/tt_vocab.cpp +++ b/engines/titanic/true_talk/tt_vocab.cpp @@ -1,585 +1,585 @@ -/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can 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 "titanic/true_talk/tt_vocab.h"
-#include "titanic/true_talk/script_handler.h"
-#include "titanic/true_talk/tt_action.h"
-#include "titanic/true_talk/tt_adj.h"
-#include "titanic/true_talk/tt_major_word.h"
-#include "titanic/true_talk/tt_picture.h"
-#include "titanic/true_talk/tt_pronoun.h"
-#include "titanic/titanic.h"
-#include "titanic/translation.h"
-#include "common/file.h"
-
-namespace Titanic {
-
-TTvocab::TTvocab(VocabMode vocabMode): _headP(nullptr), _tailP(nullptr),
- _word(nullptr), _vocabMode(vocabMode) {
- load("STVOCAB");
-}
-
-TTvocab::~TTvocab() {
- if (_headP) {
- _headP->deleteSiblings();
- delete _headP;
- _headP = _tailP = nullptr;
- }
-}
-
-int TTvocab::load(const CString &name) {
- SimpleFile *file = g_vm->_exeResources._owner->openResource(name);
- int result = 0;
- bool skipFlag;
-
- while (!result && !file->eos()) {
- skipFlag = false;
- WordClass wordClass = (WordClass)file->readNumber();
- TTstring space(" ");
-
- switch (wordClass) {
- case WC_UNKNOWN: {
- if (_word)
- result = _word->readSyn(file);
- skipFlag = true;
- break;
- }
-
- case WC_ACTION: {
- TTaction *word = new TTaction(space, WC_UNKNOWN, 0, 0, 0);
- result = word->load(file);
- _word = word;
- break;
- }
-
- case WC_THING: {
- TTpicture *word = new TTpicture(space, WC_UNKNOWN, 0, 0, 0, 0, 0);
- result = word->load(file);
- _word = word;
- break;
- }
-
- case WC_ABSTRACT:
- case WC_ADVERB: {
- TTmajorWord *word = new TTmajorWord(space, WC_UNKNOWN, 0, 0);
- result = word->load(file, wordClass);
- _word = word;
- break;
- }
-
- case WC_ARTICLE:
- case WC_CONJUNCTION:
- case WC_PREPOSITION: {
- TTword *word = new TTword(space, WC_UNKNOWN, 0);
- result = word->load(file, wordClass);
- _word = word;
- break;
- }
-
- case WC_ADJECTIVE: {
- TTadj *word = new TTadj(space, WC_UNKNOWN, 0, 0, 0);
- result = word->load(file);
- _word = word;
- break;
- }
-
- case WC_PRONOUN: {
- TTpronoun *word = new TTpronoun(space, WC_UNKNOWN, 0, 0, 0);
- result = word->load(file);
- _word = word;
- break;
- }
-
- default:
- result = 4;
- break;
- }
-
- if (!skipFlag && _word) {
- if (result) {
- // Something wrong occurred, so delete word
- delete _word;
- _word = nullptr;
- } else {
- // Add the word to the master vocab list
- addWord(_word);
- }
- }
- }
-
- // Close resource and return result
- delete file;
- return result;
-}
-
-void TTvocab::addWord(TTword *word) {
- TTword *existingWord = g_language == Common::DE_DEU ? nullptr :
- findWord(word->_text);
-
- if (existingWord) {
- if (word->_synP) {
- // Move over the synonym
- existingWord->appendNode(word->_synP);
- word->_synP = nullptr;
- }
-
- _word = nullptr;
- if (word)
- delete word;
- } else if (_tailP) {
- _tailP->_nextP = word;
- _tailP = word;
- } else {
- if (!_headP)
- _headP = word;
-
- _tailP = word;
- }
-}
-
-TTword *TTvocab::findWord(const TTstring &str) {
- TTsynonym *tempNode = new TTsynonym();
- bool flag = false;
- TTword *word = _headP;
-
- while (word && !flag) {
- if (_vocabMode != VOCAB_MODE_EN || strcmp(word->c_str(), str)) {
- if (word->findSynByName(str, tempNode, _vocabMode))
- flag = true;
- else
- word = word->_nextP;
- } else {
- flag = true;
- }
- }
-
- delete tempNode;
- return word;
-}
-
-TTword *TTvocab::getWord(TTstring &str, TTword **srcWord) const {
- TTword *word = getPrimeWord(str, srcWord);
-
- if (!word) {
- TTstring tempStr(str);
- if (tempStr.size() > 2) {
- word = getSuffixedWord(tempStr, srcWord);
-
- if (!word)
- word = getPrefixedWord(tempStr, srcWord);
- }
- }
-
- return word;
-}
-
-TTword *TTvocab::getPrimeWord(TTstring &str, TTword **srcWord) const {
- TTsynonym tempSyn;
- char c = str.charAt(0);
- TTword *newWord = nullptr;
- TTword *vocabP;
-
- if (Common::isDigit(c)) {
- // Number
- vocabP = _headP;
- newWord = new TTword(str, WC_ABSTRACT, 300);
- } else {
- // Standard word
- for (vocabP = _headP; vocabP; vocabP = vocabP->_nextP) {
- if (_vocabMode == VOCAB_MODE_EN && !strcmp(str.c_str(), vocabP->c_str())) {
- newWord = vocabP->copy();
- newWord->_nextP = nullptr;
- newWord->setSyn(nullptr);
- break;
- } else if (vocabP->findSynByName(str, &tempSyn, _vocabMode)) {
- // Create a copy of the word and the found synonym
- TTsynonym *newSyn = new TTsynonym(tempSyn);
- newSyn->_nextP = newSyn->_priorP = nullptr;
- newWord = vocabP->copy();
- newWord->_nextP = nullptr;
- newWord->setSyn(newSyn);
- break;
- }
- }
- }
-
- if (srcWord)
- // Pass out the pointer to the original word
- *srcWord = vocabP;
-
- // Return the new copy of the word
- return newWord;
-}
-
-TTword *TTvocab::getSuffixedWord(TTstring &str, TTword **srcWord) const {
- TTstring tempStr(str);
- TTword *word = nullptr;
-
- if (g_language == Common::DE_DEU) {
- static const char *const SUFFIXES[11] = {
- "est", "em", "en", "er", "es", "et", "st",
- "s", "e", "n", "t"
- };
-
- for (int idx = 0; idx < 11; ++idx) {
- if (tempStr.hasSuffix(SUFFIXES[idx])) {
- tempStr.deleteSuffix(strlen(SUFFIXES[idx]));
- word = getPrimeWord(tempStr, srcWord);
- if (word)
- break;
- tempStr = str;
- }
- }
-
- if (word)
- word->setSynStr(str);
- return word;
- }
-
- if (tempStr.hasSuffix("s")) {
- tempStr.deleteSuffix(1);
- word = getPrimeWord(tempStr);
-
- if (!word) {
- if (!tempStr.hasSuffix("e")) {
- tempStr = str;
- } else {
- tempStr.deleteLastChar();
- word = getPrimeWord(tempStr);
- }
- }
-
- } else if (tempStr.hasSuffix("ing")) {
- tempStr.deleteSuffix(3);
- word = getPrimeWord(tempStr);
-
- if (word) {
- if (word->_wordClass == 1) {
- delete word;
- word = nullptr;
- } else {
- delete word;
- word = new TTadj(str, WC_ADJECTIVE, 0, 0, 0);
- }
- } else {
- tempStr += "e";
- word = getPrimeWord(tempStr);
-
- if (word) {
- if (word->_wordClass != 1) {
- delete word;
- word = new TTadj(str, WC_ADJECTIVE, 0, 0, 0);
- }
- } else {
- tempStr.deleteSuffix(2);
- word = getPrimeWord(tempStr);
-
- if (word) {
- if (word->_wordClass != 1) {
- delete word;
- word = new TTadj(str, WC_ADJECTIVE, 0, 0, 0);
- }
- } else {
- tempStr = str;
- }
- }
- }
-
- } else if (tempStr.hasSuffix("ed")) {
- tempStr.deleteSuffix(1);
- word = getPrimeWord(tempStr);
-
- if (!word) {
- tempStr.deleteSuffix(1);
- word = getPrimeWord(tempStr);
- }
-
- if (word) {
- if (word->_wordClass == WC_ACTION) {
- TTaction *action = dynamic_cast<TTaction *>(word);
- assert(action);
- action->setVal(1);
- }
- } else {
- tempStr = str;
- }
-
- } else if (tempStr.hasSuffix("ly")) {
- tempStr.deleteSuffix(2);
- word = getPrimeWord(tempStr);
-
- if (word) {
- delete word;
- word = new TTword(str, WC_ADVERB, 0);
- } else {
- tempStr = str;
- }
-
- } else if (tempStr.hasSuffix("er")) {
- tempStr.deleteSuffix(1);
- word = getPrimeWord(tempStr);
-
- if (word) {
- if (word->_wordClass == WC_ADJECTIVE) {
- TTadj *adj = static_cast<TTadj *>(word);
- int val1 = word->proc15();
- int val2 = word->proc15();
-
- if (val2 < 5) {
- if (--val1 > 0) {
- adj->adjFn1(val1);
- }
- } else {
- if (++val1 < 11) {
- adj->adjFn1(val1);
- }
- }
- }
- } else {
- tempStr.deleteSuffix(1);
- word = getPrimeWord(tempStr);
-
- if (word) {
- if (word->_wordClass == WC_ADJECTIVE) {
- TTadj *adj = dynamic_cast<TTadj *>(word);
- int val1 = word->proc15();
- int val2 = word->proc15();
-
- if (val2 < 5) {
- if (--val1 > 0) {
- adj->adjFn1(val1);
- }
- } else {
- if (++val1 < 11) {
- adj->adjFn1(val1);
- }
- }
- }
- } else {
- tempStr.deleteSuffix(1);
- word = getPrimeWord(tempStr);
-
- if (word && word->_wordClass == WC_ADJECTIVE) {
- TTadj *adj = dynamic_cast<TTadj *>(word);
- int val1 = word->proc15();
- int val2 = word->proc15();
-
- if (val2 < 5) {
- if (--val1 > 0) {
- adj->adjFn1(val1);
- }
- } else {
- if (++val1 < 11) {
- adj->adjFn1(val1);
- }
- }
- }
- }
- }
-
- } else if (tempStr.hasSuffix("est")) {
- tempStr.deleteSuffix(2);
- word = getPrimeWord(tempStr);
-
- if (word) {
- if (word->_wordClass == WC_ADJECTIVE) {
- TTadj *adj = static_cast<TTadj *>(word);
- int val1 = word->proc15();
- int val2 = word->proc15();
-
- if (val2 < 5) {
- if (--val1 > 0) {
- adj->adjFn1(val1);
- }
- } else {
- if (++val1 < 11) {
- adj->adjFn1(val1);
- }
- }
- }
- } else {
- tempStr.deleteSuffix(1);
- word = getPrimeWord(tempStr);
-
- if (word) {
- if (word->_wordClass == WC_ADJECTIVE) {
- TTadj *adj = dynamic_cast<TTadj *>(word);
- int val1 = word->proc15();
- int val2 = word->proc15();
-
- if (val2 < 5) {
- if (--val1 > 0) {
- adj->adjFn1(val1);
- }
- } else {
- if (++val1 < 11) {
- adj->adjFn1(val1);
- }
- }
- }
- } else {
- tempStr.deleteSuffix(1);
- word = getPrimeWord(tempStr);
-
- if (word) {
- TTadj *adj = dynamic_cast<TTadj *>(word);
- int val1 = word->proc15();
- int val2 = word->proc15();
-
- if (val2 < 5) {
- if (--val1 > 0) {
- adj->adjFn1(val1);
- }
- } else {
- if (++val1 < 11) {
- adj->adjFn1(val1);
- }
- }
- }
- }
- }
-
- } else if (tempStr.hasSuffix("s*")) {
- tempStr.deleteSuffix(2);
- word = getPrimeWord(tempStr);
-
- if (word) {
- if (word->_wordClass == WC_PRONOUN || word->_wordClass == WC_ADVERB) {
- delete word;
- TTstring isStr("is");
- word = getPrimeWord(isStr);
- } else {
- switch (word->_id) {
- case 200:
- if (word->proc10() == 2) {
- delete word;
- word = new TTpronoun(tempStr, WC_PRONOUN, 601, 0, 5);
- } else if (word->proc10() == 1) {
- delete word;
- word = new TTpronoun(tempStr, WC_PRONOUN, 601, 0, 4);
- }
- break;
-
- case 201:
- delete word;
- word = new TTpronoun(tempStr, WC_PRONOUN, 601, 0, 5);
- break;
-
- case 202:
- case 203:
- if (word->proc10() == 2) {
- delete word;
- word = new TTpronoun(tempStr, WC_PRONOUN, 601, 0, 5);
- } else {
- int val = word->proc10() == 1 ? 0 : 4;
- delete word;
- word = new TTpronoun(tempStr, WC_PRONOUN, 601, 0, val);
- }
- break;
-
- case 204:
- delete word;
- word = new TTpronoun(tempStr, WC_PRONOUN, 601, 0, 6);
- break;
-
- default:
- delete word;
- word = new TTpronoun(tempStr, WC_PRONOUN, 601, 0, 0);
- break;
- }
- }
- }
- }
-
- if (word)
- word->setSynStr(str);
-
- return word;
-}
-
-TTword *TTvocab::getPrefixedWord(TTstring &str, TTword **srcWord) const {
- TTstring tempStr(str);
- TTword *word = nullptr;
- int prefixLen = 0;
-
- if (tempStr.hasPrefix("pre")) {
- prefixLen = 3;
- } else if (tempStr.hasPrefix("re") || tempStr.hasPrefix("co")) {
- prefixLen = 2;
- } else if (tempStr.hasPrefix("inter") || tempStr.hasPrefix("multi")) {
- prefixLen = 5;
- } else if (tempStr.hasPrefix("over") || tempStr.hasPrefix("post") || tempStr.hasPrefix("self")) {
- prefixLen = 4;
- }
-
- if (prefixLen) {
- // Known prefix found, so scan for word without prefix
- tempStr.deletePrefix(prefixLen);
- word = getPrimeWord(tempStr);
- if (word)
- tempStr = str;
-
- } else if (tempStr.hasPrefix("anti") || tempStr.hasPrefix("counter")) {
- prefixLen = tempStr[0] == 'a' ? 4 : 7;
-
- tempStr.deletePrefix(prefixLen);
- word = getPrimeWord(tempStr);
- if (!word)
- tempStr = str;
- else if (word->_wordClass == 8) {
- delete word;
- word = nullptr;
- }
-
- } else if (tempStr.hasPrefix("hyper") || tempStr.hasPrefix("super") ||
- tempStr.hasPrefix("ultra")) {
- tempStr.deletePrefix(5);
- word = getPrimeWord(tempStr);
-
- if (!word)
- tempStr = str;
- else if (word->_wordClass == WC_ADJECTIVE) {
- TTadj *adj = static_cast<TTadj *>(word);
- int val1 = word->proc15();
- int val2 = word->proc15();
-
- if (val2 < 5) {
- if (--val1 > 0)
- adj->adjFn1(val1);
- } else if (++val1 < 11) {
- adj->adjFn1(val1);
- }
- }
- }
-
- if (word) {
- // Set the original word on either the found word or synonym
- if (word->hasSynonyms())
- word->setSynStr(str);
- else
- word->_text = str;
- }
-
- return word;
-}
-
-} // End of namespace Titanic
+/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can 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 "titanic/true_talk/tt_vocab.h" +#include "titanic/true_talk/script_handler.h" +#include "titanic/true_talk/tt_action.h" +#include "titanic/true_talk/tt_adj.h" +#include "titanic/true_talk/tt_major_word.h" +#include "titanic/true_talk/tt_picture.h" +#include "titanic/true_talk/tt_pronoun.h" +#include "titanic/titanic.h" +#include "titanic/translation.h" +#include "common/file.h" + +namespace Titanic { + +TTvocab::TTvocab(VocabMode vocabMode): _headP(nullptr), _tailP(nullptr), + _word(nullptr), _vocabMode(vocabMode) { + load("STVOCAB"); +} + +TTvocab::~TTvocab() { + if (_headP) { + _headP->deleteSiblings(); + delete _headP; + _headP = _tailP = nullptr; + } +} + +int TTvocab::load(const CString &name) { + SimpleFile *file = g_vm->_exeResources._owner->openResource(name); + int result = 0; + bool skipFlag; + + while (!result && !file->eos()) { + skipFlag = false; + WordClass wordClass = (WordClass)file->readNumber(); + TTstring space(" "); + + switch (wordClass) { + case WC_UNKNOWN: { + if (_word) + result = _word->readSyn(file); + skipFlag = true; + break; + } + + case WC_ACTION: { + TTaction *word = new TTaction(space, WC_UNKNOWN, 0, 0, 0); + result = word->load(file); + _word = word; + break; + } + + case WC_THING: { + TTpicture *word = new TTpicture(space, WC_UNKNOWN, 0, 0, 0, 0, 0); + result = word->load(file); + _word = word; + break; + } + + case WC_ABSTRACT: + case WC_ADVERB: { + TTmajorWord *word = new TTmajorWord(space, WC_UNKNOWN, 0, 0); + result = word->load(file, wordClass); + _word = word; + break; + } + + case WC_ARTICLE: + case WC_CONJUNCTION: + case WC_PREPOSITION: { + TTword *word = new TTword(space, WC_UNKNOWN, 0); + result = word->load(file, wordClass); + _word = word; + break; + } + + case WC_ADJECTIVE: { + TTadj *word = new TTadj(space, WC_UNKNOWN, 0, 0, 0); + result = word->load(file); + _word = word; + break; + } + + case WC_PRONOUN: { + TTpronoun *word = new TTpronoun(space, WC_UNKNOWN, 0, 0, 0); + result = word->load(file); + _word = word; + break; + } + + default: + result = 4; + break; + } + + if (!skipFlag && _word) { + if (result) { + // Something wrong occurred, so delete word + delete _word; + _word = nullptr; + } else { + // Add the word to the master vocab list + addWord(_word); + } + } + } + + // Close resource and return result + delete file; + return result; +} + +void TTvocab::addWord(TTword *word) { + TTword *existingWord = g_language == Common::DE_DEU ? nullptr : + findWord(word->_text); + + if (existingWord) { + if (word->_synP) { + // Move over the synonym + existingWord->appendNode(word->_synP); + word->_synP = nullptr; + } + + _word = nullptr; + if (word) + delete word; + } else if (_tailP) { + _tailP->_nextP = word; + _tailP = word; + } else { + if (!_headP) + _headP = word; + + _tailP = word; + } +} + +TTword *TTvocab::findWord(const TTstring &str) { + TTsynonym *tempNode = new TTsynonym(); + bool flag = false; + TTword *word = _headP; + + while (word && !flag) { + if (_vocabMode != VOCAB_MODE_EN || strcmp(word->c_str(), str)) { + if (word->findSynByName(str, tempNode, _vocabMode)) + flag = true; + else + word = word->_nextP; + } else { + flag = true; + } + } + + delete tempNode; + return word; +} + +TTword *TTvocab::getWord(TTstring &str, TTword **srcWord) const { + TTword *word = getPrimeWord(str, srcWord); + + if (!word) { + TTstring tempStr(str); + if (tempStr.size() > 2) { + word = getSuffixedWord(tempStr, srcWord); + + if (!word) + word = getPrefixedWord(tempStr, srcWord); + } + } + + return word; +} + +TTword *TTvocab::getPrimeWord(TTstring &str, TTword **srcWord) const { + TTsynonym tempSyn; + char c = str.charAt(0); + TTword *newWord = nullptr; + TTword *vocabP; + + if (Common::isDigit(c)) { + // Number + vocabP = _headP; + newWord = new TTword(str, WC_ABSTRACT, 300); + } else { + // Standard word + for (vocabP = _headP; vocabP; vocabP = vocabP->_nextP) { + if (_vocabMode == VOCAB_MODE_EN && !strcmp(str.c_str(), vocabP->c_str())) { + newWord = vocabP->copy(); + newWord->_nextP = nullptr; + newWord->setSyn(nullptr); + break; + } else if (vocabP->findSynByName(str, &tempSyn, _vocabMode)) { + // Create a copy of the word and the found synonym + TTsynonym *newSyn = new TTsynonym(tempSyn); + newSyn->_nextP = newSyn->_priorP = nullptr; + newWord = vocabP->copy(); + newWord->_nextP = nullptr; + newWord->setSyn(newSyn); + break; + } + } + } + + if (srcWord) + // Pass out the pointer to the original word + *srcWord = vocabP; + + // Return the new copy of the word + return newWord; +} + +TTword *TTvocab::getSuffixedWord(TTstring &str, TTword **srcWord) const { + TTstring tempStr(str); + TTword *word = nullptr; + + if (g_language == Common::DE_DEU) { + static const char *const SUFFIXES[11] = { + "est", "em", "en", "er", "es", "et", "st", + "s", "e", "n", "t" + }; + + for (int idx = 0; idx < 11; ++idx) { + if (tempStr.hasSuffix(SUFFIXES[idx])) { + tempStr.deleteSuffix(strlen(SUFFIXES[idx])); + word = getPrimeWord(tempStr, srcWord); + if (word) + break; + tempStr = str; + } + } + + if (word) + word->setSynStr(str); + return word; + } + + if (tempStr.hasSuffix("s")) { + tempStr.deleteSuffix(1); + word = getPrimeWord(tempStr); + + if (!word) { + if (!tempStr.hasSuffix("e")) { + tempStr = str; + } else { + tempStr.deleteLastChar(); + word = getPrimeWord(tempStr); + } + } + + } else if (tempStr.hasSuffix("ing")) { + tempStr.deleteSuffix(3); + word = getPrimeWord(tempStr); + + if (word) { + if (word->_wordClass == 1) { + delete word; + word = nullptr; + } else { + delete word; + word = new TTadj(str, WC_ADJECTIVE, 0, 0, 0); + } + } else { + tempStr += "e"; + word = getPrimeWord(tempStr); + + if (word) { + if (word->_wordClass != 1) { + delete word; + word = new TTadj(str, WC_ADJECTIVE, 0, 0, 0); + } + } else { + tempStr.deleteSuffix(2); + word = getPrimeWord(tempStr); + + if (word) { + if (word->_wordClass != 1) { + delete word; + word = new TTadj(str, WC_ADJECTIVE, 0, 0, 0); + } + } else { + tempStr = str; + } + } + } + + } else if (tempStr.hasSuffix("ed")) { + tempStr.deleteSuffix(1); + word = getPrimeWord(tempStr); + + if (!word) { + tempStr.deleteSuffix(1); + word = getPrimeWord(tempStr); + } + + if (word) { + if (word->_wordClass == WC_ACTION) { + TTaction *action = dynamic_cast<TTaction *>(word); + assert(action); + action->setVal(1); + } + } else { + tempStr = str; + } + + } else if (tempStr.hasSuffix("ly")) { + tempStr.deleteSuffix(2); + word = getPrimeWord(tempStr); + + if (word) { + delete word; + word = new TTword(str, WC_ADVERB, 0); + } else { + tempStr = str; + } + + } else if (tempStr.hasSuffix("er")) { + tempStr.deleteSuffix(1); + word = getPrimeWord(tempStr); + + if (word) { + if (word->_wordClass == WC_ADJECTIVE) { + TTadj *adj = static_cast<TTadj *>(word); + int val1 = word->proc15(); + int val2 = word->proc15(); + + if (val2 < 5) { + if (--val1 > 0) { + adj->adjFn1(val1); + } + } else { + if (++val1 < 11) { + adj->adjFn1(val1); + } + } + } + } else { + tempStr.deleteSuffix(1); + word = getPrimeWord(tempStr); + + if (word) { + if (word->_wordClass == WC_ADJECTIVE) { + TTadj *adj = dynamic_cast<TTadj *>(word); + int val1 = word->proc15(); + int val2 = word->proc15(); + + if (val2 < 5) { + if (--val1 > 0) { + adj->adjFn1(val1); + } + } else { + if (++val1 < 11) { + adj->adjFn1(val1); + } + } + } + } else { + tempStr.deleteSuffix(1); + word = getPrimeWord(tempStr); + + if (word && word->_wordClass == WC_ADJECTIVE) { + TTadj *adj = dynamic_cast<TTadj *>(word); + int val1 = word->proc15(); + int val2 = word->proc15(); + + if (val2 < 5) { + if (--val1 > 0) { + adj->adjFn1(val1); + } + } else { + if (++val1 < 11) { + adj->adjFn1(val1); + } + } + } + } + } + + } else if (tempStr.hasSuffix("est")) { + tempStr.deleteSuffix(2); + word = getPrimeWord(tempStr); + + if (word) { + if (word->_wordClass == WC_ADJECTIVE) { + TTadj *adj = static_cast<TTadj *>(word); + int val1 = word->proc15(); + int val2 = word->proc15(); + + if (val2 < 5) { + if (--val1 > 0) { + adj->adjFn1(val1); + } + } else { + if (++val1 < 11) { + adj->adjFn1(val1); + } + } + } + } else { + tempStr.deleteSuffix(1); + word = getPrimeWord(tempStr); + + if (word) { + if (word->_wordClass == WC_ADJECTIVE) { + TTadj *adj = dynamic_cast<TTadj *>(word); + int val1 = word->proc15(); + int val2 = word->proc15(); + + if (val2 < 5) { + if (--val1 > 0) { + adj->adjFn1(val1); + } + } else { + if (++val1 < 11) { + adj->adjFn1(val1); + } + } + } + } else { + tempStr.deleteSuffix(1); + word = getPrimeWord(tempStr); + + if (word) { + TTadj *adj = dynamic_cast<TTadj *>(word); + int val1 = word->proc15(); + int val2 = word->proc15(); + + if (val2 < 5) { + if (--val1 > 0) { + adj->adjFn1(val1); + } + } else { + if (++val1 < 11) { + adj->adjFn1(val1); + } + } + } + } + } + + } else if (tempStr.hasSuffix("s*")) { + tempStr.deleteSuffix(2); + word = getPrimeWord(tempStr); + + if (word) { + if (word->_wordClass == WC_PRONOUN || word->_wordClass == WC_ADVERB) { + delete word; + TTstring isStr("is"); + word = getPrimeWord(isStr); + } else { + switch (word->_id) { + case 200: + if (word->proc10() == 2) { + delete word; + word = new TTpronoun(tempStr, WC_PRONOUN, 601, 0, 5); + } else if (word->proc10() == 1) { + delete word; + word = new TTpronoun(tempStr, WC_PRONOUN, 601, 0, 4); + } + break; + + case 201: + delete word; + word = new TTpronoun(tempStr, WC_PRONOUN, 601, 0, 5); + break; + + case 202: + case 203: + if (word->proc10() == 2) { + delete word; + word = new TTpronoun(tempStr, WC_PRONOUN, 601, 0, 5); + } else { + int val = word->proc10() == 1 ? 0 : 4; + delete word; + word = new TTpronoun(tempStr, WC_PRONOUN, 601, 0, val); + } + break; + + case 204: + delete word; + word = new TTpronoun(tempStr, WC_PRONOUN, 601, 0, 6); + break; + + default: + delete word; + word = new TTpronoun(tempStr, WC_PRONOUN, 601, 0, 0); + break; + } + } + } + } + + if (word) + word->setSynStr(str); + + return word; +} + +TTword *TTvocab::getPrefixedWord(TTstring &str, TTword **srcWord) const { + TTstring tempStr(str); + TTword *word = nullptr; + int prefixLen = 0; + + if (tempStr.hasPrefix("pre")) { + prefixLen = 3; + } else if (tempStr.hasPrefix("re") || tempStr.hasPrefix("co")) { + prefixLen = 2; + } else if (tempStr.hasPrefix("inter") || tempStr.hasPrefix("multi")) { + prefixLen = 5; + } else if (tempStr.hasPrefix("over") || tempStr.hasPrefix("post") || tempStr.hasPrefix("self")) { + prefixLen = 4; + } + + if (prefixLen) { + // Known prefix found, so scan for word without prefix + tempStr.deletePrefix(prefixLen); + word = getPrimeWord(tempStr); + if (word) + tempStr = str; + + } else if (tempStr.hasPrefix("anti") || tempStr.hasPrefix("counter")) { + prefixLen = tempStr[0] == 'a' ? 4 : 7; + + tempStr.deletePrefix(prefixLen); + word = getPrimeWord(tempStr); + if (!word) + tempStr = str; + else if (word->_wordClass == 8) { + delete word; + word = nullptr; + } + + } else if (tempStr.hasPrefix("hyper") || tempStr.hasPrefix("super") || + tempStr.hasPrefix("ultra")) { + tempStr.deletePrefix(5); + word = getPrimeWord(tempStr); + + if (!word) + tempStr = str; + else if (word->_wordClass == WC_ADJECTIVE) { + TTadj *adj = static_cast<TTadj *>(word); + int val1 = word->proc15(); + int val2 = word->proc15(); + + if (val2 < 5) { + if (--val1 > 0) + adj->adjFn1(val1); + } else if (++val1 < 11) { + adj->adjFn1(val1); + } + } + } + + if (word) { + // Set the original word on either the found word or synonym + if (word->hasSynonyms()) + word->setSynStr(str); + else + word->_text = str; + } + + return word; +} + +} // End of namespace Titanic diff --git a/engines/toltecs/detection.cpp b/engines/toltecs/detection.cpp index 9303760057..0cd9596c8c 100644 --- a/engines/toltecs/detection.cpp +++ b/engines/toltecs/detection.cpp @@ -276,7 +276,7 @@ SaveStateList ToltecsMetaEngine::listSaves(const char *target) const { if (slotNum >= 0 && slotNum <= 999) { Common::InSaveFile *in = saveFileMan->openForLoading(file->c_str()); if (in) { - if (Toltecs::ToltecsEngine::readSaveHeader(in, false, header) == Toltecs::ToltecsEngine::kRSHENoError) { + if (Toltecs::ToltecsEngine::readSaveHeader(in, header) == Toltecs::ToltecsEngine::kRSHENoError) { saveList.push_back(SaveStateDescriptor(slotNum, header.description)); } delete in; @@ -325,7 +325,7 @@ SaveStateDescriptor ToltecsMetaEngine::querySaveMetaInfos(const char *target, in Toltecs::ToltecsEngine::SaveHeader header; Toltecs::ToltecsEngine::kReadSaveHeaderError error; - error = Toltecs::ToltecsEngine::readSaveHeader(in, true, header); + error = Toltecs::ToltecsEngine::readSaveHeader(in, header, false); delete in; if (error == Toltecs::ToltecsEngine::kRSHENoError) { diff --git a/engines/toltecs/menu.cpp b/engines/toltecs/menu.cpp index 5fc0599c2a..c4373265e6 100644 --- a/engines/toltecs/menu.cpp +++ b/engines/toltecs/menu.cpp @@ -525,7 +525,7 @@ int MenuSystem::loadSavegamesList() { if (slotNum >= 0 && slotNum <= 999) { Common::InSaveFile *in = saveFileMan->openForLoading(file->c_str()); if (in) { - if (Toltecs::ToltecsEngine::readSaveHeader(in, false, header) == Toltecs::ToltecsEngine::kRSHENoError) { + if (Toltecs::ToltecsEngine::readSaveHeader(in, header) == Toltecs::ToltecsEngine::kRSHENoError) { _savegames.push_back(SavegameItem(slotNum, header.description)); //debug("%s -> %s", file->c_str(), header.description.c_str()); } diff --git a/engines/toltecs/saveload.cpp b/engines/toltecs/saveload.cpp index 409fc97076..1f2198fc35 100644 --- a/engines/toltecs/saveload.cpp +++ b/engines/toltecs/saveload.cpp @@ -41,7 +41,7 @@ namespace Toltecs { #define TOLTECS_SAVEGAME_VERSION 4 -ToltecsEngine::kReadSaveHeaderError ToltecsEngine::readSaveHeader(Common::SeekableReadStream *in, bool loadThumbnail, SaveHeader &header) { +WARN_UNUSED_RESULT ToltecsEngine::kReadSaveHeaderError ToltecsEngine::readSaveHeader(Common::SeekableReadStream *in, SaveHeader &header, bool skipThumbnail) { header.version = in->readUint32LE(); if (header.version > TOLTECS_SAVEGAME_VERSION) @@ -52,10 +52,8 @@ ToltecsEngine::kReadSaveHeaderError ToltecsEngine::readSaveHeader(Common::Seekab while (descriptionLen--) header.description += (char)in->readByte(); - if (loadThumbnail) { - header.thumbnail = Graphics::loadThumbnail(*in); - } else { - Graphics::skipThumbnail(*in); + if (!Graphics::loadThumbnail(*in, header.thumbnail, skipThumbnail)) { + return kRSHEIoError; } // Not used yet, reserved for future usage @@ -147,7 +145,7 @@ void ToltecsEngine::loadgame(const char *filename) { SaveHeader header; - kReadSaveHeaderError errorCode = readSaveHeader(in, false, header); + kReadSaveHeaderError errorCode = readSaveHeader(in, header); if (errorCode != kRSHENoError) { warning("Error loading savegame '%s'", filename); diff --git a/engines/toltecs/toltecs.h b/engines/toltecs/toltecs.h index ece82f4a1a..1c9e9366c7 100644 --- a/engines/toltecs/toltecs.h +++ b/engines/toltecs/toltecs.h @@ -225,7 +225,7 @@ public: const char *getSavegameFilename(int num); static Common::String getSavegameFilename(const Common::String &target, int num); - static kReadSaveHeaderError readSaveHeader(Common::SeekableReadStream *in, bool loadThumbnail, SaveHeader &header); + WARN_UNUSED_RESULT static kReadSaveHeaderError readSaveHeader(Common::SeekableReadStream *in, SaveHeader &header, bool skipThumbnail = true); }; diff --git a/engines/tony/gfxcore.cpp b/engines/tony/gfxcore.cpp index 27145d7c4b..a59d14d5b6 100644 --- a/engines/tony/gfxcore.cpp +++ b/engines/tony/gfxcore.cpp @@ -912,7 +912,7 @@ void RMGfxSourceBuffer8RLE::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPri if (prim->isFlipped()) { // Eliminate horizontal clipping // width = m_dimx; -// x1=prim->Dst().x1; +// x1=prim->getDst()._x1; // Clipping u = _dimx - (width + u); @@ -1699,7 +1699,7 @@ void RMGfxSourceBuffer8AA::drawAA(RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *pri } //width = _dimx; - //x1 = prim->Dst().x1; + //x1 = prim->getDst()._x1; // Position into the destination buffer buf = bigBuf; @@ -1714,7 +1714,7 @@ void RMGfxSourceBuffer8AA::drawAA(RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *pri // Loop buf += bigBuf.getDimx(); // Skip the first line for (int y = 1; y < height - 1; y++) { - // if (prim->IsFlipped()) + // if (prim->isFlipped()) // mybuf=&buf[x1+m_dimx-1]; // else mybuf = &buf[x1]; @@ -1748,7 +1748,7 @@ void RMGfxSourceBuffer8AA::drawAA(RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *pri // Looppone buf += bigBuf.getDimx(); for (int y = 1; y < height - 1; y++) { - // if (prim->IsFlipped()) + // if (prim->isFlipped()) // mybuf=&buf[x1+m_dimx-1]; // else mybuf = &buf[x1]; diff --git a/engines/tony/loc.cpp b/engines/tony/loc.cpp index be2b20294f..fe553b5fab 100644 --- a/engines/tony/loc.cpp +++ b/engines/tony/loc.cpp @@ -1005,7 +1005,7 @@ void RMCharacter::goTo(CORO_PARAM, RMPoint destcoord, bool bReversed) { _walkCount = 0; if (bReversed) { - while (0) ; + while (0); } int nPatt = getCurPattern(); diff --git a/engines/tony/mpal/lzo.cpp b/engines/tony/mpal/lzo.cpp index b9accb6c89..1bb54af1f0 100644 --- a/engines/tony/mpal/lzo.cpp +++ b/engines/tony/mpal/lzo.cpp @@ -94,9 +94,9 @@ int lzo1x_decompress(const byte *in, uint32 in_len, byte *out, uint32 *out_len) if (t < 4) goto match_next; assert(t > 0); - do + do { *op++ = *ip++; - while (--t > 0); + } while (--t > 0); goto first_literal_run; } @@ -116,9 +116,9 @@ int lzo1x_decompress(const byte *in, uint32 in_len, byte *out, uint32 *out_len) *op++ = *ip++; *op++ = *ip++; *op++ = *ip++; - do + do { *op++ = *ip++; - while (--t > 0); + } while (--t > 0); first_literal_run: t = *ip++; @@ -186,9 +186,9 @@ match: copy_match: *op++ = *m_pos++; *op++ = *m_pos++; - do + do { *op++ = *m_pos++; - while (--t > 0); + } while (--t > 0); } match_done: diff --git a/engines/tony/sound.cpp b/engines/tony/sound.cpp index fed51dacf4..07d15e71c9 100644 --- a/engines/tony/sound.cpp +++ b/engines/tony/sound.cpp @@ -412,11 +412,7 @@ void FPSfx::setPause(bool pause) { * */ void FPSfx::setVolume(int volume) { - if (volume > 63) - volume = 63; - - if (volume < 0) - volume = 0; + volume = CLIP(volume, 0, 63); _lastVolume = volume; diff --git a/engines/toon/detection.cpp b/engines/toon/detection.cpp index e93d676d87..634d286c7c 100644 --- a/engines/toon/detection.cpp +++ b/engines/toon/detection.cpp @@ -132,7 +132,7 @@ public: _directoryGlobs = directoryGlobs; } - virtual const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const { + ADDetectedGame fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const override { return detectGameFilebased(allFiles, fslist, Toon::fileBasedFallback); } @@ -232,7 +232,11 @@ SaveStateDescriptor ToonMetaEngine::querySaveMetaInfos(const char *target, int s SaveStateDescriptor desc(slot, saveName); - Graphics::Surface *const thumbnail = Graphics::loadThumbnail(*file); + Graphics::Surface *thumbnail; + if (!Graphics::loadThumbnail(*file, thumbnail)) { + delete file; + return SaveStateDescriptor(); + } desc.setThumbnail(thumbnail); uint32 saveDate = file->readUint32BE(); diff --git a/engines/toon/flux.cpp b/engines/toon/flux.cpp index 14218ba4ef..65ad66ffd8 100644 --- a/engines/toon/flux.cpp +++ b/engines/toon/flux.cpp @@ -86,8 +86,7 @@ int32 CharacterFlux::fixFacingForAnimation(int32 originalFacing, int32 animation } v5 >>= 1; v6 <<= 1; - } - while (!facingMask); + } while (!facingMask); int32 finalFacing = 0; for (finalFacing = 0; ; ++finalFacing) { diff --git a/engines/toon/hotspot.cpp b/engines/toon/hotspot.cpp index 04368df5ca..cdc6dc40d9 100644 --- a/engines/toon/hotspot.cpp +++ b/engines/toon/hotspot.cpp @@ -57,8 +57,8 @@ void Hotspots::save(Common::WriteStream *Stream) { } } -int32 Hotspots::FindBasedOnCorner(int16 x, int16 y) { - debugC(1, kDebugHotspot, "FindBasedOnCorner(%d, %d)", x, y); +int32 Hotspots::findBasedOnCorner(int16 x, int16 y) { + debugC(1, kDebugHotspot, "findBasedOnCorner(%d, %d)", x, y); for (int32 i = 0; i < _numItems; i++) { if (x == _items[i].getX1()) { @@ -73,8 +73,8 @@ int32 Hotspots::FindBasedOnCorner(int16 x, int16 y) { return -1; } -int32 Hotspots::Find(int16 x, int16 y) { - debugC(6, kDebugHotspot, "Find(%d, %d)", x, y); +int32 Hotspots::find(int16 x, int16 y) { + debugC(6, kDebugHotspot, "find(%d, %d)", x, y); int32 priority = -1; int32 foundId = -1; @@ -96,8 +96,8 @@ int32 Hotspots::Find(int16 x, int16 y) { return foundId; } -bool Hotspots::LoadRif(const Common::String &rifName, const Common::String &additionalRifName) { - debugC(1, kDebugHotspot, "LoadRif(%s, %s)", rifName.c_str(), additionalRifName.c_str()); +bool Hotspots::loadRif(const Common::String &rifName, const Common::String &additionalRifName) { + debugC(1, kDebugHotspot, "loadRif(%s, %s)", rifName.c_str(), additionalRifName.c_str()); uint32 size = 0; uint8 *rifData = _vm->resources()->getFileData(rifName, &size); @@ -139,8 +139,8 @@ bool Hotspots::LoadRif(const Common::String &rifName, const Common::String &addi return true; } -HotspotData *Hotspots::Get(int32 id) { - debugC(5, kDebugHotspot, "Get(%d)", id); +HotspotData *Hotspots::get(int32 id) { + debugC(5, kDebugHotspot, "get(%d)", id); if (id < 0 || id >= _numItems) return 0; diff --git a/engines/toon/hotspot.h b/engines/toon/hotspot.h index 782d06a9fe..387007cbf6 100644 --- a/engines/toon/hotspot.h +++ b/engines/toon/hotspot.h @@ -50,10 +50,10 @@ public: Hotspots(ToonEngine *vm); ~Hotspots(); - bool LoadRif(const Common::String &rifName, const Common::String &additionalRifName); - int32 Find(int16 x, int16 y); - int32 FindBasedOnCorner(int16 x, int16 y); - HotspotData *Get(int32 id); + bool loadRif(const Common::String &rifName, const Common::String &additionalRifName); + int32 find(int16 x, int16 y); + int32 findBasedOnCorner(int16 x, int16 y); + HotspotData *get(int32 id); int32 getCount() const { return _numItems; } void load(Common::ReadStream *Stream); diff --git a/engines/toon/script_func.cpp b/engines/toon/script_func.cpp index 7a8925b766..d2a9de3c02 100644 --- a/engines/toon/script_func.cpp +++ b/engines/toon/script_func.cpp @@ -379,9 +379,9 @@ int32 ScriptFunc::sys_Cmd_Visited_Scene(EMCState *state) { int32 ScriptFunc::sys_Cmd_Query_Rif_Flag(EMCState *state) { - int32 hs = _vm->getHotspots()->FindBasedOnCorner(stackPos(0), stackPos(1)); + int32 hs = _vm->getHotspots()->findBasedOnCorner(stackPos(0), stackPos(1)); if (hs >= 0) - return _vm->getHotspots()->Get(hs)->getData(stackPos(2)); + return _vm->getHotspots()->get(hs)->getData(stackPos(2)); return 0; } @@ -472,9 +472,9 @@ int32 ScriptFunc::sys_Cmd_Say_Lines(EMCState *state) { } int32 ScriptFunc::sys_Cmd_Set_Rif_Flag(EMCState *state) { - int32 hs = _vm->getHotspots()->FindBasedOnCorner(stackPos(0), stackPos(1)); + int32 hs = _vm->getHotspots()->findBasedOnCorner(stackPos(0), stackPos(1)); if (hs >= 0) - _vm->getHotspots()->Get(hs)->setData(stackPos(2), stackPos(3)); + _vm->getHotspots()->get(hs)->setData(stackPos(2), stackPos(3)); return 0; } diff --git a/engines/toon/toon.cpp b/engines/toon/toon.cpp index 05192ae65e..248057d539 100644 --- a/engines/toon/toon.cpp +++ b/engines/toon/toon.cpp @@ -1559,9 +1559,9 @@ void ToonEngine::loadScene(int32 SceneId, bool forGameLoad) { if (state()->_locations[SceneId]._flags & 0x40) { Common::String cutaway = state()->_locations[SceneId]._cutaway; - _hotspots->LoadRif(locationName + ".RIC", cutaway + ".RIC"); + _hotspots->loadRif(locationName + ".RIC", cutaway + ".RIC"); } else { - _hotspots->LoadRif(locationName + ".RIC", ""); + _hotspots->loadRif(locationName + ".RIC", ""); } restoreRifFlags(_gameState->_currentScene); @@ -1837,10 +1837,10 @@ void ToonEngine::clickEvent() { } // find hotspot - int32 hot = _hotspots->Find(mouseX + state()->_currentScrollValue , _mouseY); + int32 hot = _hotspots->find(mouseX + state()->_currentScrollValue , _mouseY); HotspotData *currentHot = 0; if (hot > -1) { - currentHot = _hotspots->Get(hot); + currentHot = _hotspots->get(hot); } if (_currentHotspotItem == -3) { @@ -2012,9 +2012,9 @@ void ToonEngine::selectHotspot() { } } - int32 hot = _hotspots->Find(mouseX + state()->_currentScrollValue, _mouseY); + int32 hot = _hotspots->find(mouseX + state()->_currentScrollValue, _mouseY); if (hot != -1) { - HotspotData *hotspot = _hotspots->Get(hot); + HotspotData *hotspot = _hotspots->get(hot); int32 item = hotspot->getData(14); if (hotspot->getType() == 3) item += 2000; @@ -2271,8 +2271,8 @@ void ToonEngine::storeRifFlags(int32 location) { } for (int32 i = 0; i < _hotspots->getCount(); i++) { - _gameState->_locations[location]._rifBoxesFlags[i * 2 + 0] = _hotspots->Get(i)->getData(4); - _gameState->_locations[location]._rifBoxesFlags[i * 2 + 1] = _hotspots->Get(i)->getData(7); + _gameState->_locations[location]._rifBoxesFlags[i * 2 + 0] = _hotspots->get(i)->getData(4); + _gameState->_locations[location]._rifBoxesFlags[i * 2 + 1] = _hotspots->get(i)->getData(7); } } @@ -2280,8 +2280,8 @@ void ToonEngine::restoreRifFlags(int32 location) { if (_hotspots) { if (!_gameState->_locations[location]._visited) { for (int32 i = 0; i < _hotspots->getCount(); i++) { - _gameState->_locations[location]._rifBoxesFlags[i * 2 + 0] = _hotspots->Get(i)->getData(4); - _gameState->_locations[location]._rifBoxesFlags[i * 2 + 1] = _hotspots->Get(i)->getData(7); + _gameState->_locations[location]._rifBoxesFlags[i * 2 + 0] = _hotspots->get(i)->getData(4); + _gameState->_locations[location]._rifBoxesFlags[i * 2 + 1] = _hotspots->get(i)->getData(7); } _gameState->_locations[location]._numRifBoxes = _hotspots->getCount(); } else { @@ -2289,8 +2289,8 @@ void ToonEngine::restoreRifFlags(int32 location) { return; for (int32 i = 0; i < _hotspots->getCount(); i++) { - _hotspots->Get(i)->setData(4, _gameState->_locations[location]._rifBoxesFlags[i * 2 + 0]); - _hotspots->Get(i)->setData(7, _gameState->_locations[location]._rifBoxesFlags[i * 2 + 1]); + _hotspots->get(i)->setData(4, _gameState->_locations[location]._rifBoxesFlags[i * 2 + 0]); + _hotspots->get(i)->setData(7, _gameState->_locations[location]._rifBoxesFlags[i * 2 + 1]); } } } diff --git a/engines/touche/detection.cpp b/engines/touche/detection.cpp index dcb58ffae6..51b17b26d9 100644 --- a/engines/touche/detection.cpp +++ b/engines/touche/detection.cpp @@ -133,15 +133,8 @@ public: _directoryGlobs = directoryGlobs; } - virtual const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const { - ADFilePropertiesMap filesProps; - - const ADGameDescription *matchedDesc = detectGameFilebased(allFiles, fslist, Touche::fileBasedFallback, &filesProps); - if (!matchedDesc) - return 0; - - reportUnknown(fslist.begin()->getParent(), filesProps); - return matchedDesc; + ADDetectedGame fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const override { + return detectGameFilebased(allFiles, fslist, Touche::fileBasedFallback); } virtual const char *getName() const { diff --git a/engines/touche/touche.cpp b/engines/touche/touche.cpp index 15bcbbc0fc..4414ed1918 100644 --- a/engines/touche/touche.cpp +++ b/engines/touche/touche.cpp @@ -1370,7 +1370,8 @@ int ToucheEngine::getStringWidth(int num) const { debug("stringwidth: %s", str); debugN("raw:"); const char *p = str; - while (*p) debugN(" %02X", (unsigned char)*p++); + while (*p) + debugN(" %02X", (unsigned char)*p++); debugN("\n"); } return Graphics::getStringWidth16(str); diff --git a/engines/touche/touche.h b/engines/touche/touche.h index c76532b302..be636738a8 100644 --- a/engines/touche/touche.h +++ b/engines/touche/touche.h @@ -330,6 +330,7 @@ enum { kScreenHeight = 400, kRoomHeight = 352, kStartupEpisode = 90, + // TODO: If the following truncation is intentional (it probably is) it should be clearly marked as such kCycleDelay = 1000 / (1193180 / 32768), kIconWidth = 58, kIconHeight = 42, diff --git a/engines/tsage/blue_force/blueforce_scenes9.cpp b/engines/tsage/blue_force/blueforce_scenes9.cpp index 5bcc44a2b9..28023cf363 100644 --- a/engines/tsage/blue_force/blueforce_scenes9.cpp +++ b/engines/tsage/blue_force/blueforce_scenes9.cpp @@ -3165,7 +3165,7 @@ bool Scene930::Object4::startAction(CursorType action, Event &event) { if (BF_GLOBALS._bookmark >= bFlashBackTwo) { _lookLineNum = 71; NamedObject::startAction(action, event); - scene->ShowSoleInset(); + scene->showSoleInset(); remove(); } else NamedObject::startAction(action, event); @@ -3304,7 +3304,7 @@ void Scene930::Action2::signal() { 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); - scene->ShowBoxInset(); + scene->showBoxInset(); BF_GLOBALS._player.enableControl(); remove(); break; @@ -3460,7 +3460,7 @@ void Scene930::showBootInset() { _bootsInset.setDetails(930, 69, 70, 93); } -void Scene930::ShowBoxInset() { +void Scene930::showBoxInset() { _boxInset.postInit(); _boxInset.setVisage(930); _boxInset.setStrip(1); @@ -3470,7 +3470,7 @@ void Scene930::ShowBoxInset() { _boxInset.setDetails(930, 73, 74, 75); } -void Scene930::ShowSoleInset() { +void Scene930::showSoleInset() { _soleInset.postInit(); _soleInset.setVisage(930); _soleInset.setStrip(3); diff --git a/engines/tsage/blue_force/blueforce_scenes9.h b/engines/tsage/blue_force/blueforce_scenes9.h index 52935debd4..eb6f481edc 100644 --- a/engines/tsage/blue_force/blueforce_scenes9.h +++ b/engines/tsage/blue_force/blueforce_scenes9.h @@ -380,8 +380,8 @@ class Scene930: public PalettedScene { }; void showBootInset(); - void ShowBoxInset(); - void ShowSoleInset(); + void showBoxInset(); + void showSoleInset(); public: SequenceManager _sequenceManager1; Object1 _box; diff --git a/engines/tsage/detection.cpp b/engines/tsage/detection.cpp index e476391f71..5d31cca75e 100644 --- a/engines/tsage/detection.cpp +++ b/engines/tsage/detection.cpp @@ -131,9 +131,6 @@ public: if (in) { if (TsAGE::Saver::readSavegameHeader(in, header)) { saveList.push_back(SaveStateDescriptor(slot, header._saveName)); - - header._thumbnail->free(); - delete header._thumbnail; } delete in; @@ -161,7 +158,11 @@ public: if (f) { TsAGE::tSageSavegameHeader header; - TsAGE::Saver::readSavegameHeader(f, header); + if (!TsAGE::Saver::readSavegameHeader(f, header, false)) { + delete f; + return SaveStateDescriptor(); + } + delete f; // Create the return descriptor diff --git a/engines/tsage/saveload.cpp b/engines/tsage/saveload.cpp index 03f615db21..81bb973d02 100644 --- a/engines/tsage/saveload.cpp +++ b/engines/tsage/saveload.cpp @@ -189,10 +189,10 @@ Common::Error Saver::restore(int slot) { // Read in the savegame header tSageSavegameHeader header; - readSavegameHeader(saveFile, header); - if (header._thumbnail) - header._thumbnail->free(); - delete header._thumbnail; + if (!readSavegameHeader(saveFile, header)) { + delete saveFile; + return Common::kReadingFailed; + } serializer.setSaveVersion(header._version); @@ -247,9 +247,8 @@ Common::Error Saver::restore(int slot) { const char *SAVEGAME_STR = "SCUMMVM_TSAGE"; #define SAVEGAME_STR_SIZE 13 -bool Saver::readSavegameHeader(Common::InSaveFile *in, tSageSavegameHeader &header) { +WARN_UNUSED_RESULT bool Saver::readSavegameHeader(Common::InSaveFile *in, tSageSavegameHeader &header, bool skipThumbnail) { char saveIdentBuffer[SAVEGAME_STR_SIZE + 1]; - header._thumbnail = NULL; // Validate the header Id in->read(saveIdentBuffer, SAVEGAME_STR_SIZE + 1); @@ -266,9 +265,9 @@ bool Saver::readSavegameHeader(Common::InSaveFile *in, tSageSavegameHeader &head while ((ch = (char)in->readByte()) != '\0') header._saveName += ch; // Get the thumbnail - header._thumbnail = Graphics::loadThumbnail(*in); - if (!header._thumbnail) + if (!Graphics::loadThumbnail(*in, header._thumbnail, skipThumbnail)) { return false; + } // Read in save date/time header._saveYear = in->readSint16LE(); diff --git a/engines/tsage/saveload.h b/engines/tsage/saveload.h index 04a1e02b28..3de34489fd 100644 --- a/engines/tsage/saveload.h +++ b/engines/tsage/saveload.h @@ -221,7 +221,7 @@ public: Common::Error save(int slot, const Common::String &saveName); Common::Error restore(int slot); - static bool readSavegameHeader(Common::InSaveFile *in, tSageSavegameHeader &header); + WARN_UNUSED_RESULT static bool readSavegameHeader(Common::InSaveFile *in, tSageSavegameHeader &header, bool skipThumbnail = true); static void writeSavegameHeader(Common::OutSaveFile *out, tSageSavegameHeader &header); void addListener(SaveListener *obj); diff --git a/engines/tucker/detection.cpp b/engines/tucker/detection.cpp index 7d07edabd3..2318947b12 100644 --- a/engines/tucker/detection.cpp +++ b/engines/tucker/detection.cpp @@ -149,18 +149,19 @@ public: return desc != 0; } - virtual const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const { + virtual ADDetectedGame fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const override { for (Common::FSList::const_iterator d = fslist.begin(); d != fslist.end(); ++d) { Common::FSList audiofslist; if (d->isDirectory() && d->getName().equalsIgnoreCase("audio") && d->getChildren(audiofslist, Common::FSNode::kListFilesOnly)) { for (Common::FSList::const_iterator f = audiofslist.begin(); f != audiofslist.end(); ++f) { if (!f->isDirectory() && f->getName().equalsIgnoreCase("demorolc.raw")) { - return &tuckerDemoGameDescription; + return ADDetectedGame(&tuckerDemoGameDescription); } } } } - return 0; + + return ADDetectedGame(); } virtual SaveStateList listSaves(const char *target) const { @@ -175,7 +176,7 @@ public: if (ext && (slot = atoi(ext + 1)) >= 0 && slot <= Tucker::kLastSaveSlot) { Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(*file); if (in) { - if (Tucker::TuckerEngine::readSavegameHeader(in, header, false) == Tucker::TuckerEngine::kSavegameNoError) { + if (Tucker::TuckerEngine::readSavegameHeader(in, header) == Tucker::TuckerEngine::kSavegameNoError) { saveList.push_back(SaveStateDescriptor(slot, header.description)); } @@ -207,7 +208,7 @@ public: } Tucker::TuckerEngine::SavegameHeader header; - Tucker::TuckerEngine::SavegameError savegameError = Tucker::TuckerEngine::readSavegameHeader(file, header, true); + Tucker::TuckerEngine::SavegameError savegameError = Tucker::TuckerEngine::readSavegameHeader(file, header, false); if (savegameError) { delete file; return SaveStateDescriptor(); diff --git a/engines/tucker/locations.cpp b/engines/tucker/locations.cpp index 28fca114b0..d78eacc55d 100644 --- a/engines/tucker/locations.cpp +++ b/engines/tucker/locations.cpp @@ -217,7 +217,7 @@ void TuckerEngine::execData3PreUpdate_locationNum2Helper() { return; } int start, end; - if (_locationNum == 2) { + if (_location == kLocationBackAlley) { start = 116; end = 125; } else { @@ -2050,8 +2050,8 @@ void TuckerEngine::execData3PreUpdate_locationNum41() { if (_panelLockedFlag && _yPosCurrent > 130 && _selectedObject._yPos > 135 && _nextAction == 0 && _flagsTable[223] == 0) { _panelLockedFlag = false; _csDataLoaded = false; - _nextLocationNum = 0; - _selectedObject._locationObjectLocationNum = 0; + _nextLocation = kLocationNone; + _selectedObject._locationObjectLocation = kLocationNone; _locationMaskType = 0; _nextAction = _flagsTable[163] + 32; ++_flagsTable[163]; @@ -2084,7 +2084,7 @@ void TuckerEngine::updateSprite_locationNum42(int i) { } else if (_flagsTable[223] == 3) { state = 5; _spritesTable[i]._updateDelay = 5; - _spritesTable[i]._state = _spritesTable[i]._firstFrame - 1; // FIXME: bug, fxNum ? + _spritesTable[i]._state = _spritesTable[i]._firstFrame - 1; _updateSpriteFlag1 = true; } else { state = 2; @@ -2482,7 +2482,7 @@ void TuckerEngine::updateSprite_locationNum58(int i) { void TuckerEngine::execData3PreUpdate_locationNum58() { // workaround original game glitch #2872348: do not change position on location change - if (_nextLocationNum == 0 && _flagsTable[190] < 3 && _xPosCurrent > 310) { + if (_nextLocation == kLocationNone && _flagsTable[190] < 3 && _xPosCurrent > 310) { _xPosCurrent = 310; _panelLockedFlag = false; } @@ -2893,13 +2893,25 @@ void TuckerEngine::updateSprite_locationNum66_4(int i) { } void TuckerEngine::execData3PreUpdate_locationNum66() { - // FIXME: shouldn't be executed after using the map + // WORKAROUND + // If you don't have the appointment card yet and use the map to go to + // Seedy Street Bud ends up being teleported back to the mall. + // Because of the destination coordinates which warp Bud past x==583 the + // below 'if' triggers and automatically makes Violet refuse Bud entrance + // to the dentist. On top of that the graphics end up all garbled which + // indicates that even worse things happen under the hood. + // To work around this we only trigger Violet if Bud actually _walked_ past + // the trigger coordinates (as opposed to using the map). + // Fixes Trac#10452. + if (_nextLocation != kLocationNone) + return; + _flagsTable[137] = 0; if (_xPosCurrent > 583 && _flagsTable[191] == 0 && _nextAction == 0 && _locationMaskType == 0) { _panelLockedFlag = false; _csDataLoaded = false; - _nextLocationNum = 0; - _selectedObject._locationObjectLocationNum = 0; + _nextLocation = kLocationNone; + _selectedObject._locationObjectLocation = kLocationNone; if (_flagsTable[131] == 0) { _nextAction = 13; } else if (_flagsTable[130] == 0) { diff --git a/engines/tucker/resource.cpp b/engines/tucker/resource.cpp index 0ea24adf2d..5d7ae2c2d5 100644 --- a/engines/tucker/resource.cpp +++ b/engines/tucker/resource.cpp @@ -491,36 +491,36 @@ void TuckerEngine::loadCTable02() { void TuckerEngine::loadLoc() { Common::String filename; - int i = _locationWidthTable[_locationNum]; - _locationHeight = (_locationNum < 73) ? 140 : 200; - filename = Common::String::format((i == 1) ? "loc%02d.pcx" : "loc%02da.pcx", _locationNum); + int i = _locationWidthTable[_location]; + _locationHeight = (_location < kLocationJesusCutscene1) ? 140 : 200; + filename = Common::String::format((i == 1) ? "loc%02d.pcx" : "loc%02da.pcx", _location); copyLocBitmap(filename.c_str(), 0, false); Graphics::copyRect(_quadBackgroundGfxBuf, 320, _locationBackgroundGfxBuf, 640, 320, _locationHeight); if (_locationHeight == 200) { return; } - filename = Common::String::format((i != 2) ? "path%02d.pcx" : "path%02da.pcx", _locationNum); + filename = Common::String::format((i != 2) ? "path%02d.pcx" : "path%02da.pcx", _location); copyLocBitmap(filename.c_str(), 0, true); if (i > 1) { - filename = Common::String::format("loc%02db.pcx", _locationNum); + filename = Common::String::format("loc%02db.pcx", _location); copyLocBitmap(filename.c_str(), 320, false); Graphics::copyRect(_quadBackgroundGfxBuf + 44800, 320, _locationBackgroundGfxBuf + 320, 640, 320, _locationHeight); if (i == 2) { - filename = Common::String::format("path%02db.pcx", _locationNum); + filename = Common::String::format("path%02db.pcx", _location); copyLocBitmap(filename.c_str(), 320, true); } } if (i > 2) { - filename = Common::String::format("loc%02dc.pcx", _locationNum); + filename = Common::String::format("loc%02dc.pcx", _location); copyLocBitmap(filename.c_str(), 0, false); Graphics::copyRect(_quadBackgroundGfxBuf + 89600, 320, _locationBackgroundGfxBuf, 640, 320, 140); } - if (_locationNum == 1) { + if (_location == kLocationHotelRoom) { _loadLocBufPtr = _quadBackgroundGfxBuf + 89600; loadImage("rochpath.pcx", _loadLocBufPtr, 0); } if (i > 3) { - filename = Common::String::format("loc%02dd.pcx", _locationNum); + filename = Common::String::format("loc%02dd.pcx", _location); copyLocBitmap(filename.c_str(), 0, false); Graphics::copyRect(_quadBackgroundGfxBuf + 134400, 320, _locationBackgroundGfxBuf + 320, 640, 320, 140); } @@ -528,13 +528,15 @@ void TuckerEngine::loadLoc() { } void TuckerEngine::loadObj() { - if (_locationNum == 99) { + if (_location == kLocationMap) { return; } - if (_locationNum < 24) { + if (_location <= kLocationWarehouseCutscene) { _part = kPartOne; _speechSoundBaseNum = 2639; - } else if (_locationNum < 41 || (_locationNum > 69 && _locationNum < 73) || (_locationNum > 78 && _locationNum < 83)) { + } else if (( _location <= kLocationFarDocks) + || (_location >= kLocationComputerScreen && _location <= kLocationSeedyStreetCutscene) + || (_location >= kLocationElvisCutscene && _location <= kLocationJesusCutscene2)) { _part = kPartTwo; _speechSoundBaseNum = 2679; } else { @@ -544,7 +546,7 @@ void TuckerEngine::loadObj() { if (_part == _currentPart) { return; } - debug(2, "loadObj() part %d locationNum %d", _part, _locationNum); + debug(2, "loadObj() part %d location %d", _part, _location); // If a savegame is loaded from the launcher, skip the display chapter if (_startSlot != -1) _startSlot = -1; @@ -631,7 +633,7 @@ void TuckerEngine::loadData3() { loadFile("data3.c", _loadTempBuf); DataTokenizer t(_loadTempBuf, _fileLoadSize); _locationAnimationsCount = 0; - if (t.findIndex(_locationNum)) { + if (t.findIndex(_location)) { while (t.findNextToken(kDataTokenDw)) { int num = t.getNextInteger(); if (num < 0) { @@ -678,7 +680,7 @@ void TuckerEngine::loadData4() { _displayGameHints = t.getNextInteger() != 0; } _locationObjectsCount = 0; - if (t.findIndex(_locationNum)) { + if (t.findIndex(_location)) { while (t.findNextToken(kDataTokenDw)) { int i = t.getNextInteger(); if (i < 0) @@ -694,8 +696,8 @@ void TuckerEngine::loadData4() { d->_standY = t.getNextInteger(); d->_textNum = t.getNextInteger(); d->_cursorStyle = (CursorStyle)t.getNextInteger(); - d->_locationNum = t.getNextInteger(); - if (d->_locationNum > 0) { + d->_location = (Location)t.getNextInteger(); + if (d->_location != kLocationNone) { d->_toX = t.getNextInteger(); d->_toY = t.getNextInteger(); d->_toX2 = t.getNextInteger(); @@ -708,29 +710,19 @@ void TuckerEngine::loadData4() { } void TuckerEngine::loadActionFile() { - char filename[40]; - if ((_gameFlags & kGameFlagDemo) != 0) { - strcpy(filename, "action.c"); + assert(_part != kPartInit); + + Common::String filename; + if (_gameFlags & kGameFlagDemo) { + filename = "action.c"; } else { - switch (_part) { - case kPartOne: - strcpy(filename, "action1.c"); - break; - case kPartTwo: - strcpy(filename, "action2.c"); - break; - case kPartThree: - strcpy(filename, "action3.c"); - break; - default: - break; - } + filename = Common::String::format("action%d.c", _part); } - loadFile(filename, _loadTempBuf); + loadFile(filename.c_str(), _loadTempBuf); DataTokenizer t(_loadTempBuf, _fileLoadSize); _actionsCount = 0; - if (t.findIndex(_locationNum)) { + if (t.findIndex(_location)) { while (t.findNextToken(kDataTokenDw)) { int keyA = t.getNextInteger(); if (keyA < 0) { @@ -763,7 +755,7 @@ void TuckerEngine::loadCharPos() { loadFile("charpos.c", _loadTempBuf); DataTokenizer t(_loadTempBuf, _fileLoadSize); _charPosCount = 0; - if (t.findIndex(_locationNum)) { + if (t.findIndex(_location)) { while (t.findNextToken(kDataTokenDw)) { const int i = t.getNextInteger(); if (i < 0) { @@ -823,9 +815,9 @@ void TuckerEngine::loadCharPos() { void TuckerEngine::loadSprA02_01() { unloadSprA02_01(); - const int count = _sprA02LookupTable[_locationNum]; + const int count = _sprA02LookupTable[_location]; for (int i = 1; i < count + 1; ++i) { - Common::String filename = Common::String::format("sprites/a%02d_%02d.spr", _locationNum, i); + Common::String filename = Common::String::format("sprites/a%02d_%02d.spr", _location, i); _sprA02Table[i] = loadFile(filename.c_str(), 0); } _sprA02Table[0] = _sprA02Table[1]; @@ -841,13 +833,13 @@ void TuckerEngine::unloadSprA02_01() { void TuckerEngine::loadSprC02_01() { unloadSprC02_01(); - const int count = _sprC02LookupTable[_locationNum]; + const int count = _sprC02LookupTable[_location]; for (int i = 1; i < count + 1; ++i) { - Common::String filename = Common::String::format("sprites/c%02d_%02d.spr", _locationNum, i); + Common::String filename = Common::String::format("sprites/c%02d_%02d.spr", _location, i); _sprC02Table[i] = loadFile(filename.c_str(), 0); } _sprC02Table[0] = _sprC02Table[1]; - _spritesCount = _sprC02LookupTable2[_locationNum]; + _spritesCount = _sprC02LookupTable2[_location]; for (int i = 0; i < kMaxCharacters; ++i) { memset(&_spritesTable[i], 0, sizeof(Sprite)); _spritesTable[i]._state = -1; @@ -866,7 +858,7 @@ void TuckerEngine::unloadSprC02_01() { void TuckerEngine::loadFx() { loadFile("fx.c", _loadTempBuf); DataTokenizer t(_loadTempBuf, _fileLoadSize); - if (t.findIndex(_locationNum)) { + if (t.findIndex(_location)) { t.findNextToken(kDataTokenDw); _locationSoundsCount = t.getNextInteger(); _currentFxSet = 0; @@ -937,7 +929,7 @@ void TuckerEngine::loadFx() { } } } else { - error("loadFx() - Index not found for location %d", _locationNum); + error("loadFx() - Index not found for location %d", _location); } } @@ -993,7 +985,7 @@ void TuckerEngine::loadActionsTable() { do { if (!_csDataLoaded) { DataTokenizer t(_csDataBuf, _csDataSize); - bool found = t.findIndex(_locationNum); + bool found = t.findIndex(_location); assert(found); for (int i = 0; i < _nextAction; ++i) { found = t.findNextToken(kDataTokenDw); diff --git a/engines/tucker/saveload.cpp b/engines/tucker/saveload.cpp index 4e33988ed2..00911b9c44 100644 --- a/engines/tucker/saveload.cpp +++ b/engines/tucker/saveload.cpp @@ -50,36 +50,44 @@ Common::String generateGameStateFileName(const char *target, int slot, bool pref return name; } -static void saveOrLoadInt(Common::WriteStream &stream, int &i) { +static void saveOrLoadVar(Common::WriteStream &stream, int &i) { stream.writeSint32LE(i); } -static void saveOrLoadInt(Common::ReadStream &stream, int &i) { +static void saveOrLoadVar(Common::ReadStream &stream, int &i) { i = stream.readSint32LE(); } +static void saveOrLoadVar(Common::WriteStream &stream, Location &location) { + stream.writeSint32LE((int)location); +} + +static void saveOrLoadVar(Common::ReadStream &stream, Location &location) { + location = (Location)stream.readSint32LE(); +} + template<class S> TuckerEngine::SavegameError TuckerEngine::saveOrLoadGameStateData(S &s) { for (int i = 0; i < kFlagsTableSize; ++i) { - saveOrLoadInt(s, _flagsTable[i]); + saveOrLoadVar(s, _flagsTable[i]); } for (int i = 0; i < 40; ++i) { - saveOrLoadInt(s, _inventoryObjectsList[i]); + saveOrLoadVar(s, _inventoryObjectsList[i]); } for (int i = 0; i < 50; ++i) { - saveOrLoadInt(s, _inventoryItemsState[i]); + saveOrLoadVar(s, _inventoryItemsState[i]); } for (int i = 0; i < 50; ++i) { - saveOrLoadInt(s, _panelObjectsOffsetTable[i]); + saveOrLoadVar(s, _panelObjectsOffsetTable[i]); } - saveOrLoadInt(s, _mainSpritesBaseOffset); - saveOrLoadInt(s, _selectedObject._xPos); - saveOrLoadInt(s, _selectedObject._yPos); - saveOrLoadInt(s, _locationNum); - saveOrLoadInt(s, _xPosCurrent); - saveOrLoadInt(s, _yPosCurrent); - saveOrLoadInt(s, _inventoryObjectsCount); - saveOrLoadInt(s, _inventoryObjectsOffset); + saveOrLoadVar(s, _mainSpritesBaseOffset); + saveOrLoadVar(s, _selectedObject._xPos); + saveOrLoadVar(s, _selectedObject._yPos); + saveOrLoadVar(s, _location); + saveOrLoadVar(s, _xPosCurrent); + saveOrLoadVar(s, _yPosCurrent); + saveOrLoadVar(s, _inventoryObjectsCount); + saveOrLoadVar(s, _inventoryObjectsOffset); return s.err() ? kSavegameIoError : kSavegameNoError; } @@ -101,18 +109,18 @@ Common::Error TuckerEngine::loadGameState(int slot) { if (savegameError) { switch (savegameError) { - case kSavegameInvalidTypeError: - warning("Invalid savegame '%s' (does not look like a ScummVM Tucker-engine savegame)", fileName.c_str()); - break; - - case kSavegameInvalidVersionError: - warning("Invalid savegame '%s' (expected savegame version v%i-v%i, got v%i)", - fileName.c_str(), kSavegameVersionMinimum, kSavegameVersionCurrent, header.version); - break; - - default: - warning("Failed to load savegame '%s'", fileName.c_str()); - break; + case kSavegameInvalidTypeError: + warning("Invalid savegame '%s' (does not look like a ScummVM Tucker-engine savegame)", fileName.c_str()); + break; + + case kSavegameInvalidVersionError: + warning("Invalid savegame '%s' (expected savegame version v%i-v%i, got v%i)", + fileName.c_str(), kSavegameVersionMinimum, kSavegameVersionCurrent, header.version); + break; + + default: + warning("Failed to load savegame '%s'", fileName.c_str()); + break; } delete file; @@ -121,17 +129,19 @@ Common::Error TuckerEngine::loadGameState(int slot) { g_engine->setTotalPlayTime(header.playTime * 1000); - _nextLocationNum = _locationNum; + _nextLocation = _location; setBlackPalette(); loadBudSpr(); _forceRedrawPanelItems = true; + _panelType = kPanelTypeNormal; + setCursorState(kCursorStateNormal); delete file; return Common::kNoError; } -TuckerEngine::SavegameError TuckerEngine::readSavegameHeader(const char *target, int slot, SavegameHeader &header) { +WARN_UNUSED_RESULT TuckerEngine::SavegameError TuckerEngine::readSavegameHeader(const char *target, int slot, SavegameHeader &header) { Common::String fileName = generateGameStateFileName(target, slot); Common::InSaveFile *file = g_system->getSavefileManager()->openForLoading(fileName); @@ -145,8 +155,8 @@ TuckerEngine::SavegameError TuckerEngine::readSavegameHeader(const char *target, return savegameError; } -TuckerEngine::SavegameError TuckerEngine::readSavegameHeader(Common::InSaveFile *file, SavegameHeader &header, bool loadThumbnail) { - header.version = -1; +WARN_UNUSED_RESULT TuckerEngine::SavegameError TuckerEngine::readSavegameHeader(Common::InSaveFile *file, SavegameHeader &header, bool skipThumbnail) { + header.version = 0; header.flags = 0; header.description.clear(); header.saveDate = 0; @@ -186,10 +196,8 @@ TuckerEngine::SavegameError TuckerEngine::readSavegameHeader(Common::InSaveFile header.saveTime = file->readUint32LE(); header.playTime = file->readUint32LE(); - if (loadThumbnail) { - header.thumbnail = Graphics::loadThumbnail(*file); - } else { - Graphics::skipThumbnail(*file); + if (!Graphics::loadThumbnail(*file, header.thumbnail, skipThumbnail)) { + return kSavegameIoError; } } @@ -278,13 +286,19 @@ bool TuckerEngine::isAutosaveAllowed(const char *target) { void TuckerEngine::writeAutosave() { if (canSaveGameStateCurrently()) { + // unconditionally reset last autosave timestamp so we don't start + // hammering the disk in case we can't/don't actually write the file + _lastSaveTime = _system->getMillis(); + if (!isAutosaveAllowed()) { warning("Refusing to overwrite non-autosave savegame in slot %i, skipping autosave", kAutoSaveSlot); return; } - writeSavegame(kAutoSaveSlot, "Autosave", true); - _lastSaveTime = _system->getMillis(); + if (writeSavegame(kAutoSaveSlot, "Autosave", true).getCode() != Common::kNoError) { + warning("Can't create autosave in slot %i, game not saved", kAutoSaveSlot); + return; + } } } diff --git a/engines/tucker/sequences.cpp b/engines/tucker/sequences.cpp index cefb9e90e1..fd66c4f49c 100644 --- a/engines/tucker/sequences.cpp +++ b/engines/tucker/sequences.cpp @@ -50,13 +50,13 @@ void TuckerEngine::handleCreditsSequence() { int counter3 = 0; int num = 0; int imgNum = 0; - int prevLocationNum = _locationNum; + Location prevLocation = _location; int counter2 = 0; int counter1 = 0; loadCharset2(); showCursor(false); stopSounds(); - _locationNum = 74; + _location = kLocationCredits; _flagsTable[236] = 74; uint8 *imgBuf = (uint8 *)malloc(16 * 64000); loadSprC02_01(); @@ -151,7 +151,7 @@ void TuckerEngine::handleCreditsSequence() { } } while (!_quitGame && isSpeechSoundPlaying()); free(imgBuf); - _locationNum = prevLocationNum; + _location = prevLocation; do { if (_fadePaletteCounter > 0) { fadeInPalette(); @@ -185,7 +185,9 @@ void TuckerEngine::handleCongratulationsSequence() { } void TuckerEngine::handleNewPartSequence() { - char filename[40]; + assert(_part != kPartInit); + + Common::String filename; showCursor(false); stopSounds(); @@ -204,40 +206,29 @@ void TuckerEngine::handleNewPartSequence() { _redrawPanelItemsCounter = 0; } _scrollOffset = 0; - switch (_part) { - case kPartOne: - strcpy(filename, "pt1bak.pcx"); - break; - case kPartTwo: - strcpy(filename, "pt2bak.pcx"); - break; - case kPartThree: - strcpy(filename, "pt3bak.pcx"); - break; - default: - break; - } - loadImage(filename, _quadBackgroundGfxBuf, 1); + + filename = Common::String::format("pt%dbak.pcx", _part); + loadImage(filename.c_str(), _quadBackgroundGfxBuf, 1); _spritesCount = 1; clearSprites(); - int currentLocation = _locationNum; - _locationNum = 98; + Location currentLocation = _location; + _location = kLocationNewPart; unloadSprA02_01(); unloadSprC02_01(); switch (_part) { - case kPartOne: - strcpy(filename, "sprites/partone.spr"); - break; - case kPartTwo: - strcpy(filename, "sprites/parttwo.spr"); - break; - case kPartThree: - strcpy(filename, "sprites/partthr.spr"); - break; - default: - break; + case kPartOne: + filename = "sprites/partone.spr"; + break; + case kPartTwo: + filename = "sprites/parttwo.spr"; + break; + case kPartThree: + filename = "sprites/partthr.spr"; + break; + default: + break; } - _sprC02Table[1] = loadFile(filename, 0); + _sprC02Table[1] = loadFile(filename.c_str(), 0); startSpeechSound(9000, 60); _fadePaletteCounter = 0; do { @@ -269,31 +260,21 @@ void TuckerEngine::handleNewPartSequence() { redrawScreen(0); waitForTimer(3); } while (_fadePaletteCounter > 0 && !_quitGame); - _locationNum = currentLocation; + _location = currentLocation; showCursor(true); } void TuckerEngine::handleMeanwhileSequence() { - char filename[40]; + assert(_part != kPartInit); + + Common::String filename; uint8 backupPalette[256 * 3]; memcpy(backupPalette, _currentPalette, 256 * 3); - switch (_part) { - case kPartOne: - strcpy(filename, "meanw01.pcx"); - break; - case kPartTwo: - strcpy(filename, "meanw02.pcx"); - break; - case kPartThree: - strcpy(filename, "meanw03.pcx"); - break; - default: - break; - } + filename = Common::String::format("meanw%02d.pcx", _part); if (_flagsTable[215] == 0 && _flagsTable[231] == 1) { - strcpy(filename, "loc80.pcx"); + filename = "loc80.pcx"; } - loadImage(filename, _quadBackgroundGfxBuf + 89600, 1); + loadImage(filename.c_str(), _quadBackgroundGfxBuf + 89600, 1); showCursor(false); _fadePaletteCounter = 0; for (int i = 0; i < 60 && !_quitGame; ++i) { @@ -328,7 +309,7 @@ void TuckerEngine::handleMeanwhileSequence() { void TuckerEngine::handleMapSequence() { loadImage("map2.pcx", _quadBackgroundGfxBuf + 89600, 0); loadImage("map1.pcx", _loadTempBuf, 1); - _selectedObject._locationObjectLocationNum = 0; + _selectedObject._locationObjectLocation = kLocationNone; if (_flagsTable[7] > 0) { copyMapRect(0, 0, 140, 86); } @@ -345,53 +326,54 @@ void TuckerEngine::handleMapSequence() { copyMapRect(220, 0, 100, 180); } _fadePaletteCounter = 0; - int xPos = 0, yPos = 0, textNum = 0; + int xPos = 0, yPos = 0; while (!_quitGame) { + int textNum = 0; waitForTimer(2); updateMouseState(); Graphics::copyRect(_locationBackgroundGfxBuf + _scrollOffset, 640, _quadBackgroundGfxBuf + 89600, 320, 320, 200); _fullRedraw = true; if (_flagsTable[7] > 0 && _mousePosX > 30 && _mousePosX < 86 && _mousePosY > 36 && _mousePosY < 86) { textNum = 13; - _nextLocationNum = (_part == kPartOne) ? 3 : 65; + _nextLocation = (_part == kPartOne) ? kLocationSeedyStreet : kLocationSeedyStreetPartThree; xPos = 620; yPos = 130; } else if (_flagsTable[7] > 1 && _mousePosX > 60 && _mousePosX < 120 && _mousePosY > 120 && _mousePosY < 170) { textNum = 14; - _nextLocationNum = (_part == kPartOne) ? 9 : 66; + _nextLocation = (_part == kPartOne) ? kLocationMall : kLocationMallPartThree; xPos = 344; yPos = 120; } else if (_flagsTable[7] > 2 && _mousePosX > 160 && _mousePosX < 210 && _mousePosY > 110 && _mousePosY < 160) { textNum = 15; - _nextLocationNum = (_part == kPartOne) ? 16 : 61; + _nextLocation = (_part == kPartOne) ? kLocationPark : kLocationParkPartThree; xPos = 590; yPos = 130; } else if ((_flagsTable[7] == 4 || _flagsTable[7] == 6) && _mousePosX > 150 && _mousePosX < 200 && _mousePosY > 20 && _mousePosY < 70) { textNum = 16; - _nextLocationNum = (_part == kPartOne) ? 20 : 68; + _nextLocation = (_part == kPartOne) ? kLocationOutsideMuseum : kLocationOutsideMuseumPartThree; xPos = 20; yPos = 130; } else if (_flagsTable[120] == 1 && _mousePosX > 240 && _mousePosX < 290 && _mousePosY > 35 && _mousePosY < 90) { textNum = 17; - _nextLocationNum = (_part == kPartOne) ? 19 : 62; + _nextLocation = (_part == kPartOne) ? kLocationDocks : kLocationDocksPartThree; xPos = 20; yPos = 124; } else if (_mousePosX > 135 && _mousePosX < 185 && _mousePosY > 170 && _mousePosY < 200) { textNum = 18; - _nextLocationNum = _locationNum; + _nextLocation = _location; if (!_noPositionChangeAfterMap) { xPos = _xPosCurrent; yPos = _yPosCurrent; - } else if (_locationNum == 3 || _locationNum == 65) { + } else if (_location == kLocationSeedyStreet || _location == kLocationSeedyStreetPartThree) { xPos = 620; yPos = 130; - } else if (_locationNum == 9 || _locationNum == 66) { + } else if (_location == kLocationMall || _location == kLocationMallPartThree) { xPos = 344; yPos = 120; - } else if (_locationNum == 16 || _locationNum == 61) { + } else if (_location == kLocationPark || _location == kLocationParkPartThree) { xPos = 590; yPos = 130; - } else if (_locationNum == 20 || _locationNum == 68) { + } else if (_location == kLocationOutsideMuseum || _location == kLocationOutsideMuseumPartThree) { xPos = 20; yPos = 130; } else { @@ -417,11 +399,11 @@ void TuckerEngine::handleMapSequence() { --_fadePaletteCounter; } _mouseClick = 1; - if (_nextLocationNum == 9 && _noPositionChangeAfterMap) { + if (_nextLocation == kLocationMall && _noPositionChangeAfterMap) { _backgroundSpriteCurrentAnimation = 2; _backgroundSpriteCurrentFrame = 0; setCursorState(kCursorStateDisabledHidden); - } else if (_nextLocationNum == 66 && _noPositionChangeAfterMap) { + } else if (_nextLocation == kLocationMallPartThree && _noPositionChangeAfterMap) { _backgroundSpriteCurrentAnimation = 1; _backgroundSpriteCurrentFrame = 0; setCursorState(kCursorStateDisabledHidden); @@ -442,21 +424,21 @@ void TuckerEngine::copyMapRect(int x, int y, int w, int h) { } bool TuckerEngine::handleSpecialObjectSelectionSequence() { - char filename[40]; + Common::String filename; if (_part == kPartOne && _selectedObjectNum == 6) { - strcpy(filename, "news1.pcx"); + filename = "news1.pcx"; _flagsTable[7] = 4; } else if (_part == kPartThree && _selectedObjectNum == 45) { - strcpy(filename, "profnote.pcx"); + filename = "profnote.pcx"; } else if (_part == kPartOne && _selectedObjectNum == 26) { - strcpy(filename, "photo.pcx"); + filename = "photo.pcx"; } else if (_part == kPartThree && _selectedObjectNum == 39) { - strcpy(filename, "news2.pcx"); + filename = "news2.pcx"; _flagsTable[135] = 1; } else if (_currentInfoString1SourceType == 0 && _currentActionObj1Num == 259) { - strcpy(filename, "postit.pcx"); + filename = "postit.pcx"; } else if (_currentInfoString1SourceType == 1 && _currentActionObj1Num == 91) { - strcpy(filename, "memo.pcx"); + filename = "memo.pcx"; } else { return false; } @@ -466,7 +448,7 @@ bool TuckerEngine::handleSpecialObjectSelectionSequence() { --_fadePaletteCounter; } _mouseClick = 1; - loadImage(filename, _quadBackgroundGfxBuf, 1); + loadImage(filename.c_str(), _quadBackgroundGfxBuf, 1); _fadePaletteCounter = 0; while (!_quitGame) { waitForTimer(2); diff --git a/engines/tucker/tucker.cpp b/engines/tucker/tucker.cpp index 56247a126d..3a49260ab5 100644 --- a/engines/tucker/tucker.cpp +++ b/engines/tucker/tucker.cpp @@ -157,11 +157,11 @@ void TuckerEngine::resetVariables() { _mainLoopCounter1 = _mainLoopCounter2 = 0; _timerCounter2 = 0; _part = _currentPart = kPartInit; - _locationNum = 0; - _nextLocationNum = (_gameFlags & kGameFlagDemo) == 0 ? kStartupLocationGame : kStartupLocationDemo; + _location = kLocationNone; + _nextLocation = (_gameFlags & kGameFlagDemo) ? kLocationInitDemo : kLocationInit; _gamePaused = false; _gameDebug = false; - _displaySpeechText = (_gameFlags & kGameFlagNoSubtitles) == 0 ? ConfMan.getBool("subtitles") : false; + _displaySpeechText = (_gameFlags & kGameFlagNoSubtitles) ? false : ConfMan.getBool("subtitles"); memset(_flagsTable, 0, sizeof(_flagsTable)); _gameHintsIndex = 0; @@ -400,7 +400,7 @@ void TuckerEngine::mainLoop() { loadGameState(slot); } } else if (ConfMan.hasKey("boot_param")) { - _nextLocationNum = ConfMan.getInt("boot_param"); + _nextLocation = (Location)ConfMan.getInt("boot_param"); } do { @@ -427,7 +427,7 @@ void TuckerEngine::mainLoop() { if (_nextAction != 0) { loadActionsTable(); } - if (_nextLocationNum > 0) { + if (_nextLocation != kLocationNone) { setupNewLocation(); } updateCharPosition(); @@ -499,13 +499,13 @@ void TuckerEngine::mainLoop() { } } _mainSpritesBaseOffset = 0; - if (_locationWidthTable[_locationNum] > 3) { + if (_locationWidthTable[_location] > 3) { ++_currentGfxBackgroundCounter; if (_currentGfxBackgroundCounter > 39) { _currentGfxBackgroundCounter = 0; } _currentGfxBackground = _quadBackgroundGfxBuf + (_currentGfxBackgroundCounter / 10) * 44800; - if (_fadePaletteCounter < 34 && _locationNum == 22) { + if (_fadePaletteCounter < 34 && _location == kLocationFishingTrawler) { int offset = (_currentGfxBackgroundCounter > 29 ? 1 : (_currentGfxBackgroundCounter / 10)); _spritesTable[0]._gfxBackgroundOffset = offset * 640; _mainSpritesBaseOffset = offset; @@ -589,7 +589,7 @@ void TuckerEngine::mainLoop() { } if (_inputKeys[kInputKeyPause]) { _inputKeys[kInputKeyPause] = false; - if (_locationNum != 70) { + if (_location != kLocationComputerScreen) { _gamePaused = true; } } @@ -650,6 +650,12 @@ void TuckerEngine::parseEvents() { while (_eventMan->pollEvent(ev)) { switch (ev.type) { case Common::EVENT_KEYDOWN: + switch (ev.kbd.ascii) { + // do not use KEYCODE_PERIOD here so that it works with most keyboard layouts + case '.': + _inputKeys[kInputKeySkipSpeech] = true; + break; + } switch (ev.kbd.keycode) { case Common::KEYCODE_f: if (ev.kbd.hasFlags(Common::KBD_CTRL)) { @@ -672,9 +678,6 @@ void TuckerEngine::parseEvents() { _inputKeys[kInputKeyEscape] = true; _inputKeys[kInputKeySkipSpeech] = true; break; - case Common::KEYCODE_PERIOD: - _inputKeys[kInputKeySkipSpeech] = true; - break; case Common::KEYCODE_d: if (ev.kbd.hasFlags(Common::KBD_CTRL)) { this->getDebugger()->attach(); @@ -757,10 +760,10 @@ void TuckerEngine::showCursor(bool visible) { } void TuckerEngine::setupNewLocation() { - debug(2, "setupNewLocation() current %d next %d", _locationNum, _nextLocationNum); - _locationNum = _nextLocationNum; + debug(2, "setupNewLocation() current %d next %d", _location, _nextLocation); + _location = _nextLocation; loadObj(); - _nextLocationNum = 0; + _nextLocation = kLocationNone; _fadePaletteCounter = 0; _mainLoopCounter2 = 0; _mainLoopCounter1 = 0; @@ -772,7 +775,7 @@ void TuckerEngine::setupNewLocation() { _backgroundSpriteCurrentAnimation = -1; _backgroundSpriteCurrentFrame = 0; } - if (!_panelLockedFlag || (_backgroundSpriteCurrentAnimation > 0 && _locationNum != 25)) { + if (!_panelLockedFlag || (_backgroundSpriteCurrentAnimation > 0 && _location != kLocationVentSystem)) { _locationMaskType = 0; } else { _locationMaskType = 3; @@ -803,7 +806,7 @@ void TuckerEngine::setupNewLocation() { void TuckerEngine::copyLocBitmap(const char *filename, int offset, bool isMask) { int type = !isMask ? 1 : 0; - if (offset > 0 && _locationNum == 16) { + if (offset > 0 && _location == kLocationPark) { type = 0; } loadImage(filename, _loadTempBuf, type); @@ -863,7 +866,7 @@ void TuckerEngine::updateCharPosition() { if (_currentActionVerb == kVerbWalk || _locationMaskCounter == 0) { return; } - if (_currentActionVerb == kVerbLook && _locationNum != 18) { + if (_currentActionVerb == kVerbLook && _location != kLocationRoystonsHomeBoxroom) { int pos; _actionPosX = _xPosCurrent; _actionPosY = _yPosCurrent - 64; @@ -1066,10 +1069,10 @@ void TuckerEngine::setBlackPalette() { void TuckerEngine::updateCursor() { setCursorStyle(kCursorNormal); - if (_backgroundSpriteCurrentAnimation == -1 && !_panelLockedFlag && _selectedObject._locationObjectLocationNum > 0) { - _selectedObject._locationObjectLocationNum = 0; + if (_backgroundSpriteCurrentAnimation == -1 && !_panelLockedFlag && _selectedObject._locationObjectLocation != kLocationNone) { + _selectedObject._locationObjectLocation = kLocationNone; } - if (_locationMaskType > 0 || _selectedObject._locationObjectLocationNum > 0 || _pendingActionDelay > 0) { + if (_locationMaskType > 0 || _selectedObject._locationObjectLocation != kLocationNone || _pendingActionDelay > 0) { return; } if (_rightMouseButtonPressed) { @@ -1088,7 +1091,7 @@ void TuckerEngine::updateCursor() { } if (!_actionVerbLocked) { setActionVerbUnderCursor(); - if (_actionVerb == kVerbWalk && _locationNum == 63) { + if (_actionVerb == kVerbWalk && _location == kLocationTV) { _actionVerb = kVerbUse; } } @@ -1201,7 +1204,7 @@ void TuckerEngine::updateCharactersPath() { if (!_panelLockedFlag) { return; } - if (_backgroundSpriteCurrentAnimation != -1 && _locationNum != 25) { + if (_backgroundSpriteCurrentAnimation != -1 && _location != kLocationVentSystem) { if (_xPosCurrent == _selectedObject._xPos && _yPosCurrent == _selectedObject._yPos) { _locationMaskCounter = 1; _panelLockedFlag = false; @@ -1255,7 +1258,7 @@ void TuckerEngine::updateCharactersPath() { } } } - if (_locationNum == 25) { + if (_location == kLocationVentSystem) { if ((_backgroundSpriteCurrentAnimation != 3 || _characterBackFrontFacing) && (_backgroundSpriteCurrentAnimation != 6 || !_characterBackFrontFacing)) { _xPosCurrent = xPos; _yPosCurrent = yPos; @@ -1274,7 +1277,7 @@ void TuckerEngine::updateCharactersPath() { if (_characterPrevFacingDirection <= 0 || _characterPrevFacingDirection >= 5) { return; } - if (_selectedObject._locationObjectLocationNum == 0) { + if (_selectedObject._locationObjectLocation == kLocationNone) { _characterFacingDirection = 5; while (_spriteAnimationFramesTable[_spriteAnimationFrameIndex] != 999) { ++_spriteAnimationFrameIndex; @@ -1319,7 +1322,7 @@ void TuckerEngine::updateData3() { a->_animCurrentCounter = a->_animInitCounter; a->_drawFlag = false; } - if (_locationNum == 24 && i == 0) { + if (_location == kLocationStoreRoom && i == 0) { // workaround bug #2872385: update fish animation sequence for correct // position in aquarium. if (a->_animInitCounter == 505 && a->_animCurrentCounter == 513) { @@ -1390,7 +1393,7 @@ void TuckerEngine::saveOrLoad() { drawSpeechText(_scrollOffset + 120, 170, _infoBarBuf, 21, 102); } if (_mousePosY > 140) { - if (_mouseWheelUp && _currentSaveLoadGameState < 99) { + if (_mouseWheelUp && _currentSaveLoadGameState < kLastSaveSlot) { ++_currentSaveLoadGameState; _forceRedrawPanelItems = true; return; @@ -1403,7 +1406,7 @@ void TuckerEngine::saveOrLoad() { if (_leftMouseButtonPressed && _mouseClick == 0) { _mouseClick = 1; if (_mousePosX > 228 && _mousePosX < 240 && _mousePosY > 154 && _mousePosY < 170) { - if (_currentSaveLoadGameState < 99) { + if (_currentSaveLoadGameState < kLastSaveSlot) { ++_currentSaveLoadGameState; _forceRedrawPanelItems = true; } @@ -1467,23 +1470,23 @@ void TuckerEngine::handleMouseOnPanel() { void TuckerEngine::togglePanelStyle() { switch (_panelState) { - case kPanelStateShrinking: - if (++_switchPanelCounter == 25) { - _panelStyle = (_panelStyle == kPanelStyleVerbs) ? kPanelStyleIcons : kPanelStyleVerbs; - loadPanel(); - _forceRedrawPanelItems = true; - _panelState = kPanelStateExpanding; - } - break; + case kPanelStateShrinking: + if (++_switchPanelCounter == 25) { + _panelStyle = (_panelStyle == kPanelStyleVerbs) ? kPanelStyleIcons : kPanelStyleVerbs; + loadPanel(); + _forceRedrawPanelItems = true; + _panelState = kPanelStateExpanding; + } + break; - case kPanelStateExpanding: - if (--_switchPanelCounter == 0) { - _panelState = kPanelStateNormal; - } - break; + case kPanelStateExpanding: + if (--_switchPanelCounter == 0) { + _panelState = kPanelStateNormal; + } + break; - default: - break; + default: + break; } } @@ -1541,11 +1544,11 @@ void TuckerEngine::drawConversationTexts() { void TuckerEngine::updateScreenScrolling() { int scrollPrevOffset = _scrollOffset; - if (_locationWidthTable[_locationNum] != 2) { + if (_locationWidthTable[_location] != 2) { _scrollOffset = 0; } else if (_validInstructionId) { _scrollOffset = _xPosCurrent - 200; - } else if (_locationNum == 16 && _backgroundSpriteCurrentAnimation == 6 && _scrollOffset + 200 < _xPosCurrent) { + } else if (_location == kLocationPark && _backgroundSpriteCurrentAnimation == 6 && _scrollOffset + 200 < _xPosCurrent) { ++_scrollOffset; if (_scrollOffset > 320) { _scrollOffset = 320; @@ -1641,7 +1644,7 @@ void TuckerEngine::drawData3() { } void TuckerEngine::execData3PreUpdate() { - switch (_locationNum) { + switch (_location) { case 1: execData3PreUpdate_locationNum1(); break; @@ -1774,11 +1777,13 @@ void TuckerEngine::execData3PreUpdate() { case 70: execData3PreUpdate_locationNum70(); break; + default: + break; } } void TuckerEngine::execData3PostUpdate() { - switch (_locationNum) { + switch (_location) { case 1: execData3PostUpdate_locationNum1(); break; @@ -1815,6 +1820,8 @@ void TuckerEngine::execData3PostUpdate() { case 66: execData3PostUpdate_locationNum66(); break; + default: + break; } } @@ -1825,28 +1832,23 @@ void TuckerEngine::drawBackgroundSprites() { int srcH = READ_LE_UINT16(_backgroundSpriteDataPtr + frameOffset + 2); int srcX = READ_LE_UINT16(_backgroundSpriteDataPtr + frameOffset + 8); int srcY = READ_LE_UINT16(_backgroundSpriteDataPtr + frameOffset + 10); - if (_locationNum == 22 && _backgroundSpriteCurrentAnimation > 1) { + if (_location == kLocationFishingTrawler && _backgroundSpriteCurrentAnimation > 1) { srcY += _mainSpritesBaseOffset; } - if (_locationNum == 29 && _backgroundSpriteCurrentAnimation == 3) { + if (_location == kLocationSubmarineHangar && _backgroundSpriteCurrentAnimation == 3) { srcX += 228; - } else if (_locationNum == 58 && _backgroundSpriteCurrentAnimation == 1) { + } else if (_location == kLocationInsideMuseumPartThree && _backgroundSpriteCurrentAnimation == 1) { srcX += 100; } else if (_xPosCurrent > 320 && _xPosCurrent < 640) { srcX += 320; } srcX += _backgroundSprOffset; - Graphics::decodeRLE_248(_locationBackgroundGfxBuf + srcY * 640 + srcX, _backgroundSpriteDataPtr + frameOffset + 12, srcW, srcH, 0, _locationHeightTable[_locationNum], false); + Graphics::decodeRLE_248(_locationBackgroundGfxBuf + srcY * 640 + srcX, _backgroundSpriteDataPtr + frameOffset + 12, srcW, srcH, 0, _locationHeightTable[_location], false); addDirtyRect(srcX, srcY, srcW, srcH); } } void TuckerEngine::drawCurrentSprite() { - // Workaround original game glitch: skip first bud frame drawing when entering location (tracker item #2597763) - if ((_locationNum == 17 || _locationNum == 18) && _currentSpriteAnimationFrame == 16) { - return; - } - // WORKAROUND: original game glitch // Locations 48 and 61 contain reserved colors from [0xE0-0xF8] in a walkable area which // results in a number of pixels being falsely drawn in the foreground (on top of Bud). @@ -1859,15 +1861,18 @@ void TuckerEngine::drawCurrentSprite() { // [0xE0, ... ..., 0xEF] static const int whitelistReservedColorsLocation48[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }; static const int whitelistReservedColorsLocation61[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1 }; - switch (_locationNum) { - case 48: - whitelistReservedColors = (const int *)&whitelistReservedColorsLocation48; - break; + switch (_location) { + case kLocationCorridor: + whitelistReservedColors = (const int *)&whitelistReservedColorsLocation48; + break; - case 61: - if (_xPosCurrent <= 565) - whitelistReservedColors = (const int *)&whitelistReservedColorsLocation61; - break; + case kLocationParkPartThree: + if (_xPosCurrent <= 565) + whitelistReservedColors = (const int *)&whitelistReservedColorsLocation61; + break; + + default: + break; } SpriteFrame *chr = &_spriteFramesTable[_currentSpriteAnimationFrame]; @@ -1879,7 +1884,7 @@ void TuckerEngine::drawCurrentSprite() { xPos -= chr->_xSize + chr->_xOffset - 14; } Graphics::decodeRLE_248(_locationBackgroundGfxBuf + yPos * 640 + xPos, _spritesGfxBuf + chr->_sourceOffset, chr->_xSize, chr->_ySize, - chr->_yOffset, _locationHeightTable[_locationNum], _mirroredDrawing, whitelistReservedColors); + chr->_yOffset, _locationHeightTable[_location], _mirroredDrawing, whitelistReservedColors); addDirtyRect(xPos, yPos, chr->_xSize, chr->_ySize); if (_currentSpriteAnimationLength > 1) { SpriteFrame *chr2 = &_spriteFramesTable[_currentSpriteAnimationFrame2]; @@ -1891,7 +1896,7 @@ void TuckerEngine::drawCurrentSprite() { xPos -= chr2->_xSize + chr2->_xOffset - 14; } Graphics::decodeRLE_248(_locationBackgroundGfxBuf + yPos * 640 + xPos, _spritesGfxBuf + chr2->_sourceOffset, chr2->_xSize, chr2->_ySize, - chr2->_yOffset, _locationHeightTable[_locationNum], _mirroredDrawing, whitelistReservedColors); + chr2->_yOffset, _locationHeightTable[_location], _mirroredDrawing, whitelistReservedColors); addDirtyRect(xPos, yPos, chr2->_xSize, chr2->_ySize); } } @@ -1963,32 +1968,37 @@ void TuckerEngine::redrawPanelItems() { int sz = 0; switch (_panelType) { - case kPanelTypeNormal: - src = _panelGfxBuf; - dst = _itemsGfxBuf + 3200; - sz = 16000; - break; - case kPanelTypeEmpty: - src = _panelGfxBuf + 16320; - dst = _itemsGfxBuf; - sz = 19200; - break; - case kPanelTypeLoadSavePlayQuit: - src = _panelGfxBuf + 16320; - dst = _itemsGfxBuf; - sz = 19200; - memcpy(dst, src, sz); - src = _panelGfxBuf + 55040; - dst = _itemsGfxBuf + 6400; - sz = 5120; - break; - case kPanelTypeLoadSaveSavegame: - src = _panelGfxBuf + 35200; - dst = _itemsGfxBuf; - sz = 19200; - break; - default: - break; + case kPanelTypeNormal: + src = _panelGfxBuf; + dst = _itemsGfxBuf + 3200; + sz = 16000; + break; + case kPanelTypeEmpty: + src = _panelGfxBuf + 16320; + dst = _itemsGfxBuf; + sz = 19200; + break; + case kPanelTypeLoadSavePlayQuit: + // The following offset does not match disassembly on purpose to fix a + // "glitch" in the original game. + // This ensures that the background image ends up in the same place as + // in the case of kPanelTypeLoadSaveSavegame. + // This fixes Trac#10496. + src = _panelGfxBuf + 16000; + dst = _itemsGfxBuf; + sz = 19200; + memcpy(dst, src, sz); + src = _panelGfxBuf + 55040; + dst = _itemsGfxBuf + 6400; + sz = 5120; + break; + case kPanelTypeLoadSaveSavegame: + src = _panelGfxBuf + 35200; + dst = _itemsGfxBuf; + sz = 19200; + break; + default: + break; } memcpy(dst, src, sz); if (_panelType == kPanelTypeNormal) { @@ -2176,7 +2186,7 @@ void TuckerEngine::updateCharacterAnimation() { assert(_backgroundSpriteCurrentAnimation >= 0 && _backgroundSpriteCurrentAnimation < kSprA02TableSize); _backgroundSpriteDataPtr = _sprA02Table[_backgroundSpriteCurrentAnimation]; _backgroundSpriteLastFrame = READ_LE_UINT16(_backgroundSpriteDataPtr); - } else if (_locationNum == 25 && !_panelLockedFlag && (_backgroundSpriteCurrentAnimation == 3 || _backgroundSpriteCurrentAnimation == 6)) { + } else if (_location == kLocationVentSystem && !_panelLockedFlag && (_backgroundSpriteCurrentAnimation == 3 || _backgroundSpriteCurrentAnimation == 6)) { _backgroundSpriteCurrentFrame = 0; _backgroundSpriteCurrentAnimation = -1; } else { @@ -2191,10 +2201,10 @@ void TuckerEngine::updateCharacterAnimation() { } } } - if (_locationNum == 24 && _flagsTable[103] == 0) { + if (_location == kLocationStoreRoom && _flagsTable[103] == 0) { if (_panelLockedFlag) { _panelLockedFlag = false; - _selectedObject._locationObjectLocationNum = 0; + _selectedObject._locationObjectLocation = kLocationNone; if (_actionVerb != kVerbTalk) { _speechSoundNum = 2236; startSpeechSound(_speechSoundNum, _speechVolume); @@ -2227,7 +2237,7 @@ void TuckerEngine::updateCharacterAnimation() { _backgroundSpriteDataPtr = _sprA02Table[_backgroundSpriteCurrentAnimation]; _backgroundSpriteLastFrame = READ_LE_UINT16(_backgroundSpriteDataPtr); } - } else if (_locationNum == 25) { + } else if (_location == kLocationVentSystem) { if (_backgroundSpriteCurrentFrame == 0) { if (!_characterBackFrontFacing) { if (_characterBackFrontFacing != _characterPrevBackFrontFacing) { @@ -2256,7 +2266,7 @@ void TuckerEngine::updateCharacterAnimation() { _backgroundSpriteLastFrame = READ_LE_UINT16(_backgroundSpriteDataPtr); } _backgroundSprOffset = _xPosCurrent - 160; - } else if (_locationNum == 63 && _backgroundSpriteCurrentFrame == 0) { + } else if (_location == kLocationTV && _backgroundSpriteCurrentFrame == 0) { if (_charSpeechSoundCounter > 0 && _actionCharacterNum == 99) { _backgroundSpriteCurrentAnimation = 1; } else { @@ -2267,7 +2277,7 @@ void TuckerEngine::updateCharacterAnimation() { _backgroundSpriteLastFrame = READ_LE_UINT16(_backgroundSpriteDataPtr); } int frame = _spriteAnimationFramesTable[_spriteAnimationFrameIndex]; - if (!_panelLockedFlag && _characterFacingDirection < 5 && _selectedObject._locationObjectLocationNum == 0) { + if (!_panelLockedFlag && _characterFacingDirection < 5 && _selectedObject._locationObjectLocation == kLocationNone) { _characterFacingDirection = 0; } if (_charSpeechSoundCounter > 0 && _characterFacingDirection != 6 && _actionCharacterNum == 99) { @@ -2326,7 +2336,7 @@ void TuckerEngine::updateCharacterAnimation() { num = 13; } else if (getRandomNumber() < 3000) { num = 14; - if (_locationNum == 57) { + if (_location == kLocationFishShopPartThree) { num = 18; } } else { @@ -2414,7 +2424,7 @@ void TuckerEngine::handleMap() { _panelLockedFlag = false; } } - if (!_panelLockedFlag && (_backgroundSpriteCurrentAnimation == -1 || _locationNum == 25) && _locationMaskType == 3) { + if (!_panelLockedFlag && (_backgroundSpriteCurrentAnimation == -1 || _location == kLocationVentSystem) && _locationMaskType == 3) { setCursorState(kCursorStateNormal); if (_locationMaskCounter == 1) { _characterFacingDirection = 0; @@ -2422,10 +2432,10 @@ void TuckerEngine::handleMap() { } return; } - if (_selectedObject._locationObjectLocationNum != 0 && _locationMaskCounter != 0 && (_backgroundSpriteCurrentAnimation <= -1 || _locationNum == 25)) { + if (_selectedObject._locationObjectLocation != kLocationNone && _locationMaskCounter != 0 && (_backgroundSpriteCurrentAnimation <= -1 || _location == kLocationVentSystem)) { // TODO // This is actually "_locationNum != 25" in disassembly. Is this a typo? - if (_locationNum == 25 || _backgroundSpriteCurrentAnimation != 4) { + if (_location == kLocationVentSystem || _backgroundSpriteCurrentAnimation != 4) { if (_locationMaskType == 0) { _locationMaskType = 1; setCursorState(kCursorStateDisabledHidden); @@ -2440,7 +2450,7 @@ void TuckerEngine::handleMap() { } _backgroundSpriteCurrentFrame = 0; _mirroredDrawing = false; - if (_locationNum == 25) { + if (_location == kLocationVentSystem) { _backgroundSpriteDataPtr = _sprA02Table[_backgroundSpriteCurrentAnimation]; _backgroundSpriteLastFrame = READ_LE_UINT16(_backgroundSpriteDataPtr); _backgroundSpriteCurrentFrame = 1; @@ -2457,7 +2467,7 @@ void TuckerEngine::handleMap() { _locationMaskType = 2; _panelType = kPanelTypeNormal; setCursorState(kCursorStateNormal); - if (_selectedObject._locationObjectLocationNum == 99) { + if (_selectedObject._locationObjectLocation == kLocationMap) { _noPositionChangeAfterMap = true; handleMapSequence(); return; @@ -2467,7 +2477,7 @@ void TuckerEngine::handleMap() { redrawScreen(_scrollOffset); _fadePaletteCounter = 34; } - _nextLocationNum = _selectedObject._locationObjectLocationNum; + _nextLocation = _selectedObject._locationObjectLocation; _xPosCurrent = _selectedObject._locationObjectToX; _yPosCurrent = _selectedObject._locationObjectToY; if (_selectedObject._locationObjectToX2 > 800) { @@ -2488,7 +2498,7 @@ void TuckerEngine::handleMap() { _scrollOffset = 0; _handleMapCounter = 0; _locationMaskCounter = 0; - _selectedObject._locationObjectLocationNum = 0; + _selectedObject._locationObjectLocation = kLocationNone; } } } @@ -2502,7 +2512,7 @@ void TuckerEngine::clearSprites() { } void TuckerEngine::updateSprites() { - const int count = (_locationNum == 9) ? 3 : _spritesCount; + const int count = (_location == kLocationMall) ? 3 : _spritesCount; for (int i = 0; i < count; ++i) { if (_spritesTable[i]._stateIndex > -1) { ++_spritesTable[i]._stateIndex; @@ -2569,7 +2579,7 @@ void TuckerEngine::updateSprite(int i) { _updateSpriteFlag2 = false; _spritesTable[i]._defaultUpdateDelay = 0; _spritesTable[i]._updateDelay = 0; - switch (_locationNum) { + switch (_location) { case 2: updateSprite_locationNum2(); break; @@ -2942,13 +2952,23 @@ void TuckerEngine::updateSprite(int i) { case 98: _spritesTable[0]._state = 1; break; + default: + break; } if (_spritesTable[i]._stateIndex <= -1) { if (!_updateSpriteFlag1) { _spritesTable[i]._animationFrame = 1; } if (_spritesTable[i]._state < 0 || !_sprC02Table[_spritesTable[i]._state]) { -// warning("Invalid state %d for sprite %d location %d", _spritesTable[i].state, i, _locationNum); + // WORKAROUND + // The original game unconditionally reads into _sprC02Table[] below which + // results in out-of-bounds reads when _spritesTable[i]._state == -1. + // We reset the sprite's animation data in this case so sprite updates + // are triggered correctly. This most prominently fixes a bug where Lola's + // transition from dancing -> sitting happens too late. + // This fixes Trac#6644. + _spritesTable[i]._animationData = nullptr; + _spritesTable[i]._firstFrame = 0; return; } _spritesTable[i]._animationData = _sprC02Table[_spritesTable[i]._state]; @@ -3046,7 +3066,7 @@ bool TuckerEngine::testLocationMask(int x, int y) { if (_locationMaskType > 0 || _locationMaskIgnore) { return true; } - if (_locationNum == 26 || _locationNum == 32) { + if (_location == kLocationSubwayTunnel || _location == kLocationKitchen) { y -= 3; } const int offset = y * 640 + x; @@ -3138,7 +3158,8 @@ enum TableInstructionCode { kCode_was, kCode_wfx, kCode_xhr, - kCode_xhm + kCode_xhm, + kCode_no3 // NOOP, throw away 3-byte parameter }; static const struct { @@ -3158,12 +3179,14 @@ static const struct { { "bso", kCode_bso }, { "bus", kCode_bus }, { "b0s", kCode_bus }, // only ref 65.25 + { "buv", kCode_no3 }, { "buw", kCode_buw }, { "bdx", kCode_bux }, { "bux", kCode_bux }, { "c0a", kCode_c0a }, { "c0c", kCode_c0c }, { "c0s", kCode_c0s }, + { "c0v", kCode_no3 }, { "end", kCode_end }, { "fad", kCode_fad }, { "fw", kCode_fw }, @@ -3302,7 +3325,7 @@ int TuckerEngine::executeTableInstruction() { // As a workaround, do not ignore the location mask during this specific // action when entering the club. // This fixes Trac#5838. - if (!(_locationNum == 6 && _nextAction == 59)) { + if (!(_location == kLocationStripJoint && _nextAction == 59)) { _locationMaskIgnore = true; } _panelLockedFlag = true; @@ -3377,10 +3400,10 @@ int TuckerEngine::executeTableInstruction() { _characterAnimationNum = readTableInstructionParam(2); return 0; case kCode_loc: - _nextLocationNum = readTableInstructionParam(2); + _nextLocation = (Location)readTableInstructionParam(2); return 1; case kCode_mof: - // TODO: Unknown opcode in Spanish version. Identify if this has any function. + setCursorState(kCursorStateDisabledHidden); return 0; case kCode_opt: _conversationOptionsCount = readTableInstructionParam(2); @@ -3449,6 +3472,27 @@ int TuckerEngine::executeTableInstruction() { return 1; case kCode_wsm: _stopActionOnPanelLock = true; + + // WORKAROUND + // Some versions have a script bug which allows you to freely click around + // during the sequence of Bud freeing the professor in part two which even + // allows Bud to leave the room while talking to the professor resulting in + // general glitchiness. The Spanish and Polish versions (and possibly others) + // fixed this by introducing the 'mof' opcode to disable the mouse during the + // sequence. + // + // The difference is as follows: + // Buggy: 61dw buw,148,125,wsm,buw,148,132,wsm,wat,050[...] + // Fixed: 61dw buw,148,125,wsm,buw,148,132,wsm,mof,pan,01,wat,050[...] + // ^^^^^^^^^^ + // To work around the issue in the problematic versions we inject these two + // instructions after the first occurence of the 'wsm' instruction (which + // proves good enough). + if (_location == kLocationStoreRoom && _nextAction == 61) { + setCursorState(kCursorStateDisabledHidden); + _panelType = kPanelTypeEmpty; + } + return 1; case kCode_wat: _stopActionCounter = readTableInstructionParam(3); @@ -3465,6 +3509,10 @@ int TuckerEngine::executeTableInstruction() { case kCode_xhm: _validInstructionId = false; return 0; + case kCode_no3: + // opcodes mapped here are treated as NOOPs + readTableInstructionParam(3); + return 0; } return 2; } @@ -3552,7 +3600,7 @@ void TuckerEngine::setSelectedObjectKey() { _locationMaskCounter = 0; _actionRequiresTwoObjects = false; _selectedObject._yPos = 0; - _selectedObject._locationObjectLocationNum = 0; + _selectedObject._locationObjectLocation = kLocationNone; _pendingActionIndex = 0; if (_selectedObjectType == 0) { if (_selectedObjectNum == 0) { @@ -3562,7 +3610,7 @@ void TuckerEngine::setSelectedObjectKey() { _selectedObject._xPos = _locationObjectsTable[_selectedCharacterNum]._standX; _selectedObject._yPos = _locationObjectsTable[_selectedCharacterNum]._standY; if (_actionVerb == kVerbWalk || _actionVerb == kVerbUse) { - _selectedObject._locationObjectLocationNum = _locationObjectsTable[_selectedCharacterNum]._locationNum; + _selectedObject._locationObjectLocation = _locationObjectsTable[_selectedCharacterNum]._location; _selectedObject._locationObjectToX = _locationObjectsTable[_selectedCharacterNum]._toX; _selectedObject._locationObjectToY = _locationObjectsTable[_selectedCharacterNum]._toY; _selectedObject._locationObjectToX2 = _locationObjectsTable[_selectedCharacterNum]._toX2; @@ -3592,14 +3640,14 @@ void TuckerEngine::setSelectedObjectKey() { _selectedObject._yPos = _mousePosY; } _selectedObjectLocationMask = testLocationMask(_selectedObject._xPos, _selectedObject._yPos); - if (!_selectedObjectLocationMask && _objectKeysLocationTable[_locationNum] == 1) { - if (_selectedObject._yPos < _objectKeysPosYTable[_locationNum]) { - while (!_selectedObjectLocationMask && _selectedObject._yPos < _objectKeysPosYTable[_locationNum]) { + if (!_selectedObjectLocationMask && _objectKeysLocationTable[_location] == 1) { + if (_selectedObject._yPos < _objectKeysPosYTable[_location]) { + while (!_selectedObjectLocationMask && _selectedObject._yPos < _objectKeysPosYTable[_location]) { ++_selectedObject._yPos; _selectedObjectLocationMask = testLocationMask(_selectedObject._xPos, _selectedObject._yPos); } } else { - while (!_selectedObjectLocationMask && _selectedObject._yPos < _objectKeysPosYTable[_locationNum]) { + while (!_selectedObjectLocationMask && _selectedObject._yPos < _objectKeysPosYTable[_location]) { --_selectedObject._yPos; _selectedObjectLocationMask = testLocationMask(_selectedObject._xPos, _selectedObject._yPos); } @@ -3607,12 +3655,12 @@ void TuckerEngine::setSelectedObjectKey() { } if (_selectedObjectLocationMask) { _selectedObjectLocationMask = testLocationMaskArea(_xPosCurrent, _yPosCurrent, _selectedObject._xPos, _selectedObject._yPos); - if (_selectedObjectLocationMask && _objectKeysPosXTable[_locationNum] > 0) { + if (_selectedObjectLocationMask && _objectKeysPosXTable[_location] > 0) { _selectedObject._xDefaultPos = _selectedObject._xPos; _selectedObject._yDefaultPos = _selectedObject._yPos; - _selectedObject._xPos = _objectKeysPosXTable[_locationNum]; - _selectedObject._yPos = _objectKeysPosYTable[_locationNum]; - if (_objectKeysLocationTable[_locationNum] == 1) { + _selectedObject._xPos = _objectKeysPosXTable[_location]; + _selectedObject._yPos = _objectKeysPosYTable[_location]; + if (_objectKeysLocationTable[_location] == 1) { _selectedObject._xPos = _selectedObject._xDefaultPos; } } @@ -3699,7 +3747,7 @@ void TuckerEngine::handleMouseClickOnInventoryObject() { break; case 1: if (_actionVerb == kVerbUse && _leftMouseButtonPressed) { - if (_mapSequenceFlagsLocationTable[_locationNum - 1] == 1) { + if (_mapSequenceFlagsLocationTable[_location - 1] == 1) { handleMapSequence(); } else { _actionPosX = _xPosCurrent; @@ -3772,6 +3820,11 @@ int TuckerEngine::setLocationAnimationUnderCursor() { continue; } if (_locationAnimationsTable[i]._selectable == 0) { + // WORKAROUND + // The original game does a "return -1" here which is not correct in + // case of overlapping hotspots. + // This most prominently fixes Trac#6645, a bug where the cellar in part three + // could be entered without having done the cellar door puzzle first. continue; } _selectedObjectType = 1; diff --git a/engines/tucker/tucker.h b/engines/tucker/tucker.h index cd12939443..e3748680fe 100644 --- a/engines/tucker/tucker.h +++ b/engines/tucker/tucker.h @@ -110,10 +110,103 @@ enum VerbPreposition { }; enum Part { - kPartInit = 0, - kPartOne = 1, - kPartTwo = 2, - kPartThree = 3 + kPartInit = 0, + kPartOne = 1, + kPartTwo = 2, + kPartThree = 3 +}; + +enum Location { + kLocationNone = 0, + + kLocationHotelRoom = 1, + kLocationBackAlley = 2, + kLocationSeedyStreet = 3, + kLocationBakersShop = 4, + kLocationBakersKitchen = 5, + kLocationStripJoint = 6, + kLocationPoliceHQ = 7, + kLocationPoliceCell = 8, + kLocationMall = 9, + kLocationFishShop = 10, + kLocationBurgerJoint = 11, + kLocationRecordShop = 12, + kLocationDentist = 13, + kLocationPlugShop = 14, + kLocationTouristInfo = 15, + kLocationPark = 16, + kLocationRoystonsHomeHallway = 17, + kLocationRoystonsHomeBoxroom = 18, + kLocationDocks = 19, + kLocationOutsideMuseum = 20, + kLocationInsideMuseum = 21, + kLocationFishingTrawler = 22, + kLocationWarehouseCutscene = 23, + kLocationStoreRoom = 24, + kLocationVentSystem = 25, + kLocationSubwayTunnel = 26, + kLocationStrangeRoom = 27, + kLocationTopCorridor = 28, + kLocationSubmarineHangar = 29, + kLocationBunkRoom = 30, + kLocationBottomCorridor = 31, + kLocationKitchen = 32, + kLocationCommandCentre = 33, + kLocationSubmarineHatch = 34, + kLocationSubmarineWalkway = 35, + kLocationSubmarineBridge = 36, + kLocationSubmarineOffice = 37, + kLocationSubmarineEngineRoom = 38, + kLocationLuxuryApartment = 39, + kLocationFarDocks = 40, + kLocationAlleyway = 41, + kLocationBasement = 42, + kLocationTateTowerEntrance = 43, + kLocationRooftop = 44, + kLocationConferenceRoom = 45, + kLocationAnteChamber = 46, + kLocationHelipad = 47, + kLocationCorridor = 48, + kLocationWaitingRoom = 49, + kLocationkLocationCorridorCutscene = 50, + kLocationCells = 51, + kLocationMachineRoom = 52, + kLocationRecordShopPartThree = 53, + kLocationPlugShopPartThree = 54, + kLocationTouristInfoPartThree = 55, + kLocationDentistPartThree = 56, + kLocationFishShopPartThree = 57, + kLocationInsideMuseumPartThree = 58, + kLocationBakersShopPartThree = 59, + kLocationStripJointPartThree = 60, + kLocationParkPartThree = 61, + kLocationDocksPartThree = 62, + kLocationTV = 63, + kLocationSewer = 64, + kLocationSeedyStreetPartThree = 65, + kLocationMallPartThree = 66, + kLocationBurgerJointPartThree = 67, + kLocationOutsideMuseumPartThree = 68, + kLocation69Cutscene = 69, + kLocationComputerScreen = 70, + kLocationParkCutscene = 71, + kLocationSeedyStreetCutscene = 72, + kLocationJesusCutscene1 = 73, + kLocationCredits = 74, + kLocation75Cutscene = 75, + kLocationBeachCutscene = 76, + kLocationHospitalCutscene = 77, + kLocation78Cutscene = 78, + kLocationElvisCutscene = 79, + kLocationPyramidCutscene = 80, + kLocationCleopatraCutscene = 81, + kLocationJesusCutscene2 = 82, + + kLocationNewPart = 98, + kLocationMap = 99, + + kLocationInit = 1, + kLocationInitDemo = 9 }; struct Action { @@ -212,7 +305,7 @@ struct LocationObject { int _xSize; int _ySize; int _textNum; - int _locationNum; + Location _location; int _toX; int _toY; int _toX2; @@ -251,8 +344,6 @@ enum { kScreenHeight = 200, kScreenPitch = 640, kFadePaletteStep = 5, - kStartupLocationDemo = 9, - kStartupLocationGame = 1, kDefaultCharSpeechSoundCounter = 1, kMaxSoundVolume = 127, kLastSaveSlot = 99, @@ -361,8 +452,8 @@ public: virtual bool hasFeature(EngineFeature f) const; GUI::Debugger *getDebugger() { return _console; } - static SavegameError readSavegameHeader(Common::InSaveFile *file, SavegameHeader &header, bool loadThumbnail = false); - static SavegameError readSavegameHeader(const char *target, int slot, SavegameHeader &header); + WARN_UNUSED_RESULT static SavegameError readSavegameHeader(Common::InSaveFile *file, SavegameHeader &header, bool skipThumbnail = true); + WARN_UNUSED_RESULT static SavegameError readSavegameHeader(const char *target, int slot, SavegameHeader &header); bool isAutosaveAllowed(); static bool isAutosaveAllowed(const char *target); protected: @@ -719,8 +810,8 @@ protected: int _flagsTable[kFlagsTableSize]; Part _part; Part _currentPart; - int _locationNum; - int _nextLocationNum; + Location _location; + Location _nextLocation; bool _gamePaused; bool _gameDebug; bool _displayGameHints; @@ -850,7 +941,7 @@ protected: int _yDefaultPos; int _xPos; int _yPos; - int _locationObjectLocationNum; + Location _locationObjectLocation; int _locationObjectToX; int _locationObjectToY; int _locationObjectToX2; diff --git a/engines/voyeur/detection.cpp b/engines/voyeur/detection.cpp index eefe174e94..6452e5741f 100644 --- a/engines/voyeur/detection.cpp +++ b/engines/voyeur/detection.cpp @@ -132,7 +132,6 @@ SaveStateList VoyeurMetaEngine::listSaves(const char *target) const { if (in) { if (header.read(in)) { saveList.push_back(SaveStateDescriptor(slot, header._saveName)); - header._thumbnail->free(); } delete in; } @@ -159,7 +158,7 @@ SaveStateDescriptor VoyeurMetaEngine::querySaveMetaInfos(const char *target, int if (f) { Voyeur::VoyeurSavegameHeader header; - header.read(f); + header.read(f, false); delete f; // Create the return descriptor diff --git a/engines/voyeur/files_threads.cpp b/engines/voyeur/files_threads.cpp index 1b4e30665c..af8753c488 100644 --- a/engines/voyeur/files_threads.cpp +++ b/engines/voyeur/files_threads.cpp @@ -864,7 +864,7 @@ const byte *ThreadResource::cardPerform(const byte *card) { if (cardPerform2(card, id)) { card += subId; card = cardPerform(card); - while (*card++ != 61) ; + while (*card++ != 61) {} } else { card += subId; while (*card != 61 && *card != 29) diff --git a/engines/voyeur/voyeur.cpp b/engines/voyeur/voyeur.cpp index 7f2f0e312e..b7769c1fd4 100644 --- a/engines/voyeur/voyeur.cpp +++ b/engines/voyeur/voyeur.cpp @@ -789,9 +789,6 @@ void VoyeurEngine::loadGame(int slot) { VoyeurSavegameHeader header; if (!header.read(saveFile)) return; - if (header._thumbnail) - header._thumbnail->free(); - delete header._thumbnail; serializer.setVersion(header._version); synchronize(serializer); @@ -856,9 +853,7 @@ void VoyeurEngine::synchronize(Common::Serializer &s) { /*------------------------------------------------------------------------*/ -bool VoyeurSavegameHeader::read(Common::InSaveFile *f) { - _thumbnail = NULL; - +bool VoyeurSavegameHeader::read(Common::InSaveFile *f, bool skipThumbnail) { uint32 signature = f->readUint32BE(); if (signature != MKTAG('V', 'O', 'Y', 'R')) { warning("Invalid savegame"); @@ -875,9 +870,9 @@ bool VoyeurSavegameHeader::read(Common::InSaveFile *f) { _saveName += c; // Get the thumbnail - _thumbnail = Graphics::loadThumbnail(*f); - if (!_thumbnail) + if (!Graphics::loadThumbnail(*f, _thumbnail, skipThumbnail)) { return false; + } // Read in the save datet/ime _saveYear = f->readSint16LE(); diff --git a/engines/voyeur/voyeur.h b/engines/voyeur/voyeur.h index dcd82b24a9..a098ba9e62 100644 --- a/engines/voyeur/voyeur.h +++ b/engines/voyeur/voyeur.h @@ -312,7 +312,7 @@ struct VoyeurSavegameHeader { /** * Read in the header from the specified file */ - bool read(Common::InSaveFile *f); + bool read(Common::InSaveFile *f, bool skipThumbnail = true); /** * Write out header information to the specified file diff --git a/engines/wintermute/base/base_engine.h b/engines/wintermute/base/base_engine.h index cbf5d92d00..905d227d3c 100644 --- a/engines/wintermute/base/base_engine.h +++ b/engines/wintermute/base/base_engine.h @@ -34,10 +34,24 @@ #include "common/random.h" #include "common/language.h" -#include "engines/wintermute/game_description.h" - namespace Wintermute { +enum WMETargetExecutable { + OLDEST_VERSION, + WME_1_0_0, + WME_1_1_0, + WME_1_2_0, + WME_1_3_0, + WME_1_4_0, + WME_1_5_0, + WME_1_6_0, + WME_1_7_0, + WME_1_8_0, + WME_1_8_6, + WME_1_9_0, + LATEST_VERSION +}; + class BaseFileManager; class BaseRegistry; class BaseGame; diff --git a/engines/wintermute/base/gfx/osystem/base_render_osystem.cpp b/engines/wintermute/base/gfx/osystem/base_render_osystem.cpp index 0f6a184cb3..15cd33d28c 100644 --- a/engines/wintermute/base/gfx/osystem/base_render_osystem.cpp +++ b/engines/wintermute/base/gfx/osystem/base_render_osystem.cpp @@ -125,7 +125,7 @@ bool BaseRenderOSystem::initRenderer(int width, int height, bool windowed) { Graphics::PixelFormat format(4, 8, 8, 8, 8, 24, 16, 8, 0); g_system->beginGFXTransaction(); - g_system->initSize(_width, _height, &format); + g_system->initSize(_width, _height, &format); OSystem::TransactionError gfxError = g_system->endGFXTransaction(); if (gfxError != OSystem::kTransactionSuccess) { diff --git a/engines/wintermute/base/gfx/osystem/base_render_osystem.h b/engines/wintermute/base/gfx/osystem/base_render_osystem.h index bc267fd656..47099046e9 100644 --- a/engines/wintermute/base/gfx/osystem/base_render_osystem.h +++ b/engines/wintermute/base/gfx/osystem/base_render_osystem.h @@ -96,7 +96,7 @@ public: bool setViewport(Rect32 *rect) override { return BaseRenderer::setViewport(rect); } Rect32 getViewPort() override; void modTargetRect(Common::Rect *rect); - void pointFromScreen(Point32 *point) ; + void pointFromScreen(Point32 *point); void pointToScreen(Point32 *point); void dumpData(const char *filename) override; diff --git a/engines/wintermute/base/particles/part_emitter.cpp b/engines/wintermute/base/particles/part_emitter.cpp index c64a099cee..1c102d17ee 100644 --- a/engines/wintermute/base/particles/part_emitter.cpp +++ b/engines/wintermute/base/particles/part_emitter.cpp @@ -214,7 +214,8 @@ bool PartEmitter::initParticle(PartParticle *particle, uint32 currentTime, uint3 Vector2 vecVel(0, velocity); Matrix4 matRot; - matRot.rotationZ(Common::deg2rad(BaseUtils::normalizeAngle(angle - 180))); + float radZrot = Common::deg2rad<float>(BaseUtils::normalizeAngle(angle - 180.0)); + matRot.rotationZ(radZrot); matRot.transformVector2(vecVel); if (_alphaTimeBased) { @@ -433,7 +434,8 @@ bool PartEmitter::addForce(const Common::String &name, PartForce::TForceType typ force->_direction = Vector2(0, strength); Matrix4 matRot; - matRot.rotationZ(Common::deg2rad(BaseUtils::normalizeAngle(angle - 180))); + float radZrot = Common::deg2rad<float>(BaseUtils::normalizeAngle(angle - 180.0)); + matRot.rotationZ(radZrot); matRot.transformVector2(force->_direction); return STATUS_OK; diff --git a/engines/wintermute/base/sound/base_sound_buffer.cpp b/engines/wintermute/base/sound/base_sound_buffer.cpp index 5fdac12cef..0c8103339f 100644 --- a/engines/wintermute/base/sound/base_sound_buffer.cpp +++ b/engines/wintermute/base/sound/base_sound_buffer.cpp @@ -33,7 +33,9 @@ #include "engines/wintermute/wintermute.h" #include "audio/audiostream.h" #include "audio/mixer.h" +#ifdef USE_VORBIS #include "audio/decoders/vorbis.h" +#endif #include "audio/decoders/wave.h" #include "audio/decoders/raw.h" #include "common/system.h" @@ -103,7 +105,11 @@ bool BaseSoundBuffer::loadFromFile(const Common::String &filename, bool forceRel Common::String strFilename(filename); strFilename.toLowercase(); if (strFilename.hasSuffix(".ogg")) { +#ifdef USE_VORBIS _stream = Audio::makeVorbisStream(_file, DisposeAfterUse::YES); +#else + error("BSoundBuffer::LoadFromFile - Ogg Vorbis not supported by this version of ScummVM (please report as this shouldn't trigger)"); +#endif } else if (strFilename.hasSuffix(".wav")) { int waveSize, waveRate; byte waveFlags; diff --git a/engines/wintermute/configure.engine b/engines/wintermute/configure.engine index 55385776de..29af5601aa 100644 --- a/engines/wintermute/configure.engine +++ b/engines/wintermute/configure.engine @@ -1,3 +1,3 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] -add_engine wintermute "Wintermute" yes "" "" "jpeg png zlib vorbis 16bit highres" +add_engine wintermute "Wintermute" yes "" "" "zlib 16bit highres" diff --git a/engines/wintermute/debugger/error.h b/engines/wintermute/debugger/error.h index 4e5b973445..0798fe7cb3 100644 --- a/engines/wintermute/debugger/error.h +++ b/engines/wintermute/debugger/error.h @@ -28,27 +28,27 @@ namespace Wintermute { enum ErrorLevel { - SUCCESS, - NOTICE, - WARNING, - ERROR + SUCCESS, + NOTICE, + WARNING, + ERROR }; enum ErrorCode { - OK, - NO_SUCH_SOURCE, - COULD_NOT_OPEN, - NO_SUCH_LINE, - NOT_ALLOWED, - NO_SUCH_BYTECODE, - DUPLICATE_BREAKPOINT, - NO_SUCH_BREAKPOINT, - WRONG_TYPE, - PARSE_ERROR, - NOT_YET_IMPLEMENTED, - SOURCE_PATH_NOT_SET, - ILLEGAL_PATH, - UNKNOWN_ERROR + OK, + NO_SUCH_SOURCE, + COULD_NOT_OPEN, + NO_SUCH_LINE, + NOT_ALLOWED, + NO_SUCH_BYTECODE, + DUPLICATE_BREAKPOINT, + NO_SUCH_BREAKPOINT, + WRONG_TYPE, + PARSE_ERROR, + NOT_YET_IMPLEMENTED, + SOURCE_PATH_NOT_SET, + ILLEGAL_PATH, + UNKNOWN_ERROR }; diff --git a/engines/wintermute/detection.cpp b/engines/wintermute/detection.cpp index 9ccb75d62f..6208d775a6 100644 --- a/engines/wintermute/detection.cpp +++ b/engines/wintermute/detection.cpp @@ -22,6 +22,7 @@ #include "engines/advancedDetector.h" #include "engines/wintermute/wintermute.h" +#include "engines/wintermute/game_description.h" #include "engines/wintermute/base/base_persistence_manager.h" #include "common/config-manager.h" @@ -99,7 +100,7 @@ public: return "Copyright (C) 2011 Jan Nedoma"; } - virtual const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const { + ADDetectedGame fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const override { // Set some defaults s_fallbackDesc.extra = ""; s_fallbackDesc.language = Common::UNK_LANG; @@ -129,10 +130,12 @@ public: s_fallbackDesc.extra = offset; s_fallbackDesc.flags |= ADGF_USEEXTRAASTITLE; } - return &s_fallbackDesc; + + return ADDetectedGame(&s_fallbackDesc); } // Fall through to return 0; } - return 0; + + return ADDetectedGame(); } virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const { diff --git a/engines/wintermute/detection_tables.h b/engines/wintermute/detection_tables.h index 5b87dd439c..68ad9c1751 100644 --- a/engines/wintermute/detection_tables.h +++ b/engines/wintermute/detection_tables.h @@ -43,7 +43,7 @@ static const PlainGameDescriptor wintermuteGames[] = { {"conspiracao", "Conspiracao Dumont"}, {"corrosion", "Corrosion: Cold Winter Waiting"}, {"deadcity", "Dead City"}, - {"dfafadventure", "DFAF Adventure"}, + {"dfafadventure", "DFAF Adventure"}, {"dreamcat", "Dreamcat"}, {"dreaming", "Des Reves Elastiques Avec Mille Insectes Nommes Georges"}, {"dirtysplit", "Dirty Split"}, @@ -243,7 +243,7 @@ static const WMEGameDescription gameDescriptions[] = { "data.dcp", "7ebfd50d1a22370ed7b079bcaa631d62", 9070205), Common::RU_RUS, ADGF_UNSTABLE, LATEST_VERSION), // DFAF Adventure WME_WINENTRY("dfafadventure", "", - WME_ENTRY1s("data.dcp","5704ebef961176f647742aa66bd09352", 10083417), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), + WME_ENTRY1s("data.dcp","5704ebef961176f647742aa66bd09352", 10083417), Common::EN_ANY, ADGF_UNSTABLE | GF_LOWSPEC_ASSETS, LATEST_VERSION), // Dirty Split (Czech) WME_WINENTRY("dirtysplit", "", WME_ENTRY2s("czech.dcp", "08a71446467cf8f9444cfea446b46ad6", 127697934, @@ -271,7 +271,7 @@ static const WMEGameDescription gameDescriptions[] = { WME_ENTRY1s("data.dcp", "4af26d97ea063fc1277ce30ae431de90", 8804073), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // Dreamcat WME_WINENTRY("dreamcat", "", - WME_ENTRY1s("data.dcp","189bd4eef29034f4ff4ed30120eaac4e", 7758040), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), + WME_ENTRY1s("data.dcp","189bd4eef29034f4ff4ed30120eaac4e", 7758040), Common::EN_ANY, ADGF_UNSTABLE | GF_LOWSPEC_ASSETS, LATEST_VERSION), // Dreamscape WME_WINENTRY("dreamscape", "", WME_ENTRY1s("data.dcp", "7a5752ed4446c862be9f02d7932acf54", 17034377), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), @@ -378,7 +378,7 @@ static const WMEGameDescription gameDescriptions[] = { "d_sounds.dcp", "7d04dff8ca11174486bd4b7a80fdcabb", 154943401), Common::ES_ESP, ADGF_UNSTABLE, LATEST_VERSION), // Open Quest WME_WINENTRY("openquest", "", - WME_ENTRY1s("data.dcp", "16893e3fc15a211a49654ae66f684f28", 82281736), Common::EN_ANY, ADGF_UNSTABLE | ADGF_DEMO, LATEST_VERSION), + WME_ENTRY1s("data.dcp", "16893e3fc15a211a49654ae66f684f28", 82281736), Common::EN_ANY, ADGF_UNSTABLE | GF_LOWSPEC_ASSETS, LATEST_VERSION), // Night Train Demo WME_WINENTRY("nighttrain", "", WME_ENTRY1s("data.dcp", "5a027ef84b083a730c9a4c85ec1d3a32", 131760816), Common::EN_ANY, ADGF_UNSTABLE | ADGF_DEMO, LATEST_VERSION), @@ -589,4 +589,3 @@ static const WMEGameDescription gameDescriptions[] = { #undef WEM_ENTRY3s #undef WME_WINENTRY #undef WME_PLATENTRY - diff --git a/engines/wintermute/game_description.h b/engines/wintermute/game_description.h index 313fff8bbf..92f62dd7f6 100644 --- a/engines/wintermute/game_description.h +++ b/engines/wintermute/game_description.h @@ -24,25 +24,10 @@ #define WINTERMUTE_GAME_DESCRIPTION_H #include "engines/advancedDetector.h" +#include "engines/wintermute/base/base_engine.h" namespace Wintermute { -enum WMETargetExecutable { - OLDEST_VERSION, - WME_1_0_0, - WME_1_1_0, - WME_1_2_0, - WME_1_3_0, - WME_1_4_0, - WME_1_5_0, - WME_1_6_0, - WME_1_7_0, - WME_1_8_0, - WME_1_8_6, - WME_1_9_0, - LATEST_VERSION -}; - struct WMEGameDescription { ADGameDescription adDesc; WMETargetExecutable targetExecutable; diff --git a/engines/wintermute/wintermute.cpp b/engines/wintermute/wintermute.cpp index e68004d1e5..77df30a54a 100644 --- a/engines/wintermute/wintermute.cpp +++ b/engines/wintermute/wintermute.cpp @@ -34,6 +34,7 @@ #include "engines/wintermute/ad/ad_game.h" #include "engines/wintermute/wintermute.h" #include "engines/wintermute/debugger.h" +#include "engines/wintermute/game_description.h" #include "engines/wintermute/platform_osystem.h" #include "engines/wintermute/base/base_engine.h" @@ -43,6 +44,8 @@ #include "engines/wintermute/base/scriptables/script_engine.h" #include "engines/wintermute/debugger/debugger_controller.h" +#include "gui/message.h" + namespace Wintermute { // Simple constructor for detection - we need to setup the persistence to avoid special-casing in-engine @@ -109,7 +112,11 @@ bool WintermuteEngine::hasFeature(EngineFeature f) const { Common::Error WintermuteEngine::run() { // Initialize graphics using following: Graphics::PixelFormat format(4, 8, 8, 8, 8, 24, 16, 8, 0); - initGraphics(800, 600, &format); + if (_gameDescription->adDesc.flags & GF_LOWSPEC_ASSETS) { + initGraphics(320, 240, &format); + } else { + initGraphics(800, 600, &format); + } if (g_system->getScreenFormat() != format) { error("Wintermute currently REQUIRES 32bpp"); } @@ -138,6 +145,18 @@ Common::Error WintermuteEngine::run() { int WintermuteEngine::init() { BaseEngine::createInstance(_targetName, _gameDescription->adDesc.gameId, _gameDescription->adDesc.language, _gameDescription->targetExecutable); + + // check dependencies for games with high resolution assets + #if not defined(USE_PNG) || not defined(USE_JPEG) || not defined(USE_VORBIS) + if (!(_gameDescription->adDesc.flags & GF_LOWSPEC_ASSETS)) { + GUI::MessageDialog dialog("This game requires PNG, JPEG and Vorbis support."); + dialog.runModal(); + delete _game; + _game = nullptr; + return false; + } + #endif + _game = new AdGame(_targetName); if (!_game) { return 1; diff --git a/engines/wintermute/wintermute.h b/engines/wintermute/wintermute.h index a8f9a18530..fe999df082 100644 --- a/engines/wintermute/wintermute.h +++ b/engines/wintermute/wintermute.h @@ -24,9 +24,8 @@ #define WINTERMUTE_WINTERMUTE_H #include "engines/engine.h" -#include "engines/advancedDetector.h" #include "gui/debugger.h" -#include "engines/wintermute/game_description.h" +#include "common/fs.h" namespace Wintermute { @@ -34,6 +33,7 @@ class Console; class BaseGame; class SystemClassRegistry; class DebuggerController; +struct WMEGameDescription; // our engine debug channels enum { @@ -45,6 +45,11 @@ enum { kWintermuteDebugGeneral = 1 << 5 }; +enum WintermuteGameFeatures { + /** A game with low-spec resources. */ + GF_LOWSPEC_ASSETS = 1 << 0 +}; + class WintermuteEngine : public Engine { public: WintermuteEngine(OSystem *syst, const WMEGameDescription *desc); diff --git a/engines/xeen/POTFILES b/engines/xeen/POTFILES new file mode 100644 index 0000000000..27314a435e --- /dev/null +++ b/engines/xeen/POTFILES @@ -0,0 +1 @@ +engines/xeen/detection.cpp diff --git a/engines/xeen/character.cpp b/engines/xeen/character.cpp index 248d432a82..afc013ba89 100644 --- a/engines/xeen/character.cpp +++ b/engines/xeen/character.cpp @@ -37,14 +37,27 @@ void AttributePair::synchronize(Common::Serializer &s) { /*------------------------------------------------------------------------*/ -Character::Character(): - _weapons(this), _armor(this), _accessories(this), _misc(this), - _items(_weapons, _armor, _accessories, _misc) { +int CharacterArray::indexOf(const Character &c) { + for (uint idx = 0; idx < size(); ++idx) { + if ((*this)[idx] == c) + return idx; + } + + return -1; +} + +/*------------------------------------------------------------------------*/ + +Character::Character(): _weapons(this), _armor(this), _accessories(this), _misc(this), _items(this) { clear(); _faceSprites = nullptr; _rosterId = -1; } +Character::Character(const Character &src) : _weapons(this), _armor(this), _accessories(this), _misc(this), _items(this) { + operator=(src); +} + void Character::clear() { _sex = MALE; _race = HUMAN; @@ -90,6 +103,65 @@ void Character::clear() { _misc.clear(); } +Character &Character::operator=(const Character &src) { + clear(); + + _faceSprites = src._faceSprites; + _rosterId = src._rosterId; + _name = src._name; + _sex = src._sex; + _race = src._race; + _xeenSide = src._xeenSide; + _class = src._class; + _might = src._might; + _intellect = src._intellect; + _personality = src._personality; + _endurance = src._endurance; + _speed = src._speed; + _accuracy = src._accuracy; + _luck = src._luck; + _ACTemp = src._ACTemp; + _level = src._level; + _birthDay = src._birthDay; + _tempAge = src._tempAge; + Common::copy(&src._skills[0], &src._skills[18], &_skills[0]); + Common::copy(&src._awards[0], &src._awards[128], &_awards[0]); + Common::copy(&src._spells[0], &src._spells[SPELLS_PER_CLASS], &_spells[0]); + _lloydMap = src._lloydMap; + _lloydPosition = src._lloydPosition; + _hasSpells = src._hasSpells; + _currentSpell = src._currentSpell; + _quickOption = src._quickOption; + _weapons = src._weapons; + _armor = src._armor; + _accessories = src._accessories; + _misc = src._misc; + _lloydSide = src._lloydSide; + _fireResistence = src._fireResistence; + _coldResistence = src._coldResistence; + _electricityResistence = src._electricityResistence; + _poisonResistence = src._poisonResistence; + _energyResistence = src._energyResistence; + _magicResistence = src._magicResistence; + Common::copy(&src._conditions[0], &src._conditions[16], &_conditions[0]); + _townUnknown = src._townUnknown; + _savedMazeId = src._savedMazeId; + _currentHp = src._currentHp; + _currentSp = src._currentSp; + _birthYear = src._birthYear; + _experience = src._experience; + _currentAdventuringSpell = src._currentAdventuringSpell; + _currentCombatSpell = src._currentCombatSpell; + + for (ItemCategory category = CATEGORY_WEAPON; category <= CATEGORY_MISC; category = (ItemCategory)((int)category + 1)) { + const InventoryItems &srcItems = src._items[category]; + InventoryItems &destItems = _items[category]; + destItems = srcItems; + } + + return *this; +} + void Character::synchronize(Common::Serializer &s) { char name[16]; Common::fill(&name[0], &name[16], '\0'); @@ -124,7 +196,7 @@ void Character::synchronize(Common::Serializer &s) { // upper nibble of the first 64 bytes. Except for award 9, which was a full // byte counter counting the number of times the warzone was awarded for (int idx = 0; idx < 64; ++idx) { - byte b = (idx == WARZONE_AWARD) ? _awards[idx] : + byte b = (idx == WARZONE_AWARD) ? _awards[idx] : (_awards[idx] ? 0x1 : 0) | (_awards[idx + 64] ? 0x10 : 0); s.syncAsByte(b); if (s.isLoading()) { @@ -134,7 +206,7 @@ void Character::synchronize(Common::Serializer &s) { } // Synchronize spell list - for (int i = 0; i < MAX_SPELLS_PER_CLASS; ++i) + for (int i = 0; i < SPELLS_PER_CLASS; ++i) s.syncAsByte(_spells[i]); s.syncAsByte(_lloydMap); s.syncAsByte(_lloydPosition.x); @@ -165,8 +237,8 @@ void Character::synchronize(Common::Serializer &s) { s.syncAsUint16LE(_townUnknown); s.syncAsByte(_savedMazeId); - s.syncAsUint16LE(_currentHp); - s.syncAsUint16LE(_currentSp); + s.syncAsSint16LE(_currentHp); + s.syncAsSint16LE(_currentSp); s.syncAsUint16LE(_birthYear); s.syncAsUint32LE(_experience); s.syncAsByte(_currentAdventuringSpell); @@ -338,7 +410,7 @@ int Character::statColor(int amount, int threshold) { return 2; else if (amount == threshold) return 15; - else if (amount <= (threshold / 4)) + else if (amount >= (threshold / 4)) return 9; else return 32; @@ -393,12 +465,12 @@ bool Character::noActions() { Condition condition = worstCondition(); switch (condition) { - case CURSED: - case POISONED: - case DISEASED: - case INSANE: - case IN_LOVE: - case DRUNK: { + case ASLEEP: + case PARALYZED: + case UNCONSCIOUS: + case DEAD: + case STONED: + case ERADICATED: { Common::String msg = Common::String::format(Res.IN_NO_CONDITION, _name.c_str()); ErrorScroll::show(Party::_vm, msg, Party::_vm->_mode == 17 ? WT_LOC_WAIT : WT_NONFREEZED_WAIT); @@ -482,7 +554,7 @@ int Character::itemScan(int itemId) const { for (int idx = 0; idx < INV_ITEMS_TOTAL; ++idx) { const XeenItem &item = _weapons[idx]; - if (item._frame && !(item._bonusFlags & 0xC0) && itemId < 11 + if (item._frame && !item.isBad() && itemId < 11 && itemId != 3 && item._material >= 59 && item._material <= 130) { int mIndex = (int)item.getAttributeCategory(); if (mIndex > PERSONALITY) @@ -497,7 +569,7 @@ int Character::itemScan(int itemId) const { for (int idx = 0; idx < INV_ITEMS_TOTAL; ++idx) { const XeenItem &item = _armor[idx]; - if (item._frame && !(item._bonusFlags & 0xC0)) { + if (item._frame && !item.isBad()) { if (itemId < 11 && itemId != 3 && item._material >= 59 && item._material <= 130) { int mIndex = (int)item.getAttributeCategory(); if (mIndex > PERSONALITY) @@ -528,7 +600,7 @@ int Character::itemScan(int itemId) const { for (int idx = 0; idx < INV_ITEMS_TOTAL; ++idx) { const XeenItem &item = _accessories[idx]; - if (item._frame && !(item._bonusFlags & 0xC0)) { + if (item._frame && !item.isBad()) { if (itemId < 11 && itemId != 3 && item._material >= 59 && item._material <= 130) { int mIndex = (int)item.getAttributeCategory(); if (mIndex > PERSONALITY) @@ -800,21 +872,36 @@ bool Character::guildMember() const { FileManager &files = *g_vm->_files; Party &party = *g_vm->_party; - if (party._mazeId == 49 && !files._isDarkCc) { + if (g_vm->getGameID() == GType_Swords) { + switch (party._mazeId) { + case 49: + return true; + case 53: + return hasAward(83); + case 63: + return hasAward(85); + case 92: + return hasAward(84); + default: + return hasAward(87); + } + } else if (files._ccNum) { + switch (party._mazeId) { + case 29: + return hasAward(CASTLEVIEW_GUILD_MEMBER); + case 31: + return hasAward(SANDCASTER_GUILD_MEMBER); + case 33: + return hasAward(LAKESIDE_GUILD_MEMBER); + case 35: + return hasAward(NECROPOLIS_GUILD_MEMBER); + default: + return hasAward(OLYMPUS_GUILD_MEMBER); + } + } else if (party._mazeId == 49) { return hasAward(SHANGRILA_GUILD_MEMBER); - } - - switch (party._mazeId) { - case 29: - return hasAward(CASTLEVIEW_GUILD_MEMBER); - case 31: - return hasAward(SANDCASTER_GUILD_MEMBER); - case 33: - return hasAward(LAKESIDE_GUILD_MEMBER); - case 35: - return hasAward(NECROPOLIS_GUILD_MEMBER); - default: - return hasAward(OLYMPUS_GUILD_MEMBER); + } else { + return hasAward(party._mazeId - 28); } } @@ -881,6 +968,7 @@ int Character::getNumAwards() const { ItemCategory Character::makeItem(int p1, int itemIndex, int p3) { XeenEngine *vm = Party::_vm; Scripts &scripts = *vm->_scripts; + int itemOffset = vm->getGameID() == GType_Swords ? 6 : 0; if (!p1) return CATEGORY_WEAPON; @@ -889,22 +977,22 @@ ItemCategory Character::makeItem(int p1, int itemIndex, int p3) { int v4 = vm->getRandomNumber(100); int v6 = vm->getRandomNumber(p1 < 6 ? 100 : 80); ItemCategory category; - int v16 = 0, v14 = 0, miscBonus = 0, miscId = 0, v8 = 0, v12 = 0; + int v16 = 0, v14 = 0, miscCharges = 0, miscId = 0, v8 = 0, v12 = 0; // Randomly pick a category and item Id if (p3 == 12) { - if (scripts._itemType < 35) { + if (scripts._itemType < (35 + itemOffset)) { category = CATEGORY_WEAPON; itemId = scripts._itemType; - } else if (scripts._itemType < 49) { + } else if (scripts._itemType < (49 + itemOffset)) { category = CATEGORY_ARMOR; - itemId = scripts._itemType - 35; - } else if (scripts._itemType < 60) { + itemId = scripts._itemType - (35 + itemOffset); + } else if (scripts._itemType < (60 + itemOffset)) { category = CATEGORY_ACCESSORY; - itemId = scripts._itemType - 49; + itemId = scripts._itemType - (49 + itemOffset); } else { category = CATEGORY_MISC; - itemId = scripts._itemType - 60; + itemId = scripts._itemType - (60 + itemOffset); } } else { switch (p3) { @@ -1081,7 +1169,7 @@ ItemCategory Character::makeItem(int p1, int itemIndex, int p3) { break; case 4: - miscBonus = vm->getRandomNumber(Res.MAKE_ITEM_ARR5[p1][0], Res.MAKE_ITEM_ARR5[p1][1]); + miscCharges = vm->getRandomNumber(Res.MAKE_ITEM_ARR5[p1][0], Res.MAKE_ITEM_ARR5[p1][1]); break; default: @@ -1094,7 +1182,7 @@ ItemCategory Character::makeItem(int p1, int itemIndex, int p3) { if (p1 != 1) { newItem._material = (v14 ? v14 + 58 : 0) + (v16 ? v16 + 36 : 0) + v12; if (vm->getRandomNumber(20) == 10) - newItem._bonusFlags = vm->getRandomNumber(1, 6); + newItem._state._counter = vm->getRandomNumber(1, 6); } break; @@ -1107,7 +1195,7 @@ ItemCategory Character::makeItem(int p1, int itemIndex, int p3) { case CATEGORY_MISC: newItem._id = miscId; - newItem._bonusFlags = miscBonus; + newItem._state._counter = miscCharges; break; default: @@ -1134,6 +1222,7 @@ void Character::addHitPoints(int amount) { intf.drawParty(true); } + assert(_currentHp < 65000); Common::fill(&intf._charFX[0], &intf._charFX[MAX_ACTIVE_PARTY], 0); } @@ -1147,7 +1236,8 @@ void Character::subtractHitPoints(int amount) { // Subtract the given HP amount _currentHp -= amount; - bool flag = _currentHp <= 10; + bool breakFlag = _currentHp <= (g_vm->_extOptions._durableArmor ? -80 : -10); + assert(_currentHp < 65000); if (_currentHp < 1) { int v = getMaxHP() + _currentHp; @@ -1156,17 +1246,17 @@ void Character::subtractHitPoints(int amount) { sound.playFX(38); } else { _conditions[DEAD] = 1; - flag = true; + breakFlag = true; if (_currentHp > 0) _currentHp = 0; } - if (flag) { - // Check for breaking equipped armor + if (breakFlag) { + // Break any equipped armor the character has for (int idx = 0; idx < INV_ITEMS_TOTAL; ++idx) { XeenItem &item = _armor[idx]; if (item._id && item._frame) - item._bonusFlags |= ITEMFLAG_BROKEN; + item._state._broken = true; } } } @@ -1174,7 +1264,7 @@ void Character::subtractHitPoints(int amount) { bool Character::hasSlayerSword() const { for (uint idx = 0; idx < INV_ITEMS_TOTAL; ++idx) { - if (_weapons[idx]._id == 34) + if (_weapons[idx]._id == XEEN_SLAYER_SWORD) // Character has Xeen Slayer sword return true; } @@ -1192,18 +1282,22 @@ bool Character::hasMissileWeapon() const { return false; } -int Character::getClassCategory() const { +SpellsCategory Character::getSpellsCategory() const { switch (_class) { + case CLASS_PALADIN: + case CLASS_CLERIC: + return SPELLCAT_CLERICAL; + case CLASS_ARCHER: case CLASS_SORCERER: - return 1; + return SPELLCAT_WIZARDRY; case CLASS_DRUID: case CLASS_RANGER: - return 2; + return SPELLCAT_DRUIDIC; default: - return 0; + return SPELLCAT_INVALID; } } diff --git a/engines/xeen/character.h b/engines/xeen/character.h index 05a61c3f2e..47312efe66 100644 --- a/engines/xeen/character.h +++ b/engines/xeen/character.h @@ -34,7 +34,7 @@ namespace Xeen { #define INV_ITEMS_TOTAL 9 -#define MAX_SPELLS_PER_CLASS 39 +#define SPELLS_PER_CLASS 39 #define AWARDS_TOTAL 88 #define WARZONE_AWARD 9 @@ -44,10 +44,6 @@ enum Award { LAKESIDE_GUILD_MEMBER = 85, NECROPOLIS_GUILD_MEMBER = 86, OLYMPUS_GUILD_MEMBER = 87 }; -enum BonusFlags { - ITEMFLAG_BONUS_MASK = 0xBF, ITEMFLAG_CURSED = 0x40, ITEMFLAG_BROKEN = 0x80 -}; - enum Sex { MALE = 0, FEMALE = 1, YES_PLEASE = 2 }; enum Race { HUMAN = 0, ELF = 1, DWARF = 2, GNOME = 3, HALF_ORC = 4 }; @@ -55,7 +51,11 @@ enum Race { HUMAN = 0, ELF = 1, DWARF = 2, GNOME = 3, HALF_ORC = 4 }; enum CharacterClass { CLASS_KNIGHT = 0, CLASS_PALADIN = 1, CLASS_ARCHER = 2, CLASS_CLERIC = 3, CLASS_SORCERER = 4, CLASS_ROBBER = 5, CLASS_NINJA = 6, CLASS_BARBARIAN = 7, - CLASS_DRUID = 8, CLASS_RANGER = 9, TOTAL_CLASSES = 10, CLASS_12 = 12, CLASS_15 = 15, CLASS_16 = 16 + CLASS_DRUID = 8, CLASS_RANGER = 9, TOTAL_CLASSES = 10 +}; + +enum HatesClass { + HATES_DWARF = 12, HATES_PARTY = 15, HATES_NOBODY = 16 }; enum Attribute { @@ -83,6 +83,11 @@ enum QuickAction { QUICK_ATTACK = 0, QUICK_SPELL = 1, QUICK_BLOCK = 2, QUICK_RUN = 3 }; +enum SpellsCategory { + SPELLCAT_INVALID = -1, SPELLCAT_CLERICAL = 0, SPELLCAT_WIZARDRY = 1, SPELLCAT_DRUIDIC = 2 +}; + + class XeenEngine; class AttributePair { @@ -119,17 +124,17 @@ public: int _tempAge; int _skills[18]; int _awards[128]; - bool _spells[MAX_SPELLS_PER_CLASS]; + bool _spells[SPELLS_PER_CLASS]; int _lloydMap; Common::Point _lloydPosition; bool _hasSpells; int8 _currentSpell; QuickAction _quickOption; - InventoryItemsGroup _items; WeaponItems _weapons; ArmorItems _armor; AccessoryItems _accessories; MiscItems _misc; + InventoryItemsGroup _items; int _lloydSide; AttributePair _fireResistence; AttributePair _coldResistence; @@ -150,14 +155,37 @@ public: SpriteResource *_faceSprites; int _rosterId; public: + /** + * Constructor + */ Character(); /** + * Constructor + */ + Character(const Character &src); + + /** + * Equality operator + */ + bool operator==(const Character &src) const { return src._rosterId == _rosterId; } + + /** + * Inequality operator + */ + bool operator!=(const Character &src) const { return src._rosterId != _rosterId; } + + /** * Clears the data for a character */ void clear(); /** + * Assignment operator + */ + Character &operator=(const Character &src); + + /** * Synchronizes data for the character */ void synchronize(Common::Serializer &s); @@ -311,9 +339,16 @@ public: bool hasMissileWeapon() const; /** - * Returns a category index for a character, used such for indexing into spell data + * Returns the spells category for the character's class */ - int getClassCategory() const; + SpellsCategory getSpellsCategory() const; + + /** + * Returns an expense factor for purchasing spells by certain character classes + */ + int getSpellsExpenseFactor() const { + return (_class == CLASS_PALADIN || _class == CLASS_ARCHER || _class == CLASS_RANGER) ? 1 : 0; + } /** * Clears the character of any currently set conditions @@ -321,6 +356,14 @@ public: void clearConditions(); }; +class CharacterArray : public Common::Array<Character> { +public: + /** + * Returns the index of a given character in the array + */ + int indexOf(const Character &c); +}; + } // End of namespace Xeen #endif /* XEEN_CHARACTER_H */ diff --git a/engines/xeen/combat.cpp b/engines/xeen/combat.cpp index 603b6aef71..4dd2ac6e8a 100644 --- a/engines/xeen/combat.cpp +++ b/engines/xeen/combat.cpp @@ -114,6 +114,7 @@ Combat::Combat(XeenEngine *vm): _vm(vm), _missVoc("miss.voc") { _monsterDamage = 0; _weaponDamage = 0; _weaponDie = _weaponDice = 0; + _weaponElemMaterial = 0; _attackWeapon = nullptr; _attackWeaponId = 0; _hitChanceBonus = 0; @@ -141,9 +142,8 @@ void Combat::giveCharDamage(int damage, DamageType attackType, int charIndex) { Party &party = *_vm->_party; Sound &sound = *_vm->_sound; Windows &windows = *_vm->_windows; - int charIndex1 = charIndex + 1; - int selectedIndex1 = 0; - int selectedIndex2 = 0; + int endIndex = charIndex + 1; + int selectedIndex = 0; bool breakFlag = false; windows.closeAll(); @@ -155,11 +155,11 @@ void Combat::giveCharDamage(int damage, DamageType attackType, int charIndex) { Condition condition = c.worstCondition(); if (!(condition >= UNCONSCIOUS && condition <= ERADICATED)) { - if (!selectedIndex1) { - selectedIndex1 = idx + 1; + if (!charIndex) { + charIndex = idx + 1; } else { - selectedIndex2 = idx + 1; - --selectedIndex1; + selectedIndex = idx + 1; + --charIndex; break; } } @@ -167,12 +167,12 @@ void Combat::giveCharDamage(int damage, DamageType attackType, int charIndex) { } if (idx == (int)party._activeParty.size()) { if (!_combatTarget) - selectedIndex1 = 0; + charIndex = 0; } for (;;) { - for (; selectedIndex1 < (_combatTarget ? charIndex1 : (int)party._activeParty.size()); ++selectedIndex1) { - Character &c = party._activeParty[selectedIndex1]; + for (; charIndex < (_combatTarget ? endIndex : (int)party._activeParty.size()); ++charIndex) { + Character &c = party._activeParty[charIndex]; c._conditions[ASLEEP] = 0; // Force attacked character to be awake int frame = 0, fx = 0; @@ -224,7 +224,7 @@ void Combat::giveCharDamage(int damage, DamageType attackType, int charIndex) { // Draw the attack effect on the character sprite sound.playFX(fx); - intf._charPowSprites.draw(0, frame, Common::Point(Res.CHAR_FACES_X[selectedIndex1], 150)); + intf._charPowSprites.draw(0, frame, Common::Point(Res.CHAR_FACES_X[charIndex], 150)); windows[33].update(); // Reduce damage if power shield active, and set it zero @@ -235,7 +235,6 @@ void Combat::giveCharDamage(int damage, DamageType attackType, int charIndex) { if (damage < 0) damage = 0; - // Attacked characters which are asleep are killed if (attackType == DT_SLEEP) { damage = c._currentHp; c._conditions[DEAD] = 1; @@ -243,15 +242,15 @@ void Combat::giveCharDamage(int damage, DamageType attackType, int charIndex) { // Subtract the hit points from the character c.subtractHitPoints(damage); - if (selectedIndex2) + if (selectedIndex) break; } // Break check and if not, move to other index - if (!selectedIndex2 || breakFlag) + if (!selectedIndex || breakFlag) break; - selectedIndex1 = selectedIndex2 - 1; + charIndex = selectedIndex - 1; breakFlag = true; } @@ -325,7 +324,7 @@ void Combat::doCharDamage(Character &c, int charNum, int monsterDataIndex) { intf._charPowSprites.draw(0, frame, Common::Point(Res.CHAR_FACES_X[charNum], 150)); windows[33].update(); - damage -= party._powerShield; + damage = MAX(damage - party._powerShield, 0); if (damage > 0 && monsterData._specialAttack && !c.charSavingThrow(DT_PHYSICAL)) { switch (monsterData._specialAttack) { case SA_POISON: @@ -349,13 +348,7 @@ void Combat::doCharDamage(Character &c, int charNum, int monsterDataIndex) { sound.playFX(36); break; case SA_CURSEITEM: - for (int idx = 0; idx < INV_ITEMS_TOTAL; ++idx) { - if (c._weapons[idx]._id != 34) - c._weapons[idx]._bonusFlags |= ITEMFLAG_CURSED; - c._armor[idx]._bonusFlags |= ITEMFLAG_CURSED; - c._accessories[idx]._bonusFlags |= ITEMFLAG_CURSED; - c._misc[idx]._bonusFlags |= ITEMFLAG_CURSED; - } + c._items.curseUncurse(true); sound.playFX(37); break; case SA_DRAINSP: @@ -385,8 +378,8 @@ void Combat::doCharDamage(Character &c, int charNum, int monsterDataIndex) { case SA_BREAKWEAPON: for (int idx = 0; idx < INV_ITEMS_TOTAL; ++idx) { XeenItem &weapon = c._weapons[idx]; - if (weapon._id != 34 && weapon._id != 0 && weapon._frame != 0) { - weapon._bonusFlags |= ITEMFLAG_BROKEN; + if (weapon._id < XEEN_SLAYER_SWORD && weapon._id != 0 && weapon._frame != 0) { + weapon._state._broken = true; weapon._frame = 0; } } @@ -428,15 +421,15 @@ void Combat::doCharDamage(Character &c, int charNum, int monsterDataIndex) { default: break; } - - if (debugger._invincible) - // Invincibility mode is on, so reset conditions that were set - c.clearConditions(); - else - // Standard gameplay, deal out the damage - c.subtractHitPoints(damage); } + if (debugger._invincible) + // Invincibility mode is on, so reset conditions that were set + c.clearConditions(); + else + // Standard gameplay, deal out the damage + c.subtractHitPoints(damage); + events.ipause(2); intf.drawParty(true); } @@ -461,7 +454,9 @@ void Combat::moveMonsters() { for (uint idx = 0; idx < map._mobData._monsters.size(); ++idx) { MazeMonster &monster = map._mobData._monsters[idx]; - if ((uint)monster._position.y < 32) { + + // WORKAROUND: Original only checked on y, but some monsters have an invalid X instead + if ((uint)monster._position.x < 32 && (uint)monster._position.y < 32) { assert((uint)monster._position.x < 32); _monsterMap[monster._position.y][monster._position.x]++; } @@ -496,12 +491,12 @@ void Combat::moveMonsters() { switch (party._mazeDirection) { case DIR_NORTH: case DIR_SOUTH: - if (monsterCanMove(pt, Res.MONSTER_GRID_BITMASK[MONSTER_GRID_BITINDEX1[arrIndex]], + if (canMonsterMove(pt, Res.MONSTER_GRID_BITMASK[MONSTER_GRID_BITINDEX1[arrIndex]], MONSTER_GRID_X[arrIndex], MONSTER_GRID_Y[arrIndex], idx)) { // Move the monster moveMonster(idx, Common::Point(MONSTER_GRID_X[arrIndex], MONSTER_GRID_Y[arrIndex])); } else { - if (monsterCanMove(pt, Res.MONSTER_GRID_BITMASK[MONSTER_GRID_BITINDEX2[arrIndex]], + if (canMonsterMove(pt, Res.MONSTER_GRID_BITMASK[MONSTER_GRID_BITINDEX2[arrIndex]], arrIndex >= 21 && arrIndex <= 27 ? MONSTER_GRID3[arrIndex] : 0, arrIndex >= 21 && arrIndex <= 27 ? 0 : MONSTER_GRID3[arrIndex], idx)) { @@ -516,7 +511,7 @@ void Combat::moveMonsters() { case DIR_EAST: case DIR_WEST: - if (monsterCanMove(pt, Res.MONSTER_GRID_BITMASK[MONSTER_GRID_BITINDEX2[arrIndex]], + if (canMonsterMove(pt, Res.MONSTER_GRID_BITMASK[MONSTER_GRID_BITINDEX2[arrIndex]], arrIndex >= 21 && arrIndex <= 27 ? MONSTER_GRID3[arrIndex] : 0, arrIndex >= 21 && arrIndex <= 27 ? 0 : MONSTER_GRID3[arrIndex], idx)) { @@ -525,7 +520,7 @@ void Combat::moveMonsters() { } else { moveMonster(idx, Common::Point(0, MONSTER_GRID3[arrIndex])); } - } else if (monsterCanMove(pt, Res.MONSTER_GRID_BITMASK[MONSTER_GRID_BITINDEX1[arrIndex]], + } else if (canMonsterMove(pt, Res.MONSTER_GRID_BITMASK[MONSTER_GRID_BITINDEX1[arrIndex]], MONSTER_GRID_X[arrIndex], MONSTER_GRID_Y[arrIndex], idx)) { moveMonster(idx, Common::Point(MONSTER_GRID_X[arrIndex], MONSTER_GRID_Y[arrIndex])); } @@ -638,12 +633,12 @@ void Combat::monstersAttack() { _monstersAttacking = false; - if (_vm->_mode != MODE_SLEEPING) { + if (_vm->_mode == MODE_SLEEPING) { for (uint charNum = 0; charNum < party._activeParty.size(); ++charNum) { Condition condition = party._activeParty[charNum].worstCondition(); - if (condition != ASLEEP && (condition < PARALYZED || condition == NO_CONDITION)) { - _vm->_mode = MODE_1; + if (condition == DEPRESSED || condition == CONFUSED || condition == NO_CONDITION) { + _vm->_mode = MODE_INTERACTIVE; break; } } @@ -675,8 +670,7 @@ void Combat::setupMonsterAttack(int monsterDataIndex, const Common::Point &pt) { } } -bool Combat::monsterCanMove(const Common::Point &pt, int wallShift, - int xDiff, int yDiff, int monsterId) { +bool Combat::canMonsterMove(const Common::Point &pt, int wallShift, int xDiff, int yDiff, int monsterId) { Map &map = *_vm->_map; MazeMonster &monster = map._mobData._monsters[monsterId]; MonsterStruct &monsterData = *monster._monsterData; @@ -709,7 +703,7 @@ bool Combat::monsterCanMove(const Common::Point &pt, int wallShift, } else if (surfaceType == SURFTYPE_SPACE) { return monsterData._flying; } else { - return _vm->_files->_isDarkCc || monster._spriteId != 59; + return _vm->_files->_ccNum || monster._spriteId != 59; } default: return v <= map.mazeData()._difficulties._wallNoPass; @@ -722,6 +716,10 @@ void Combat::moveMonster(int monsterId, const Common::Point &moveDelta) { MazeMonster &monster = map._mobData._monsters[monsterId]; Common::Point newPos = monster._position + moveDelta; + // FIXME: Monster moved outside mapping area. Which shouldn't happen, so ignore the move if it does + if ((uint)newPos.x >= 32 || (uint)newPos.y >= 32) + return; + if (_monsterMap[newPos.y][newPos.x] < 3 && monster._damageType == DT_PHYSICAL && _moveMonsters) { // Adjust monster's position ++_monsterMap[newPos.y][newPos.x]; @@ -819,15 +817,20 @@ void Combat::doMonsterTurn(int monsterId) { } MonsterStruct &monsterData = map._monsterData[monsterId]; - bool flag = false; for (int attackNum = 0; attackNum < monsterData._numberOfAttacks; ++attackNum) { int charNum = -1; bool isHated = false; - if (monsterData._hatesClass != -1) { - if (monsterData._hatesClass == 15) - // Monster hates all classes - goto loop; + if (monsterData._hatesClass != CLASS_PALADIN) { + if (monsterData._hatesClass == HATES_PARTY) { + // Monster hates entire party, even the disabled/dead + for (uint idx = 0; idx < _combatParty.size(); ++idx) { + doMonsterTurn(monsterId, idx); + } + + // Move onto monster's next attack (if any) + continue; + } for (uint charIndex = 0; charIndex < _combatParty.size(); ++charIndex) { Character &c = *_combatParty[charIndex]; @@ -835,10 +838,8 @@ void Combat::doMonsterTurn(int monsterId) { if (cond >= PARALYZED && cond <= ERADICATED) continue; - isHated = false; switch (monsterData._hatesClass) { case CLASS_KNIGHT: - case CLASS_PALADIN: case CLASS_ARCHER: case CLASS_CLERIC: case CLASS_SORCERER: @@ -849,7 +850,7 @@ void Combat::doMonsterTurn(int monsterId) { case CLASS_RANGER: isHated = c._class == monsterData._hatesClass; break; - case 12: + case HATES_DWARF: isHated = c._race == DWARF; break; default: @@ -864,102 +865,78 @@ void Combat::doMonsterTurn(int monsterId) { } if (!isHated) { - // No particularly hated foe, so decide which character to start with - switch (_combatParty.size()) { - case 1: - charNum = 0; - break; - case 2: - case 3: - case 4: - case 5: - charNum = _vm->getRandomNumber(0, _combatParty.size() - 1); - break; - case 6: - if (_vm->getRandomNumber(1, 6) == 6) - charNum = 5; - else - charNum = _vm->getRandomNumber(0, 4); - break; - } + // No particularly hated foe, so pick a random character to start with + // Note: Original had a whole switch statement depending on party size, that boiled down to + // picking a random character in all cases anyway + charNum = _vm->getRandomNumber(0, _combatParty.size() - 1); } - // Attacking loop - do { - if (!flag) { - Condition cond = _combatParty[charNum]->worstCondition(); - - if (cond >= PARALYZED && cond <= ERADICATED) { - Common::Array<int> ableChars; - bool skip = false; - - for (uint idx = 0; idx < _combatParty.size() && !skip; ++idx) { - switch (_combatParty[idx]->worstCondition()) { - case PARALYZED: - case UNCONSCIOUS: - //if (flag) - // skip = true; - break; - case DEAD: - case STONED: - case ERADICATED: - break; - default: - ableChars.push_back(idx); - break; - } - } - - if (!skip) { - if (ableChars.size() == 0) { - party._dead = true; - _vm->_mode = MODE_1; - return; - } - - charNum = ableChars[_vm->getRandomNumber(0, ableChars.size() - 1)]; - } + // If the chosen character is already disabled, we need to pick a still able body character + // from the remainder of the combat party + Condition cond = _combatParty[charNum]->worstCondition(); + if (cond >= PARALYZED && cond <= ERADICATED) { + Common::Array<int> ableChars; + + for (uint idx = 0; idx < _combatParty.size(); ++idx) { + switch (_combatParty[idx]->worstCondition()) { + case PARALYZED: + case UNCONSCIOUS: + case DEAD: + case STONED: + case ERADICATED: + break; + default: + ableChars.push_back(idx); + break; } } - // Unconditional if to get around goto initialization errors - if (true) { - Character &c = *_combatParty[charNum]; - if (monsterData._attackType != DT_PHYSICAL || c._conditions[ASLEEP]) { - doCharDamage(c, charNum, monsterId); - } else { - int v = _vm->getRandomNumber(1, 20); - if (v == 1) { - // Critical Save - sound.playFX(6); - } else { - if (v == 20) - // Critical failure - doCharDamage(c, charNum, monsterId); - v += monsterData._hitChance / 4 + _vm->getRandomNumber(1, - monsterData._hitChance); - - int ac = c.getArmorClass() + (!_charsBlocked[charNum] ? 10 : - c.getCurrentLevel() / 2 + 15); - if (ac > v) { - sound.playFX(6); - } else { - doCharDamage(c, charNum, monsterId); - } - } - } - - if (flag) - break; + if (ableChars.size() == 0) { + party._dead = true; + _vm->_mode = MODE_INTERACTIVE; + return; } -loop: - flag = true; - } while (++charNum < (int)_combatParty.size()); + + charNum = ableChars[_vm->getRandomNumber(0, ableChars.size() - 1)]; + } + + doMonsterTurn(monsterId, charNum); } intf.drawParty(true); } +void Combat::doMonsterTurn(int monsterId, int charNum) { + Map &map = *_vm->_map; + Sound &sound = *_vm->_sound; + MonsterStruct &monsterData = map._monsterData[monsterId]; + Character &c = *_combatParty[charNum]; + + if (monsterData._attackType != DT_PHYSICAL || c._conditions[ASLEEP]) { + doCharDamage(c, charNum, monsterId); + } else { + int v = _vm->getRandomNumber(1, 20); + if (v == 1) { + // Critical Save + sound.playFX(6); + } else { + if (v == 20) + // Critical failure + doCharDamage(c, charNum, monsterId); + v += monsterData._hitChance / 4 + _vm->getRandomNumber(1, + monsterData._hitChance); + + int ac = c.getArmorClass() + (!_charsBlocked[charNum] ? 10 : + c.getCurrentLevel() / 2 + 15); + if (ac > v) { + sound.playFX(6); + } else { + doCharDamage(c, charNum, monsterId); + } + } + } +} + int Combat::stopAttack(const Common::Point &diffPt) { Map &map = *_vm->_map; Party &party = *_vm->_party; @@ -1083,7 +1060,7 @@ void Combat::setSpeedTable() { bool hasSpeed = _whosSpeed != -1; int oldSpeed = hasSpeed && _whosSpeed < (int)_speedTable.size() ? _speedTable[_whosSpeed] : 0; - // Set up speeds for party membres + // Set up speeds for party members int maxSpeed = 0; for (uint charNum = 0; charNum < _combatParty.size(); ++charNum) { Character &c = *_combatParty[charNum]; @@ -1108,7 +1085,7 @@ void Combat::setSpeedTable() { // Populate the _speedTable list with the character/monster indexes // in order of attacking speed _speedTable.clear(); - for (; maxSpeed >= 0; --maxSpeed) { + for (; maxSpeed > 0; --maxSpeed) { for (uint idx = 0; idx < charSpeeds.size(); ++idx) { if (charSpeeds[idx] == maxSpeed) _speedTable.push_back(idx); @@ -1116,8 +1093,10 @@ void Combat::setSpeedTable() { } if (hasSpeed) { - if (_speedTable[_whosSpeed] != oldSpeed) { - for (_whosSpeed = 0; _whosSpeed < (int)charSpeeds.size(); ++_whosSpeed) { + if (_speedTable.empty()) { + _whosSpeed = 0; + } else if (_whosSpeed >= (int)_speedTable.size() || _speedTable[_whosSpeed] != oldSpeed) { + for (_whosSpeed = 0; _whosSpeed < (int)_speedTable.size(); ++_whosSpeed) { if (oldSpeed == _speedTable[_whosSpeed]) break; } @@ -1350,32 +1329,34 @@ void Combat::attack(Character &c, RangeType rangeType) { for (int itemIndex = 0; itemIndex < INV_ITEMS_TOTAL; ++itemIndex) { XeenItem &weapon = c._weapons[itemIndex]; - if (weapon._frame != 0) { - switch (weapon._bonusFlags & ITEMFLAG_BONUS_MASK) { - case 1: + if (weapon.isEquipped()) { + switch (weapon._state._counter) { + case EFFECTIVE_DRAGON: if (monsterData._monsterType == MONSTER_DRAGON) damage *= 3; break; - case 2: + case EFFECTIVE_UNDEAD : if (monsterData._monsterType == MONSTER_UNDEAD) damage *= 3; break; - case 3: + case EFFECTIVE_GOLEM: if (monsterData._monsterType == MONSTER_GOLEM) damage *= 3; break; - case 4: + case EFFECTIVE_INSECT: if (monsterData._monsterType == MONSTER_INSECT) damage *= 3; break; - case 5: - if (monsterData._monsterType == MONSTER_0) + case EFFEctIVE_MONSTERS: + if (monsterData._monsterType == MONSTER_MONSTERS) damage *= 3; break; - case 6: + case EFFECTIVE_ANIMAL: if (monsterData._monsterType == MONSTER_ANIMAL) damage *= 3; break; + default: + break; } } } @@ -1387,28 +1368,31 @@ void Combat::attack(Character &c, RangeType rangeType) { } void Combat::attack2(int damage, RangeType rangeType) { + Debugger &debugger = *_vm->_debugger; Interface &intf = *_vm->_interface; Map &map = *_vm->_map; Party &party = *_vm->_party; Sound &sound = *_vm->_sound; - bool isDarkCc = _vm->_files->_isDarkCc; + int ccNum = _vm->_files->_ccNum; MazeMonster &monster = map._mobData._monsters[_monster2Attack]; MonsterStruct &monsterData = *monster._monsterData; bool monsterDied = false; - if (!isDarkCc && damage && rangeType != RT_SINGLE && monster._spriteId == 89) + if (!ccNum && damage && rangeType != RT_SINGLE && monster._spriteId == 89) damage = 0; + if (debugger._superStrength) + damage = 10000; if (!damage) { sound.playSound(_missVoc, 1); sound.playFX(6); } else { - if (!isDarkCc && monster._spriteId == 89) + if (!ccNum && monster._spriteId == 89) damage += 100; if (monster._damageType == DT_SLEEP || monster._damageType == DT_DRAGONSLEEP) monster._damageType = DT_PHYSICAL; - if ((rangeType == RT_SINGLE || _damageType == DT_PHYSICAL) && _attackWeaponId != 34) { + if ((rangeType == RT_SINGLE || _damageType == DT_PHYSICAL) && _attackWeaponId < XEEN_SLAYER_SWORD) { if (monsterData._phsyicalResistence != 0) { if (monsterData._phsyicalResistence == 100) { // Completely immune to the damage @@ -1430,7 +1414,7 @@ void Combat::attack2(int damage, RangeType rangeType) { int monsterResist = getMonsterResistence(rangeType); damage += monsterResist; if (monsterResist > 0) { - _pow[_attackDurationCtr]._elemFrame = _attackWeapon->getElementalCategory(); + _pow[_attackDurationCtr]._elemFrame = XeenItem::getElementalCategory(_weaponElemMaterial); _pow[_attackDurationCtr]._elemScale = getDamageScale(monsterResist); } else if (rangeType != RT_HIT) { _pow[_attackDurationCtr]._elemFrame = 0; @@ -1471,9 +1455,9 @@ void Combat::attack2(int damage, RangeType rangeType) { intf.draw3d(true); sound.stopSound(); - File powVoc(Common::String::format("pow%d.voc", - POW_WEAPON_VOCS[_attackWeaponId])); - sound.playFX(60 + POW_WEAPON_VOCS[_attackWeaponId]); + int powNum = (_attackWeaponId > XEEN_SLAYER_SWORD) ? 0 : POW_WEAPON_VOCS[_attackWeaponId]; + File powVoc(Common::String::format("pow%d.voc", powNum)); + sound.playFX(60 + powNum); sound.playSound(powVoc, 1); if (monster._hp > damage) { @@ -1488,12 +1472,12 @@ void Combat::attack2(int damage, RangeType rangeType) { intf.draw3d(true); if (monsterDied) { - if (!isDarkCc) { + if (!ccNum) { if (_monster2Attack == 20 && party._mazeId == 41) party._gameFlags[0][11] = true; if (_monster2Attack == 8 && party._mazeId == 78) { party._gameFlags[0][60] = true; - party._questFlags[0][23] = false; + party._questFlags[23] = false; for (uint idx = 0; idx < party._activeParty.size(); ++idx) party._activeParty[idx].setAward(42, true); @@ -1506,14 +1490,14 @@ void Combat::attack2(int damage, RangeType rangeType) { giveExperience(monsterData._experience); if (party._mazeId != 85) { - party._treasure._gold = monsterData._gold; - party._treasure._gems = monsterData._gems; + party._treasure._gold += monsterData._gold; + party._treasure._gems += monsterData._gems; - if (!isDarkCc && monster._spriteId == 89) { + if (!ccNum && monster._spriteId == 89) { // Xeen's Scepter of Temporal Distortion party._treasure._weapons[0]._id = 90; - party._treasure._weapons[0]._bonusFlags = 0; party._treasure._weapons[0]._material = 0; + party._treasure._weapons[0]._state.clear(); party._treasure._hasItems = true; party._questItems[8]++; } @@ -1527,7 +1511,7 @@ void Combat::attack2(int damage, RangeType rangeType) { switch (category) { case CATEGORY_WEAPON: for (int idx = 0; idx < MAX_TREASURE_ITEMS; ++idx) { - if (party._treasure._weapons[idx]._id == 0) { + if (party._treasure._weapons[idx].empty()) { party._treasure._weapons[idx] = tempChar._weapons[0]; party._treasure._hasItems = true; break; @@ -1536,7 +1520,7 @@ void Combat::attack2(int damage, RangeType rangeType) { break; case CATEGORY_ARMOR: for (int idx = 0; idx < MAX_TREASURE_ITEMS; ++idx) { - if (party._treasure._armor[idx]._id == 0) { + if (party._treasure._armor[idx].empty()) { party._treasure._armor[idx] = tempChar._armor[0]; party._treasure._hasItems = true; break; @@ -1545,7 +1529,7 @@ void Combat::attack2(int damage, RangeType rangeType) { break; case CATEGORY_ACCESSORY: for (int idx = 0; idx < MAX_TREASURE_ITEMS; ++idx) { - if (party._treasure._accessories[idx]._id == 0) { + if (party._treasure._accessories[idx].empty()) { party._treasure._accessories[idx] = tempChar._accessories[0]; party._treasure._hasItems = true; break; @@ -1554,7 +1538,7 @@ void Combat::attack2(int damage, RangeType rangeType) { break; case CATEGORY_MISC: for (int idx = 0; idx < MAX_TREASURE_ITEMS; ++idx) { - if (party._treasure._accessories[idx]._id == 0) { + if (party._treasure._accessories[idx].empty()) { party._treasure._accessories[idx] = tempChar._accessories[0]; party._treasure._hasItems = true; break; @@ -1592,7 +1576,7 @@ void Combat::quickFight() { break; case QUICK_SPELL: if (c->_currentSpell != -1) { - spells.castSpell(c, (MagicSpell)Res.SPELLS_ALLOWED[c->getClassCategory()][c->_currentSpell]); + spells.castSpell(c, (MagicSpell)Res.SPELLS_ALLOWED[c->getSpellsCategory()][c->_currentSpell]); } break; case QUICK_BLOCK: @@ -1676,6 +1660,7 @@ void Combat::getWeaponDamage(Character &c, RangeType rangeType) { _weaponDie = _weaponDice = 0; _weaponDamage = 0; _hitChanceBonus = 0; + _weaponElemMaterial = 0; for (int idx = 0; idx < INV_ITEMS_TOTAL; ++idx) { XeenItem &weapon = c._weapons[idx]; @@ -1687,10 +1672,12 @@ void Combat::getWeaponDamage(Character &c, RangeType rangeType) { } if (flag) { - if (!(weapon._bonusFlags & (ITEMFLAG_BROKEN | ITEMFLAG_CURSED))) { + if (!weapon.isBad()) { _attackWeapon = &weapon; - if (weapon._material >= 37 && weapon._material < 59) { + if (weapon._material < 37) { + _weaponElemMaterial = weapon._material; + } else if (weapon._material < 59) { _hitChanceBonus = Res.METAL_DAMAGE_PERCENT[weapon._material - 37]; _weaponDamage = Res.METAL_DAMAGE[weapon._material - 37]; } @@ -1761,7 +1748,7 @@ int Combat::getMonsterResistence(RangeType rangeType) { break; } } else { - int material = !_attackWeapon ? 0 : _attackWeapon->_material; + int material = _weaponElemMaterial; damage = Res.ELEMENTAL_DAMAGE[material]; if (material != 0) { @@ -1869,7 +1856,7 @@ void Combat::rangedAttack(PowType powNum) { _attackDurationCtr = -1; if (_monster2Attack != -1) { - _attackDurationCtr--; + _attackDurationCtr = attackDurationCtr - 1; if (attackMonsters.empty()) attackMonsters.resize(1); attackMonsters[0] = monster2Attack; @@ -2098,4 +2085,21 @@ void Combat::shootRangedWeapon() { rangedAttack(POW_ARROW); } +bool Combat::areMonstersPresent() const { + for (int idx = 0; idx < 26; ++idx) { + if (_attackMonsters[idx] != -1) + return true; + } + + return false; +} + +void Combat::reset() { + clearShooting(); + setupCombatParty(); + + _combatMode = COMBATMODE_INTERACTIVE; + _monster2Attack = -1; +} + } // End of namespace Xeen diff --git a/engines/xeen/combat.h b/engines/xeen/combat.h index 938845b022..e1a02c0759 100644 --- a/engines/xeen/combat.h +++ b/engines/xeen/combat.h @@ -58,7 +58,7 @@ enum ShootType { }; enum CombatMode { - COMBATMODE_STARTUP = 0, COMBATMODE_1 = 1, COMBATMODE_2 = 2 + COMBATMODE_STARTUP = 0, COMBATMODE_INTERACTIVE = 1, COMBATMODE_2 = 2 }; enum PowType { @@ -77,6 +77,7 @@ enum RangeType { class XeenEngine; class Character; class XeenItem; +class MonsterStruct; struct PowSlot { bool _active; @@ -151,7 +152,7 @@ private: public: Common::Array<Character *> _combatParty; bool _charsBlocked[PARTY_AND_MONSTERS]; - Common::Array<int> _charsGone; + int _charsGone[PARTY_AND_MONSTERS]; SpriteResource _powSprites; int _attackMonsters[ATTACK_MONSTERS_COUNT]; int _monster2Attack; @@ -176,6 +177,7 @@ public: int _monsterDamage; int _weaponDamage; int _weaponDie, _weaponDice; + int _weaponElemMaterial; XeenItem *_attackWeapon; int _attackWeaponId; File _missVoc; @@ -204,6 +206,11 @@ public: void clearShooting(); /** + * Resets all combat related data + */ + void reset(); + + /** * Gives damage to character or characters in the party */ void giveCharDamage(int damage, DamageType attackType, int charIndex); @@ -272,18 +279,30 @@ public: /** * Determines whether a given monster can move + * @param pt Monster position + * @param wallShift Shift mask for determining direction being moved + * @param xDiff X Delta for move + * @param yDiff Y Delta for move + * @param monsterId Monster number being tested */ - bool monsterCanMove(const Common::Point &pt, int wallShift, - int v1, int v2, int monsterId); + bool canMonsterMove(const Common::Point &pt, int wallShift, int xDiff, int yDiff, int monsterId); /** * Moves a monster by a given delta amount if it's a valid move */ void moveMonster(int monsterId, const Common::Point &moveDelta); + /** + * Handle a monster's turn at attacking combat party members + */ void doMonsterTurn(int monsterId); /** + * Handles a monster's turn at attacking a specific member of the combat party + */ + void doMonsterTurn(int monsterId, int charNum); + + /** * Called when combat has ended */ void endAttack(); @@ -304,6 +323,11 @@ public: * Fires off a ranged attack at all oncoming monsters */ void shootRangedWeapon(); + + /** + * Returns true if there are any monsters in the vacinity + */ + bool areMonstersPresent() const; }; } // End of namespace Xeen diff --git a/engines/xeen/configure.engine b/engines/xeen/configure.engine index 489254f269..6907bde982 100644 --- a/engines/xeen/configure.engine +++ b/engines/xeen/configure.engine @@ -1,3 +1,3 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] -add_engine xeen "World of Xeen" no +add_engine xeen "World of Xeen" yes diff --git a/engines/xeen/debugger.cpp b/engines/xeen/debugger.cpp index 42f61b02b5..7d8f170129 100644 --- a/engines/xeen/debugger.cpp +++ b/engines/xeen/debugger.cpp @@ -45,7 +45,7 @@ static int strToInt(const char *s) { /*------------------------------------------------------------------------*/ Debugger::Debugger(XeenEngine *vm) : GUI::Debugger(), _vm(vm), - _invincible(false) { + _spellId(-1), _invincible(false), _intangible(false), _superStrength(false) { registerCmd("continue", WRAP_METHOD(Debugger, cmdExit)); registerCmd("spell", WRAP_METHOD(Debugger, cmdSpell)); registerCmd("spells", WRAP_METHOD(Debugger, cmdSpells)); @@ -55,8 +55,8 @@ Debugger::Debugger(XeenEngine *vm) : GUI::Debugger(), _vm(vm), registerCmd("map", WRAP_METHOD(Debugger, cmdMap)); registerCmd("pos", WRAP_METHOD(Debugger, cmdPos)); registerCmd("invincible", WRAP_METHOD(Debugger, cmdInvincible)); - - _spellId = -1; + registerCmd("strength", WRAP_METHOD(Debugger, cmdSuperStrength)); + registerCmd("intangible", WRAP_METHOD(Debugger, cmdIntangible)); } void Debugger::update() { @@ -95,7 +95,7 @@ bool Debugger::cmdSpells(int argc, const char **argv) { for (uint charIdx = 0; charIdx < party._activeParty.size(); ++charIdx) { Character &c = party._activeParty[charIdx]; - Common::fill(c._spells, c._spells + MAX_SPELLS_PER_CLASS, true); + Common::fill(c._spells, c._spells + SPELLS_PER_CLASS, true); c._currentSp = 9999; } @@ -160,20 +160,19 @@ bool Debugger::cmdGems(int argc, const char **argv) { } bool Debugger::cmdMap(int argc, const char **argv) { - FileManager &files = *g_vm->_files; Map &map = *g_vm->_map; Party &party = *g_vm->_party; if (argc < 2) { - debugPrintf("map mapId [ sideNum [ xp, yp ]]\n"); + debugPrintf("map mapId [ xp, yp ] [ sideNum ]\n"); return true; } else { int mapId = strToInt(argv[1]); - bool side = argc < 3 ? files._isDarkCc : strToInt(argv[2]) != 0; - int x = argc < 4 ? 8 : strToInt(argv[3]); - int y = argc < 5 ? 8 : strToInt(argv[4]); + int x = argc < 3 ? 8 : strToInt(argv[2]); + int y = argc < 4 ? 8 : strToInt(argv[3]); - map._loadDarkSide = side; + if (argc == 5) + map._loadCcNum = strToInt(argv[4]); map.load(mapId); party._mazePosition.x = x; party._mazePosition.y = y; @@ -202,4 +201,16 @@ bool Debugger::cmdInvincible(int argc, const char **argv) { return true; } +bool Debugger::cmdSuperStrength(int argc, const char **argv) { + _superStrength = (argc < 2) || strcmp(argv[1], "off"); + debugPrintf("Super-powered attacks are %s\n", _superStrength ? "on" : "off"); + return true; +} + +bool Debugger::cmdIntangible(int argc, const char **argv) { + _intangible = (argc < 2) || strcmp(argv[1], "off"); + debugPrintf("Intangibility is %s\n", _intangible ? "on" : "off"); + return true; +} + } // End of namespace Xeen diff --git a/engines/xeen/debugger.h b/engines/xeen/debugger.h index b7e1f1c325..48eb8f35f6 100644 --- a/engines/xeen/debugger.h +++ b/engines/xeen/debugger.h @@ -74,8 +74,20 @@ private: * Flags whether to make the party invincible */ bool cmdInvincible(int argc, const char **argv); + + /** + * Flags whether to make the party super-strength attacks + */ + bool cmdSuperStrength(int argc, const char **argv); + + /** + * Flags whether to make the party invincible + */ + bool cmdIntangible(int argc, const char **argv); public: bool _invincible; + bool _intangible; + bool _superStrength; public: Debugger(XeenEngine *vm); diff --git a/engines/xeen/detection.cpp b/engines/xeen/detection.cpp index cf033b632f..8a5e096220 100644 --- a/engines/xeen/detection.cpp +++ b/engines/xeen/detection.cpp @@ -28,6 +28,7 @@ #include "common/savefile.h" #include "engines/advancedDetector.h" #include "common/system.h" +#include "common/translation.h" #define MAX_SAVES 99 @@ -60,6 +61,10 @@ Common::Platform XeenEngine::getPlatform() const { return _gameDescription->desc.platform; } +bool XeenEngine::getIsCD() const { + return getFeatures() & ADGF_CD; +} + } // End of namespace Xeen static const PlainGameDescriptor XeenGames[] = { @@ -71,11 +76,40 @@ static const PlainGameDescriptor XeenGames[] = { {0, 0} }; +#define GAMEOPTION_SHOW_ITEM_COSTS GUIO_GAMEOPTIONS1 +#define GAMEOPTION_DURABLE_ARMOR GUIO_GAMEOPTIONS2 + #include "xeen/detection_tables.h" + +static const ADExtraGuiOptionsMap optionsList[] = { + { + GAMEOPTION_SHOW_ITEM_COSTS, + { + _s("Show item costs in standard inventory mode"), + _s("Shows item costs in standard inventory mode, allowing the value of items to be compared"), + "ShowItemCosts", + false + } + }, + + { + GAMEOPTION_DURABLE_ARMOR, + { + _s("More durable armor"), + _s("Armor won't break until character is at -80HP, rather than merely -10HP"), + "DurableArmor", + false + } + }, + + AD_EXTRA_GUI_OPTIONS_TERMINATOR +}; + class XeenMetaEngine : public AdvancedMetaEngine { public: - XeenMetaEngine() : AdvancedMetaEngine(Xeen::gameDescriptions, sizeof(Xeen::XeenGameDescription), XeenGames) { + XeenMetaEngine() : AdvancedMetaEngine(Xeen::gameDescriptions, sizeof(Xeen::XeenGameDescription), + XeenGames, optionsList) { _maxScanDepth = 3; } @@ -137,32 +171,29 @@ SaveStateList XeenMetaEngine::listSaves(const char *target) const { Common::SaveFileManager *saveFileMan = g_system->getSavefileManager(); Common::StringArray filenames; Common::String saveDesc; - Common::String pattern = Common::String::format("%s.0??", target); + Common::String pattern = Common::String::format("%s.###", target); Xeen::XeenSavegameHeader header; filenames = saveFileMan->listSavefiles(pattern); - sort(filenames.begin(), filenames.end()); // Sort to get the files in numerical order SaveStateList saveList; for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) { const char *ext = strrchr(file->c_str(), '.'); int slot = ext ? atoi(ext + 1) : -1; - if (slot >= 0 && slot < MAX_SAVES) { + if (slot >= 0 && slot <= MAX_SAVES) { Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(*file); if (in) { - Xeen::SavesManager::readSavegameHeader(in, header); - saveList.push_back(SaveStateDescriptor(slot, header._saveName)); + if (Xeen::SavesManager::readSavegameHeader(in, header)) + saveList.push_back(SaveStateDescriptor(slot, header._saveName)); - if (header._thumbnail) - header._thumbnail->free(); - delete header._thumbnail; delete in; } } } + Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator()); return saveList; } @@ -181,7 +212,11 @@ SaveStateDescriptor XeenMetaEngine::querySaveMetaInfos(const char *target, int s if (f) { Xeen::XeenSavegameHeader header; - Xeen::SavesManager::readSavegameHeader(f, header); + if (!Xeen::SavesManager::readSavegameHeader(f, header, false)) { + delete f; + return SaveStateDescriptor(); + } + delete f; // Create the return descriptor diff --git a/engines/xeen/detection_tables.h b/engines/xeen/detection_tables.h index c311bde202..fdb9dbd668 100644 --- a/engines/xeen/detection_tables.h +++ b/engines/xeen/detection_tables.h @@ -35,8 +35,8 @@ static const XeenGameDescription gameDescriptions[] = { }, Common::EN_ANY, Common::kPlatformDOS, - ADGF_NO_FLAGS, - GUIO0() + ADGF_TESTING, + GUIO2(GAMEOPTION_SHOW_ITEM_COSTS, GAMEOPTION_DURABLE_ARMOR) }, GType_WorldOfXeen, 0 @@ -54,8 +54,8 @@ static const XeenGameDescription gameDescriptions[] = { }, Common::DE_DEU, Common::kPlatformDOS, - ADGF_NO_FLAGS, - GUIO0() + ADGF_UNSTABLE, + GUIO2(GAMEOPTION_SHOW_ITEM_COSTS, GAMEOPTION_DURABLE_ARMOR) }, GType_WorldOfXeen, 0 @@ -65,7 +65,7 @@ static const XeenGameDescription gameDescriptions[] = { // World of Xeen (2 CD talkie version) { "worldofxeen", - nullptr, + "CD", { {"xeen.cc", 0, "964078c53f649937ce9a1a3596ce3d9f", 13438429}, {"dark.cc", 0, "7f755ce39ea614fa6adb016f8bfc6e43", 11288403}, @@ -73,8 +73,8 @@ static const XeenGameDescription gameDescriptions[] = { }, Common::EN_ANY, Common::kPlatformDOS, - ADGF_NO_FLAGS, - GUIO0() + ADGF_TESTING | ADGF_CD, + GUIO2(GAMEOPTION_SHOW_ITEM_COSTS, GAMEOPTION_DURABLE_ARMOR) }, GType_WorldOfXeen, 0 @@ -91,8 +91,8 @@ static const XeenGameDescription gameDescriptions[] = { }, Common::EN_ANY, Common::kPlatformDOS, - ADGF_NO_FLAGS, - GUIO0() + ADGF_TESTING, + GUIO2(GAMEOPTION_SHOW_ITEM_COSTS, GAMEOPTION_DURABLE_ARMOR) }, GType_Clouds, 0 @@ -109,15 +109,15 @@ static const XeenGameDescription gameDescriptions[] = { }, Common::EN_ANY, Common::kPlatformDOS, - ADGF_NO_FLAGS, - GUIO0() + ADGF_TESTING, + GUIO2(GAMEOPTION_SHOW_ITEM_COSTS, GAMEOPTION_DURABLE_ARMOR) }, GType_DarkSide, 0 }, { - // Swords of Xeen (GOG) + // Swords of Xeen { "swordsofxeen", nullptr, @@ -127,8 +127,8 @@ static const XeenGameDescription gameDescriptions[] = { }, Common::EN_ANY, Common::kPlatformDOS, - ADGF_NO_FLAGS, - GUIO0() + ADGF_TESTING, + GUIO2(GAMEOPTION_SHOW_ITEM_COSTS, GAMEOPTION_DURABLE_ARMOR) }, GType_Swords, 0 diff --git a/engines/xeen/dialogs/credits_screen.cpp b/engines/xeen/dialogs/credits_screen.cpp index 09547ba282..d0269ad716 100644 --- a/engines/xeen/dialogs/credits_screen.cpp +++ b/engines/xeen/dialogs/credits_screen.cpp @@ -28,7 +28,7 @@ namespace Xeen { void CreditsScreen::show(XeenEngine *vm) { CreditsScreen *dlg = new CreditsScreen(vm); - + switch (vm->getGameID()) { case GType_Clouds: dlg->execute(Res.CLOUDS_CREDITS); @@ -41,7 +41,7 @@ void CreditsScreen::show(XeenEngine *vm) { dlg->execute(Res.DARK_SIDE_CREDITS); break; } - + delete dlg; } diff --git a/engines/xeen/dialogs/dialogs.cpp b/engines/xeen/dialogs/dialogs.cpp index d7a696b8d5..d5c732e51a 100644 --- a/engines/xeen/dialogs/dialogs.cpp +++ b/engines/xeen/dialogs/dialogs.cpp @@ -66,48 +66,54 @@ bool ButtonContainer::checkEvents(XeenEngine *vm) { EventsManager &events = *vm->_events; Party &party = *vm->_party; Windows &windows = *_vm->_windows; + PendingEvent event; _buttonValue = 0; - if (events._leftButton) { - Common::Point pt = events._mousePos; - - // Check for party member glyphs being clicked - Common::Rect r(0, 0, 32, 32); - for (uint idx = 0; idx < party._activeParty.size(); ++idx) { - r.moveTo(Res.CHAR_FACES_X[idx], 150); - if (r.contains(pt)) { - _buttonValue = Common::KEYCODE_F1 + idx; - break; + if (events.getEvent(event)) { + if (event._leftButton) { + Common::Point pt = events._mousePos; + + // Check for party member glyphs being clicked + Common::Rect r(0, 0, 32, 32); + for (uint idx = 0; idx < party._activeParty.size(); ++idx) { + r.moveTo(Res.CHAR_FACES_X[idx], 150); + if (r.contains(pt)) { + _buttonValue = Common::KEYCODE_F1 + idx; + break; + } } - } - // Check whether any button is selected - for (uint i = 0; i < _buttons.size(); ++i) { - if (_buttons[i]._bounds.contains(pt)) { - events.debounceMouse(); + // Check whether any button is selected + for (uint i = 0; i < _buttons.size(); ++i) { + if (_buttons[i]._bounds.contains(pt)) { + events.debounceMouse(); - _buttonValue = _buttons[i]._value; - break; + _buttonValue = _buttons[i]._value; + break; + } + } + + if (!_buttonValue && _waitBounds.contains(pt)) { + _buttonValue = Common::KEYCODE_SPACE; + return true; } - } - if (!_buttonValue && Common::Rect(8, 8, 224, 135).contains(pt)) { - _buttonValue = 1; - return true; + } else if (event.isKeyboard()) { + const Common::KeyCode &keycode = event._keyState.keycode; + + if (keycode == Common::KEYCODE_KP8) + _buttonValue = Common::KEYCODE_UP; + else if (keycode == Common::KEYCODE_KP2) + _buttonValue = Common::KEYCODE_DOWN; + else if (keycode == Common::KEYCODE_KP_ENTER) + _buttonValue = Common::KEYCODE_RETURN; + else if (keycode != Common::KEYCODE_LCTRL && keycode != Common::KEYCODE_RCTRL + && keycode != Common::KEYCODE_LALT && keycode != Common::KEYCODE_RALT) + _buttonValue = keycode; + + if (_buttonValue) + _buttonValue |= (event._keyState.flags & ~Common::KBD_STICKY) << 16; } - } else if (events.isKeyPending()) { - Common::KeyState keyState; - events.getKey(keyState); - - _buttonValue = keyState.keycode; - if (_buttonValue == Common::KEYCODE_KP8) - _buttonValue = Common::KEYCODE_UP; - else if (_buttonValue == Common::KEYCODE_KP2) - _buttonValue = Common::KEYCODE_DOWN; - else if (_buttonValue == Common::KEYCODE_KP_ENTER) - _buttonValue = Common::KEYCODE_RETURN; - - _buttonValue |= (keyState.flags & ~Common::KBD_STICKY) << 16; } if (_buttonValue) { @@ -118,8 +124,7 @@ bool ButtonContainer::checkEvents(XeenEngine *vm) { if (btn._draw && btn._value == _buttonValue) { // Found the correct button // Draw button depressed - btn._sprites->draw(0, btn._frameNum + 1, - Common::Point(btn._bounds.left, btn._bounds.top)); + btn._sprites->draw(0, btn._selectedFrame, Common::Point(btn._bounds.left, btn._bounds.top)); win.setBounds(btn._bounds); win.update(); @@ -128,8 +133,7 @@ bool ButtonContainer::checkEvents(XeenEngine *vm) { events.wait(2); // Redraw button in it's original non-depressed form - btn._sprites->draw(0, btn._frameNum, - Common::Point(btn._bounds.left, btn._bounds.top)); + btn._sprites->draw(0, btn._frameNum, Common::Point(btn._bounds.left, btn._bounds.top)); win.setBounds(btn._bounds); win.update(); break; @@ -146,6 +150,7 @@ void ButtonContainer::drawButtons(XSurface *surface) { for (uint btnIndex = 0; btnIndex < _buttons.size(); ++btnIndex) { UIButton &btn = _buttons[btnIndex]; if (btn._draw) { + assert(btn._sprites); btn._sprites->draw(*surface, btn._frameNum, Common::Point(btn._bounds.left, btn._bounds.top)); } @@ -153,7 +158,7 @@ void ButtonContainer::drawButtons(XSurface *surface) { } bool ButtonContainer::doScroll(bool rollUp, bool fadeIn) { - if (_vm->_files->_isDarkCc) { + if (_vm->_files->_ccNum) { return Cutscenes::doScroll(rollUp, fadeIn); } else { saveButtons(); diff --git a/engines/xeen/dialogs/dialogs.h b/engines/xeen/dialogs/dialogs.h index 9038f75ef8..e97e46c5d3 100644 --- a/engines/xeen/dialogs/dialogs.h +++ b/engines/xeen/dialogs/dialogs.h @@ -39,14 +39,36 @@ public: Common::Rect _bounds; SpriteResource *_sprites; int _value; - uint _frameNum; + uint _frameNum, _selectedFrame; bool _draw; + /** + * Constructor + */ UIButton(const Common::Rect &bounds, int value, uint frameNum, SpriteResource *sprites, bool draw) : - _bounds(bounds), _value(value), _frameNum(frameNum), + _bounds(bounds), _value(value), _frameNum(frameNum), _selectedFrame(frameNum | 1), _sprites(sprites), _draw(draw) {} - UIButton() : _value(0), _frameNum(0), _sprites(nullptr), _draw(false) {} + /** + * Constructor + */ + UIButton() : _value(0), _frameNum(0), _selectedFrame(0), _sprites(nullptr), _draw(false) {} + + /** + * Set the frame + */ + void setFrame(uint frameNum) { + _frameNum = frameNum; + _selectedFrame = frameNum | 1; + } + + /** + * Set the frame + */ + void setFrame(uint frameNum, uint selectedFrame) { + _frameNum = frameNum; + _selectedFrame = selectedFrame; + } }; class ButtonContainer : public Cutscenes { @@ -55,6 +77,7 @@ private: protected: Common::Array<UIButton> _buttons; Common::StringArray _textStrings; + Common::Rect _waitBounds; int _buttonValue; bool checkEvents(XeenEngine *vm); @@ -102,6 +125,11 @@ public: * Draws the buttons onto the passed surface */ void drawButtons(XSurface *surface); + + /** + * Clears any currently set button value + */ + void clearEvents() { _buttonValue = 0; } }; class SettingsBaseDialog : public ButtonContainer { diff --git a/engines/xeen/dialogs/dialogs_char_info.cpp b/engines/xeen/dialogs/dialogs_char_info.cpp index 0ae64ed608..aec8be5ee4 100644 --- a/engines/xeen/dialogs/dialogs_char_info.cpp +++ b/engines/xeen/dialogs/dialogs_char_info.cpp @@ -142,7 +142,7 @@ void CharacterInfo::execute(int charIndex) { case Common::KEYCODE_RETURN: case Common::KEYCODE_KP_ENTER: _buttonValue = _cursorCell + Common::KEYCODE_a; - // Deliberate fall-through + // fall through case 1001: case 1002: @@ -198,6 +198,7 @@ void CharacterInfo::execute(int charIndex) { } _vm->_mode = MODE_CHARACTER_INFO; + redrawFlag = true; break; case Common::KEYCODE_q: @@ -395,14 +396,18 @@ bool CharacterInfo::expandStat(int attrib, const Character &c) { bounds.setHeight(42); break; - case 10: + case 10: { // Hit Points - stat1 = c._currentHp; - stat2 = c.getMaxHP(); - msg = Common::String::format(Res.CURRENT_MAXIMUM_TEXT, Res.STAT_NAMES[attrib], - stat1, stat2); + Common::String fmt(Res.CURRENT_MAXIMUM_TEXT); + const char *p; + while ((p = strstr(fmt.c_str(), "%u")) != nullptr) + fmt.setChar('d', p - fmt.c_str() + 1); + + msg = Common::String::format(fmt.c_str(), Res.STAT_NAMES[attrib], + c._currentHp, c.getMaxHP()); bounds.setHeight(42); break; + } case 11: // Spell Points @@ -521,14 +526,22 @@ bool CharacterInfo::expandStat(int attrib, const Character &c) { ++total; } - if (party._blessed) + if (party._blessed) { lines[16] = Common::String::format(Res.BLESSED, party._blessed); - if (party._powerShield) + ++total; + } + if (party._powerShield) { lines[17] = Common::String::format(Res.POWER_SHIELD, party._powerShield); - if (party._holyBonus) + ++total; + } + if (party._holyBonus) { lines[18] = Common::String::format(Res.HOLY_BONUS, party._holyBonus); - if (party._heroism) + ++total; + } + if (party._heroism) { lines[19] = Common::String::format(Res.HEROISM, party._heroism); + ++total; + } msg = Common::String::format("\x2\x3""c%s\x3l%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\x1", Res.CONSUMABLE_NAMES[3], lines[0].c_str(), lines[1].c_str(), diff --git a/engines/xeen/dialogs/dialogs_control_panel.cpp b/engines/xeen/dialogs/dialogs_control_panel.cpp index 177c7785f8..80d1715ff7 100644 --- a/engines/xeen/dialogs/dialogs_control_panel.cpp +++ b/engines/xeen/dialogs/dialogs_control_panel.cpp @@ -61,9 +61,10 @@ int ControlPanel::execute() { w.writeString("\xB""000\t000\x1"); w.update(); + events.updateGameCounter(); + intf.draw3d(false, false); + do { - events.updateGameCounter(); - intf.draw3d(false, false); w.writeString("\r"); drawButtons(&w); w.writeString(text); @@ -80,7 +81,7 @@ int ControlPanel::execute() { checkEvents(_vm); if (_vm->shouldExit()) return 0; - } while (!_buttonValue && !events.timeElapsed()); + } while (!_buttonValue && events.timeElapsed() < 2); switch (_buttonValue) { case Common::KEYCODE_q: @@ -97,11 +98,11 @@ int ControlPanel::execute() { sound.playFX(51); if (g_vm->getGameID() == GType_WorldOfXeen) { - map._loadDarkSide = false; + map._loadCcNum = 0; map.load(28); party._mazeDirection = DIR_EAST; } else { - map._loadDarkSide = true; + map._loadCcNum = 1; map.load(29); party._mazeDirection = DIR_SOUTH; } @@ -169,7 +170,8 @@ int ControlPanel::execute() { intf.drawParty(true); if (result == 3) { - saves.loadGame(); + if (g_vm->canLoadGameStateCurrently()) + saves.loadGame(); } else if (result == 4) { saves.saveGame(); } diff --git a/engines/xeen/dialogs/dialogs_copy_protection.cpp b/engines/xeen/dialogs/dialogs_copy_protection.cpp new file mode 100644 index 0000000000..7788cb5a22 --- /dev/null +++ b/engines/xeen/dialogs/dialogs_copy_protection.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 "xeen/dialogs/dialogs_copy_protection.h" +#include "xeen/dialogs/dialogs_input.h" +#include "xeen/resources.h" +#include "xeen/xeen.h" + +namespace Xeen { + +bool CopyProtection::show(XeenEngine *vm) { + CopyProtection *dlg = new CopyProtection(vm); + int result = dlg->execute(); + delete dlg; + + return result; +} + +CopyProtection::CopyProtection(XeenEngine *vm) : Input(vm, &(*vm->_windows)[11]) { + loadEntries(); +} + +bool CopyProtection::execute() { + EventsManager &events = *_vm->_events; + Sound &sound = *_vm->_sound; + Window &w = *_window; + bool result = false; + Common::String line; + + // Choose a random entry + ProtectionEntry &protEntry = _entries[_vm->getRandomNumber(_entries.size() - 1)]; + Common::String msg = Common::String::format(Res.WHATS_THE_PASSWORD, + protEntry._pageNum, protEntry._lineNum, protEntry._wordNum); + + w.open(); + w.writeString(msg); + w.update(); + + for (int tryNum = 0; tryNum < 3 && !_vm->shouldExit(); ++tryNum) { + line.clear(); + if (getString(line, 20, 200, false) && !line.compareToIgnoreCase(protEntry._text)) { + sound.playFX(20); + result = true; + break; + } + + sound.playFX(21); + w.writeString("\x3l\v040\n\x4""200"); + w.writeString(Res.PASSWORD_INCORRECT); + w.update(); + + events.updateGameCounter(); + events.wait(50, false); + } + + w.close(); + return result; +} + +void CopyProtection::loadEntries() { + FileManager &files = *g_vm->_files; + File f(files._ccNum ? "timer.drv" : "cpstruct"); + ProtectionEntry pe; + byte seed = 0; + char text[13]; + + while (f.pos() < f.size()) { + pe._pageNum = f.readByte() ^ seed++; + pe._lineNum = f.readByte() ^ seed++; + pe._wordNum = f.readByte() ^ seed++; + + for (int idx = 0; idx < 13; ++idx) + text[idx] = f.readByte() ^ seed++; + text[12] = '\0'; + pe._text = Common::String(text); + + _entries.push_back(pe); + } +} + +} // End of namespace Xeen diff --git a/engines/xeen/dialogs/dialogs_copy_protection.h b/engines/xeen/dialogs/dialogs_copy_protection.h new file mode 100644 index 0000000000..97c8a6a53e --- /dev/null +++ b/engines/xeen/dialogs/dialogs_copy_protection.h @@ -0,0 +1,64 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef XEEN_DIALOGS_COPY_PROTECTION_H +#define XEEN_DIALOGS_COPY_PROTECTION_H + +#include "xeen/dialogs/dialogs_input.h" +#include "common/array.h" + +namespace Xeen { + +class CopyProtection : public Input { + struct ProtectionEntry { + uint8 _pageNum; + uint8 _lineNum; + uint8 _wordNum; + Common::String _text; + }; +private: + Common::Array<ProtectionEntry> _entries; +private: + /** + * Constructor + */ + CopyProtection(XeenEngine *vm); + + /** + * Execute the dialog + */ + bool execute(); + + /** + * Load the copy protection entries + */ + void loadEntries(); +public: + /** + * Show the dialog + */ + static bool show(XeenEngine *vm); +}; + +} // End of namespace Xeen + +#endif /* XEEN_DIALOGS_COPY_PROTECTION_H */ diff --git a/engines/xeen/dialogs/dialogs_create_char.cpp b/engines/xeen/dialogs/dialogs_create_char.cpp index 577ae5314b..ab86c4939a 100644 --- a/engines/xeen/dialogs/dialogs_create_char.cpp +++ b/engines/xeen/dialogs/dialogs_create_char.cpp @@ -389,7 +389,8 @@ int CreateCharacterDialog::newCharDetails(Race race, Sex sex, int classId, // Set up default skill for the race, if any if (Res.NEW_CHAR_RACE_SKILLS[race] != -1) { - raceSkillStr = Res.SKILL_NAMES[Res.NEW_CHAR_RACE_SKILLS[race]]; + const char *skillP = Res.SKILL_NAMES[Res.NEW_CHAR_RACE_SKILLS[race]]; + raceSkillStr = Common::String(skillP + Res.NEW_CHAR_SKILLS_OFFSET[race]); } // Set up color to use for each skill string to be displayed, based @@ -554,7 +555,7 @@ int CreateCharacterDialog::exchangeAttribute(int srcAttr) { break; int destAttr = getAttribFromKeycode(_buttonValue); - + if (destAttr != -1 && srcAttr != destAttr) { result = destAttr; break; @@ -579,7 +580,7 @@ bool CreateCharacterDialog::saveCharacter(Character &c, int classId, Race race, Window &w = windows[6]; Common::String name; int result; - bool isDarkCc = _vm->_files->_isDarkCc; + int ccNum = _vm->_files->_ccNum; // Prompt for a character name w.open(); @@ -597,11 +598,11 @@ bool CreateCharacterDialog::saveCharacter(Character &c, int classId, Race race, c.clear(); c._name = name; c._savedMazeId = party._priorMazeId; - c._xeenSide = map._loadDarkSide; + c._xeenSide = map._loadCcNum; c._sex = sex; c._race = race; c._class = (CharacterClass)classId; - c._level._permanent = isDarkCc ? 5 : 1; + c._level._permanent = ccNum ? 5 : 1; c._might._permanent = _attribs[MIGHT]; c._intellect._permanent = _attribs[INTELLECT]; diff --git a/engines/xeen/dialogs/dialogs_dismiss.cpp b/engines/xeen/dialogs/dialogs_dismiss.cpp index 716f8f0035..7f9c8d972f 100644 --- a/engines/xeen/dialogs/dialogs_dismiss.cpp +++ b/engines/xeen/dialogs/dialogs_dismiss.cpp @@ -70,6 +70,10 @@ void Dismiss::execute() { w.close(); ErrorScroll::show(_vm, Res.CANT_DISMISS_LAST_CHAR, WT_NONFREEZED_WAIT); w.open(); + } else if (party._activeParty[_buttonValue]._weapons.hasElderWeapon()) { + w.close(); + ErrorScroll::show(_vm, Res.DELETE_CHAR_WITH_ELDER_WEAPON, WT_NONFREEZED_WAIT); + w.open(); } else { // Remove the character from the party party._activeParty.remove_at(_buttonValue); diff --git a/engines/xeen/dialogs/dialogs_input.cpp b/engines/xeen/dialogs/dialogs_input.cpp index 1d05c81f32..b72e47e37a 100644 --- a/engines/xeen/dialogs/dialogs_input.cpp +++ b/engines/xeen/dialogs/dialogs_input.cpp @@ -84,9 +84,9 @@ Common::KeyState Input::waitForKey(const Common::String &msg) { intf._tillMove = 0; bool flag = !_vm->_startupWindowActive && !windows[25]._enabled - && _vm->_mode != MODE_FF && _vm->_mode != MODE_17; + && _vm->_mode != MODE_FF && _vm->_mode != MODE_INTERACTIVE7; - Common::KeyState ks; + PendingEvent pe; while (!_vm->shouldExit()) { events.updateGameCounter(); @@ -100,11 +100,8 @@ Common::KeyState Input::waitForKey(const Common::String &msg) { windows[3].update(); events.wait(1); - - if (events.isKeyPending()) { - events.getKey(ks); + if (events.getEvent(pe) && pe.isKeyboard()) break; - } } _window->writeString(""); @@ -113,7 +110,7 @@ Common::KeyState Input::waitForKey(const Common::String &msg) { intf._tillMove = oldTillMove; intf._upDoorText = oldUpDoorText; - return ks; + return pe._keyState; } void Input::animateCursor() { @@ -144,7 +141,6 @@ int StringInput::show(XeenEngine *vm, bool type, const Common::String &msg1, int StringInput::execute(bool type, const Common::String &expected, const Common::String &title, int opcode) { FileManager &files = *_vm->_files; - Interface &intf = *_vm->_interface; Scripts &scripts = *_vm->_scripts; Windows &windows = *_vm->_windows; Window &w = windows[6]; @@ -158,9 +154,9 @@ int StringInput::execute(bool type, const Common::String &expected, Common::String line; if (getString(line, 30, 200, false)) { if (type) { - if (line == intf._interfaceText) { + if (!line.compareToIgnoreCase(scripts._message)) { result = true; - } else if (line == expected) { + } else if (!line.compareToIgnoreCase(expected)) { result = (opcode == 55) ? -1 : 1; } } else { @@ -168,14 +164,14 @@ int StringInput::execute(bool type, const Common::String &expected, MirrorEntry me; scripts._mirror.clear(); - File f(Common::String::format("%smirr.txt", files._isDarkCc ? "dark" : "xeen"), 1); + File f(Common::String::format("%smirr.txt", files._ccNum ? "dark" : "xeen"), 1); while (me.synchronize(f)) scripts._mirror.push_back(me); f.close(); // Load in any extended mirror entries Common::File f2; - if (f2.open(Common::String::format("%smirr.ext", files._isDarkCc ? "dark" : "xeen"))) { + if (f2.open(Common::String::format("%smirr.ext", files._ccNum ? "dark" : "xeen"))) { while (me.synchronize(f2)) scripts._mirror.push_back(me); f2.close(); @@ -184,7 +180,7 @@ int StringInput::execute(bool type, const Common::String &expected, for (uint idx = 0; idx < scripts._mirror.size(); ++idx) { if (!line.compareToIgnoreCase(scripts._mirror[idx]._name)) { result = idx + 1; - sound.playFX(_vm->_files->_isDarkCc ? 35 : 61); + sound.playFX(_vm->_files->_ccNum ? 35 : 61); break; } } @@ -219,8 +215,8 @@ int NumericInput::execute(int maxLength, int maxWidth) { /*------------------------------------------------------------------------*/ -int Choose123::show(XeenEngine *vm, int numOptions) { - assert(numOptions <= 3); +int Choose123::show(XeenEngine *vm, uint numOptions) { + assert(numOptions <= 9); Choose123 *dlg = new Choose123(vm); int result = dlg->execute(numOptions); delete dlg; @@ -228,7 +224,7 @@ int Choose123::show(XeenEngine *vm, int numOptions) { return result; } -int Choose123::execute(int numOptions) { +int Choose123::execute(uint numOptions) { EventsManager &events = *_vm->_events; Interface &intf = *_vm->_interface; LocationManager &loc = *_vm->_locations; @@ -256,24 +252,17 @@ int Choose123::execute(int numOptions) { } events.wait(delay); + checkEvents(_vm); + if (_vm->shouldExit()) return 0; } while (!_buttonValue); - switch (_buttonValue) { - case Common::KEYCODE_ESCAPE: + if (_buttonValue == Common::KEYCODE_ESCAPE) { result = 0; - break; - case Common::KEYCODE_1: - case Common::KEYCODE_2: - case Common::KEYCODE_3: { - int v = _buttonValue - Common::KEYCODE_1 + 1; - if (v <= numOptions) - result = v; - break; - } - default: - break; + } else if (_buttonValue >= Common::KEYCODE_1 && _buttonValue < (Common::KEYCODE_1 + (int)numOptions)) { + _buttonValue -= Common::KEYCODE_0; + result = (_buttonValue == (int)numOptions) ? 0 : _buttonValue; } } @@ -283,15 +272,17 @@ int Choose123::execute(int numOptions) { return result; } -void Choose123::loadButtons(int numOptions) { +void Choose123::loadButtons(uint numOptions) { + assert(numOptions > 0 && numOptions <= 9); _iconSprites.load("choose.icn"); + const int XPOS[3] = { 235, 260, 286 }; + const int YPOS[3] = { 75, 96, 117 }; - if (numOptions >= 1) - addButton(Common::Rect(235, 75, 259, 95), Common::KEYCODE_1, &_iconSprites); - if (numOptions >= 2) - addButton(Common::Rect(260, 75, 284, 95), Common::KEYCODE_2, &_iconSprites); - if (numOptions >= 3) - addButton(Common::Rect(286, 75, 311, 95), Common::KEYCODE_3, &_iconSprites); + for (uint idx = 0; idx < numOptions; ++idx) { + Common::Rect r(24, 20); + r.moveTo(XPOS[idx % 3], YPOS[idx / 3]); + addButton(r, Common::KEYCODE_1 + idx, &_iconSprites); + } } /*------------------------------------------------------------------------*/ diff --git a/engines/xeen/dialogs/dialogs_input.h b/engines/xeen/dialogs/dialogs_input.h index 270495ffd5..8cb41f3d36 100644 --- a/engines/xeen/dialogs/dialogs_input.h +++ b/engines/xeen/dialogs/dialogs_input.h @@ -82,11 +82,11 @@ private: Choose123(XeenEngine *vm) : ButtonContainer(vm) {} - int execute(int numOptions); + int execute(uint numOptions); - void loadButtons(int numOptions); + void loadButtons(uint numOptions); public: - static int show(XeenEngine *vm, int numOptions); + static int show(XeenEngine *vm, uint numOptions); }; class HowMuch : public ButtonContainer { diff --git a/engines/xeen/dialogs/dialogs_items.cpp b/engines/xeen/dialogs/dialogs_items.cpp index 24d4603617..b67e5a4aac 100644 --- a/engines/xeen/dialogs/dialogs_items.cpp +++ b/engines/xeen/dialogs/dialogs_items.cpp @@ -43,43 +43,35 @@ Character *ItemsDialog::execute(Character *c, ItemsMode mode) { Party &party = *_vm->_party; Windows &windows = *_vm->_windows; + ItemsMode priorMode = ITEMMODE_INVALID; Character *startingChar = c; ItemCategory category = mode == ITEMMODE_RECHARGE || mode == ITEMMODE_COMBAT ? CATEGORY_MISC : CATEGORY_WEAPON; int varA = mode == ITEMMODE_COMBAT ? 1 : 0; if (varA != 0) mode = ITEMMODE_CHAR_INFO; - bool updateStock = mode == ITEMMODE_BLACKSMITH; + bool updateStock = mode == ITEMMODE_BUY; int itemIndex = -1; Common::StringArray lines; uint arr[40]; int actionIndex = -1; - events.setCursor(0); - loadButtons(mode, c); + if (mode == ITEMMODE_BUY) { + _oldCharacter = c; + c = &_itemsCharacter; + party._blacksmithWares.blackData2CharData(_itemsCharacter); + setEquipmentIcons(); + } else if (mode == ITEMMODE_ENCHANT) { + _oldCharacter = c; + } + events.setCursor(0); windows[29].open(); windows[30].open(); enum { REDRAW_NONE, REDRAW_TEXT, REDRAW_FULL } redrawFlag = REDRAW_FULL; for (;;) { if (redrawFlag == REDRAW_FULL) { - if ((mode != ITEMMODE_CHAR_INFO || category != CATEGORY_MISC) && mode != ITEMMODE_ENCHANT - && mode != ITEMMODE_RECHARGE && mode != ITEMMODE_TO_GOLD) { - _buttons[4]._bounds.moveTo(148, _buttons[4]._bounds.top); - _buttons[9]._draw = false; - } else if (mode == ITEMMODE_RECHARGE) { - _buttons[4]._value = Common::KEYCODE_r; - } else if (mode == ITEMMODE_ENCHANT) { - _buttons[4]._value = Common::KEYCODE_e; - } else if (mode == ITEMMODE_TO_GOLD) { - _buttons[4]._value = Common::KEYCODE_g; - } else { - _buttons[4]._bounds.moveTo(0, _buttons[4]._bounds.top); - _buttons[9]._draw = true; - _buttons[9]._value = Common::KEYCODE_u; - } - // Write text for the dialog Common::String msg; if (mode != ITEMMODE_CHAR_INFO && mode != ITEMMODE_8 && mode != ITEMMODE_ENCHANT @@ -99,41 +91,22 @@ Character *ItemsDialog::execute(Character *c, ItemsMode mode) { } windows[29].writeString(msg); - drawButtons(&windows[0]); Common::fill(&arr[0], &arr[40], 0); itemIndex = -1; + priorMode = ITEMMODE_INVALID; + } + + if (mode != priorMode) { + // Set up the buttons for the dialog + loadButtons(mode, c, category); + priorMode = mode; + drawButtons(&windows[0]); } if (redrawFlag == REDRAW_TEXT || redrawFlag == REDRAW_FULL) { lines.clear(); - if (mode == ITEMMODE_CHAR_INFO || category != 3) { - _iconSprites.draw(0, 8, Common::Point(148, 109)); - } - if (mode != ITEMMODE_ENCHANT && mode != ITEMMODE_RECHARGE && mode != ITEMMODE_TO_GOLD) { - _iconSprites.draw(0, 10, Common::Point(182, 109)); - _iconSprites.draw(0, 12, Common::Point(216, 109)); - _iconSprites.draw(0, 14, Common::Point(250, 109)); - } - - switch (mode) { - case ITEMMODE_CHAR_INFO: - _iconSprites.draw(0, 9, Common::Point(148, 109)); - break; - case ITEMMODE_BLACKSMITH: - _iconSprites.draw(0, 11, Common::Point(182, 109)); - break; - case ITEMMODE_REPAIR: - _iconSprites.draw(0, 15, Common::Point(250, 109)); - break; - case ITEMMODE_IDENTIFY: - _iconSprites.draw(0, 13, Common::Point(216, 109)); - break; - default: - break; - } - for (int idx = 0; idx < INV_ITEMS_TOTAL; ++idx) { DrawStruct &ds = _itemsDrawList[idx]; XeenItem &i = c->_items[category][idx]; @@ -147,8 +120,8 @@ Character *ItemsDialog::execute(Character *c, ItemsMode mode) { case CATEGORY_ARMOR: case CATEGORY_ACCESSORY: if (i._id) { - if (mode == ITEMMODE_CHAR_INFO || mode == ITEMMODE_8 - || mode == ITEMMODE_ENCHANT || mode == ITEMMODE_RECHARGE) { + if ((mode == ITEMMODE_CHAR_INFO && !g_vm->_extOptions._showItemCosts) + || mode == ITEMMODE_8 || mode == ITEMMODE_ENCHANT || mode == ITEMMODE_RECHARGE) { lines.push_back(Common::String::format(Res.ITEMS_DIALOG_LINE1, arr[idx], idx + 1, c->_items[category].getFullDescription(idx, arr[idx]).c_str())); @@ -156,7 +129,8 @@ Character *ItemsDialog::execute(Character *c, ItemsMode mode) { lines.push_back(Common::String::format(Res.ITEMS_DIALOG_LINE2, arr[idx], idx + 1, c->_items[category].getFullDescription(idx, arr[idx]).c_str(), - calcItemCost(c, idx, mode, + calcItemCost(c, idx, + (mode == ITEMMODE_CHAR_INFO) ? ITEMMODE_BUY : mode, mode == ITEMMODE_TO_GOLD ? 1 : startingChar->_skills[MERCHANT], category) )); @@ -209,7 +183,7 @@ Character *ItemsDialog::execute(Character *c, ItemsMode mode) { case ITEMMODE_CHAR_INFO: case ITEMMODE_8: windows[30].writeString(Common::String::format(Res.X_FOR_THE_Y, - category == CATEGORY_MISC ? "\x3l" : "\x3c", + category == CATEGORY_MISC ? "\x3l" : "\x3""c", Res.CATEGORY_NAMES[category], c->_name.c_str(), Res.CLASS_NAMES[c->_class], category == CATEGORY_MISC ? Res.FMT_CHARGES : " ", lines[0].c_str(), lines[1].c_str(), lines[2].c_str(), lines[3].c_str(), @@ -218,7 +192,7 @@ Character *ItemsDialog::execute(Character *c, ItemsMode mode) { )); break; - case ITEMMODE_BLACKSMITH: + case ITEMMODE_BUY: windows[30].writeString(Common::String::format(Res.AVAILABLE_GOLD_COST, Res.CATEGORY_NAMES[category], party._gold, lines[0].c_str(), lines[1].c_str(), lines[2].c_str(), lines[3].c_str(), @@ -227,7 +201,7 @@ Character *ItemsDialog::execute(Character *c, ItemsMode mode) { )); break; - case ITEMMODE_2: + case ITEMMODE_SELL: case ITEMMODE_RECHARGE: case ITEMMODE_ENCHANT: case ITEMMODE_REPAIR: @@ -265,10 +239,10 @@ Character *ItemsDialog::execute(Character *c, ItemsMode mode) { if (itemIndex != -1) { switch (mode) { - case ITEMMODE_BLACKSMITH: + case ITEMMODE_BUY: actionIndex = 0; break; - case ITEMMODE_2: + case ITEMMODE_SELL: actionIndex = 1; break; case ITEMMODE_REPAIR: @@ -336,36 +310,38 @@ Character *ItemsDialog::execute(Character *c, ItemsMode mode) { if (_buttonValue < (int)(_vm->_mode == MODE_COMBAT ? combat._combatParty.size() : party._activeParty.size())) { // Character number is valid - redrawFlag = REDRAW_TEXT; + redrawFlag = REDRAW_FULL; Character *newChar = _vm->_mode == MODE_COMBAT ? combat._combatParty[_buttonValue] : &party._activeParty[_buttonValue]; - if (mode == ITEMMODE_BLACKSMITH) { + if (mode == ITEMMODE_BUY) { _oldCharacter = newChar; startingChar = newChar; c = &_itemsCharacter; - } else if (mode == ITEMMODE_2 || mode == ITEMMODE_REPAIR || mode == ITEMMODE_IDENTIFY) { + } else if (mode == ITEMMODE_SELL || mode == ITEMMODE_REPAIR || mode == ITEMMODE_IDENTIFY) { _oldCharacter = newChar; startingChar = newChar; c = newChar; } else if (itemIndex != -1) { + // Switching item to another character InventoryItems &destItems = newChar->_items[category]; - XeenItem &destItem = destItems[INV_ITEMS_TOTAL - 1]; InventoryItems &srcItems = c->_items[category]; XeenItem &srcItem = srcItems[itemIndex]; - if (srcItem._bonusFlags & ITEMFLAG_CURSED) + if (srcItem._state._cursed) ErrorScroll::show(_vm, Res.CANNOT_REMOVE_CURSED_ITEM); - else if (destItems[INV_ITEMS_TOTAL - 1]._id) + else if (destItems.isFull()) ErrorScroll::show(_vm, Common::String::format( Res.CATEGORY_BACKPACK_IS_FULL[category], c->_name.c_str())); else { + XeenItem &destItem = destItems[INV_ITEMS_TOTAL - 1]; destItem = srcItem; srcItem.clear(); destItem._frame = 0; srcItems.sort(); destItems.sort(); + continue; } } else { c = newChar; @@ -399,9 +375,12 @@ Character *ItemsDialog::execute(Character *c, ItemsMode mode) { Common::fill(&arr[0], &arr[40], 0); arr[itemIndex] = 15; } - - redrawFlag = REDRAW_TEXT; + } else { + Common::fill(&arr[0], &arr[40], 0); + itemIndex = -1; } + + redrawFlag = REDRAW_TEXT; break; case Common::KEYCODE_a: @@ -414,7 +393,7 @@ Character *ItemsDialog::execute(Character *c, ItemsMode mode) { // Buy if (mode != ITEMMODE_CHAR_INFO && mode != ITEMMODE_RECHARGE && mode != ITEMMODE_ENCHANT && mode != ITEMMODE_TO_GOLD) { - mode = ITEMMODE_BLACKSMITH; + mode = ITEMMODE_BUY; c = &_itemsCharacter; redrawFlag = REDRAW_FULL; } @@ -466,7 +445,7 @@ Character *ItemsDialog::execute(Character *c, ItemsMode mode) { case Common::KEYCODE_m: // Misc category = CATEGORY_MISC; - redrawFlag = REDRAW_TEXT; + redrawFlag = REDRAW_FULL; break; case Common::KEYCODE_q: @@ -487,7 +466,7 @@ Character *ItemsDialog::execute(Character *c, ItemsMode mode) { case Common::KEYCODE_s: if (mode != ITEMMODE_CHAR_INFO && mode != ITEMMODE_RECHARGE && mode != ITEMMODE_ENCHANT && mode != ITEMMODE_TO_GOLD) { - mode = ITEMMODE_2; + mode = ITEMMODE_SELL; c = startingChar; redrawFlag = REDRAW_TEXT; } @@ -501,7 +480,7 @@ Character *ItemsDialog::execute(Character *c, ItemsMode mode) { case Common::KEYCODE_w: // Weapons category category = CATEGORY_WEAPON; - redrawFlag = REDRAW_TEXT; + redrawFlag = REDRAW_FULL; break; } } @@ -516,12 +495,13 @@ Character *ItemsDialog::execute(Character *c, ItemsMode mode) { return c; } -void ItemsDialog::loadButtons(ItemsMode mode, Character *&c) { - Party &party = *g_vm->_party; - _iconSprites.load(Common::String::format("%s.icn", - (mode == ITEMMODE_CHAR_INFO) ? "items" : "buy")); - _equipSprites.load("equip.icn"); +void ItemsDialog::loadButtons(ItemsMode mode, Character *&c, ItemCategory category) { + if (_iconSprites.empty()) + _iconSprites.load(Common::String::format("%s.icn", (mode == ITEMMODE_CHAR_INFO) ? "items" : "buy")); + if (_equipSprites.empty()) + _equipSprites.load("equip.icn"); + clearButtons(); if (mode == ITEMMODE_ENCHANT || mode == ITEMMODE_RECHARGE || mode == ITEMMODE_TO_GOLD) { // Enchant button list addButton(Common::Rect(12, 109, 36, 129), Common::KEYCODE_w, &_iconSprites); @@ -541,14 +521,16 @@ void ItemsDialog::loadButtons(ItemsMode mode, Character *&c) { addButton(Common::Rect(8, 83, 263, 91), Common::KEYCODE_8); addButton(Common::Rect(8, 92, 263, 100), Common::KEYCODE_9); } else { + bool flag = mode == ITEMMODE_BUY || mode == ITEMMODE_SELL || mode == ITEMMODE_IDENTIFY + || mode == ITEMMODE_REPAIR; addButton(Common::Rect(12, 109, 36, 129), Common::KEYCODE_w, &_iconSprites); addButton(Common::Rect(46, 109, 70, 129), Common::KEYCODE_a, &_iconSprites); addButton(Common::Rect(80, 109, 104, 129), Common::KEYCODE_c, &_iconSprites); addButton(Common::Rect(114, 109, 138, 129), Common::KEYCODE_m, &_iconSprites); - addButton(Common::Rect(148, 109, 172, 129), Common::KEYCODE_e, &_iconSprites); - addButton(Common::Rect(182, 109, 206, 129), Common::KEYCODE_r, &_iconSprites); - addButton(Common::Rect(216, 109, 240, 129), Common::KEYCODE_d, &_iconSprites); - addButton(Common::Rect(250, 109, 274, 129), Common::KEYCODE_q, &_iconSprites); + addButton(Common::Rect(148, 109, 172, 129), flag ? Common::KEYCODE_b : Common::KEYCODE_e, &_iconSprites); + addButton(Common::Rect(182, 109, 206, 129), flag ? Common::KEYCODE_s : Common::KEYCODE_r, &_iconSprites); + addButton(Common::Rect(216, 109, 240, 129), flag ? Common::KEYCODE_i : Common::KEYCODE_d, &_iconSprites); + addButton(Common::Rect(250, 109, 274, 129), flag ? Common::KEYCODE_f : Common::KEYCODE_q, &_iconSprites); addButton(Common::Rect(284, 109, 308, 129), Common::KEYCODE_ESCAPE, &_iconSprites); addButton(Common::Rect(8, 20, 263, 28), Common::KEYCODE_1); addButton(Common::Rect(8, 29, 263, 37), Common::KEYCODE_2); @@ -562,22 +544,44 @@ void ItemsDialog::loadButtons(ItemsMode mode, Character *&c) { addPartyButtons(_vm); } - if (mode == ITEMMODE_BLACKSMITH) { - _oldCharacter = c; - c = &_itemsCharacter; - party._blacksmithWares.blackData2CharData(_itemsCharacter); + if (mode == ITEMMODE_CHAR_INFO && category == CATEGORY_MISC) { + _buttons[4].setFrame(18); + _buttons[4]._value = Common::KEYCODE_u; + } + if (mode != ITEMMODE_ENCHANT && mode != ITEMMODE_RECHARGE && mode != ITEMMODE_TO_GOLD) { + _buttons[5].setFrame(10); + _buttons[6].setFrame(12); + _buttons[7].setFrame(14); + } - _buttons[4]._value = Common::KEYCODE_b; - _buttons[5]._value = Common::KEYCODE_s; - _buttons[6]._value = Common::KEYCODE_i; - _buttons[7]._value = Common::KEYCODE_f; + // Set button as depressed depending on which mode the dialog is currently in + switch (mode) { + case ITEMMODE_BUY: + _buttons[4].setFrame(9); + break; + case ITEMMODE_SELL: + _buttons[5].setFrame(11); + break; + case ITEMMODE_IDENTIFY: + _buttons[6].setFrame(13); + break; + case ITEMMODE_REPAIR: + _buttons[7].setFrame(15); + break; + default: + break; + } - setEquipmentIcons(); - } else { + if ((mode != ITEMMODE_CHAR_INFO || category != CATEGORY_MISC) && mode != ITEMMODE_ENCHANT + && mode != ITEMMODE_RECHARGE && mode != ITEMMODE_TO_GOLD) { + _buttons[4]._bounds.moveTo(148, _buttons[4]._bounds.top); + _buttons[9]._draw = false; + } else if (mode == ITEMMODE_RECHARGE) { + _buttons[4]._value = Common::KEYCODE_r; + } else if (mode == ITEMMODE_ENCHANT) { _buttons[4]._value = Common::KEYCODE_e; - _buttons[5]._value = Common::KEYCODE_r; - _buttons[6]._value = Common::KEYCODE_d; - _buttons[7]._value = Common::KEYCODE_q; + } else if (mode == ITEMMODE_TO_GOLD) { + _buttons[4]._value = Common::KEYCODE_g; } } @@ -643,10 +647,10 @@ int ItemsDialog::calcItemCost(Character *c, int itemIndex, ItemsMode mode, }; switch (mode) { - case ITEMMODE_BLACKSMITH: + case ITEMMODE_BUY: level = 0; break; - case ITEMMODE_2: + case ITEMMODE_SELL: case ITEMMODE_TO_GOLD: level = level == 0 ? 1 : 0; break; @@ -692,8 +696,8 @@ int ItemsDialog::calcItemCost(Character *c, int itemIndex, ItemsMode mode, amount3 = Res.ELEMENTAL_DAMAGE[i._material - 59 + 7] * 100; switch (mode) { - case ITEMMODE_BLACKSMITH: - case ITEMMODE_2: + case ITEMMODE_BUY: + case ITEMMODE_SELL: case ITEMMODE_REPAIR: case ITEMMODE_IDENTIFY: case ITEMMODE_TO_GOLD: @@ -712,8 +716,8 @@ int ItemsDialog::calcItemCost(Character *c, int itemIndex, ItemsMode mode, amount4 = Res.MISC_BASE_COSTS[i._id]; switch (mode) { - case ITEMMODE_BLACKSMITH: - case ITEMMODE_2: + case ITEMMODE_BUY: + case ITEMMODE_SELL: case ITEMMODE_REPAIR: case ITEMMODE_IDENTIFY: case ITEMMODE_TO_GOLD: @@ -721,6 +725,15 @@ int ItemsDialog::calcItemCost(Character *c, int itemIndex, ItemsMode mode, if (!result) result = 1; break; + + case ITEMMODE_3: + case ITEMMODE_RECHARGE: + case ITEMMODE_5: + case ITEMMODE_ENCHANT: + // Show number of charges + result = i._state._counter; + break; + default: break; } @@ -742,64 +755,18 @@ int ItemsDialog::doItemOptions(Character &c, int actionIndex, int itemIndex, Ite Sound &sound = *_vm->_sound; Spells &spells = *_vm->_spells; Windows &windows = *_vm->_windows; - bool isDarkCc = _vm->_files->_isDarkCc; + int ccNum = _vm->_files->_ccNum; - XeenItem *itemCategories[4] = { &c._weapons[0], &c._armor[0], &c._accessories[0], &c._misc[0] }; - XeenItem *items = itemCategories[category]; - if (!items[0]._id) + InventoryItems &items = c._items[category]; + if (items[0].empty()) // Inventory is empty return category == CATEGORY_MISC ? 0 : 2; - Window &w = windows[11]; - SpriteResource escSprites; - if (itemIndex < 0 || itemIndex > 8) { - saveButtons(); - - escSprites.load("esc.icn"); - addButton(Common::Rect(235, 111, 259, 131), Common::KEYCODE_ESCAPE, &escSprites); - addButton(Common::Rect(8, 20, 263, 28), Common::KEYCODE_1); - addButton(Common::Rect(8, 29, 263, 37), Common::KEYCODE_2); - addButton(Common::Rect(8, 38, 263, 46), Common::KEYCODE_3); - addButton(Common::Rect(8, 47, 263, 55), Common::KEYCODE_4); - addButton(Common::Rect(8, 56, 263, 64), Common::KEYCODE_5); - addButton(Common::Rect(8, 65, 263, 73), Common::KEYCODE_6); - addButton(Common::Rect(8, 74, 263, 82), Common::KEYCODE_7); - addButton(Common::Rect(8, 83, 263, 91), Common::KEYCODE_8); - addButton(Common::Rect(8, 92, 263, 100), Common::KEYCODE_9); - - w.open(); - w.writeString(Common::String::format(Res.WHICH_ITEM, Res.ITEM_ACTIONS[actionIndex])); - _iconSprites.draw(0, 0, Common::Point(235, 111)); - w.update(); - - while (!_vm->shouldExit()) { - while (!_buttonValue) { - events.pollEventsAndWait(); - checkEvents(_vm); - if (_vm->shouldExit()) - return false; - } - - if (_buttonValue == Common::KEYCODE_ESCAPE) { - itemIndex = -1; - break; - } else if (_buttonValue >= Common::KEYCODE_1 && _buttonValue <= Common::KEYCODE_9) { - // Check whether there's an item at the selected index - int selectedIndex = _buttonValue - Common::KEYCODE_1; - if (!items[selectedIndex]._id) - continue; - - itemIndex = selectedIndex; - break; - } - } - - w.close(); - restoreButtons(); - } + if (itemIndex < 0 || itemIndex > 8) + itemIndex = ItemSelectionDialog::show(actionIndex, items); if (itemIndex != -1) { - XeenItem &item = c._items[category][itemIndex]; + XeenItem &item = items[itemIndex]; switch (mode) { case ITEMMODE_CHAR_INFO: @@ -830,10 +797,8 @@ int ItemsDialog::doItemOptions(Character &c, int actionIndex, int itemIndex, Ite default: if (combat._itemFlag) { ErrorScroll::show(_vm, Res.USE_ITEM_IN_COMBAT); - } else if (i._id && (i._bonusFlags & ITEMFLAG_BONUS_MASK) - && !(i._bonusFlags & (ITEMFLAG_BROKEN | ITEMFLAG_CURSED))) { - int charges = (i._bonusFlags & ITEMFLAG_BONUS_MASK) - 1; - i._bonusFlags = charges; + } else if (i._id && !i.isBad() && i._state._counter > 0) { + --i._state._counter; _oldCharacter = &c; windows[30].close(); @@ -841,7 +806,7 @@ int ItemsDialog::doItemOptions(Character &c, int actionIndex, int itemIndex, Ite windows[24].close(); spells.castItemSpell(i._id); - if (!charges) { + if (!i._state._counter) { // Ran out of charges, so make item disappear c._items[category][itemIndex].clear(); c._items[category].sort(); @@ -868,7 +833,7 @@ int ItemsDialog::doItemOptions(Character &c, int actionIndex, int itemIndex, Ite } break; - case ITEMMODE_BLACKSMITH: { + case ITEMMODE_BUY: { InventoryItems &invItems = _oldCharacter->_items[category]; if (invItems.isFull()) { // Character's inventory for that category is already full @@ -880,15 +845,18 @@ int ItemsDialog::doItemOptions(Character &c, int actionIndex, int itemIndex, Ite if (Confirm::show(_vm, Common::String::format(Res.BUY_X_FOR_Y_GOLD, desc.c_str(), cost))) { if (party.subtract(CONS_GOLD, cost, WHERE_PARTY, WT_FREEZE_WAIT)) { - if (isDarkCc) { + if (ccNum) { sound.stopSound(); sound.playSound("choice2.voc"); } // Add entry to the end of the list - XeenItem &bsItem = c._items[category][itemIndex]; - _oldCharacter->_items[category][INV_ITEMS_TOTAL - 1] = bsItem; - bsItem.clear(); + XeenItem &srcItem = c._items[category][itemIndex]; + XeenItem &destItem = _oldCharacter->_items[category][INV_ITEMS_TOTAL - 1]; + destItem = srcItem; + destItem._frame = 0; + + srcItem.clear(); c._items[category].sort(); _oldCharacter->_items[category].sort(); } @@ -897,14 +865,14 @@ int ItemsDialog::doItemOptions(Character &c, int actionIndex, int itemIndex, Ite break; } - case ITEMMODE_2: { + case ITEMMODE_SELL: { bool noNeed; switch (category) { case CATEGORY_WEAPON: - noNeed = (item._bonusFlags & ITEMFLAG_CURSED) || item._id == 34; + noNeed = (item._state._cursed) || item._id >= XEEN_SLAYER_SWORD; break; default: - noNeed = item._bonusFlags & ITEMFLAG_CURSED; + noNeed = item._state._cursed; break; } @@ -929,17 +897,12 @@ int ItemsDialog::doItemOptions(Character &c, int actionIndex, int itemIndex, Ite } case ITEMMODE_RECHARGE: - if (category != CATEGORY_MISC || c._misc[itemIndex]._material > 9 - || c._misc[itemIndex]._id == 53 || c._misc[itemIndex]._id == 0) { + if (category != CATEGORY_MISC || item.empty() || item._material > 9 || item._id == 53) { sound.playFX(21); ErrorScroll::show(_vm, Common::String::format(Res.NOT_RECHARGABLE, Res.SPELL_FAILED)); } else { - int charges = MIN(63, _vm->getRandomNumber(1, 6) + - (c._misc[itemIndex]._bonusFlags & ITEMFLAG_BONUS_MASK)); + item._state._counter = MIN(63, _vm->getRandomNumber(1, 6) + item._state._counter); sound.playFX(20); - - c._misc[itemIndex]._bonusFlags = (c._misc[itemIndex]._bonusFlags - & ~ITEMFLAG_BONUS_MASK) | charges; } return 2; @@ -951,7 +914,7 @@ int ItemsDialog::doItemOptions(Character &c, int actionIndex, int itemIndex, Ite } case ITEMMODE_REPAIR: - if (!(item._bonusFlags & ITEMFLAG_BROKEN)) { + if (!item._state._broken) { ErrorScroll::show(_vm, Res.ITEM_NOT_BROKEN); } else { int cost = calcItemCost(&c, itemIndex, mode, actionIndex, category); @@ -961,7 +924,7 @@ int ItemsDialog::doItemOptions(Character &c, int actionIndex, int itemIndex, Ite cost); if (Confirm::show(_vm, msg) && party.subtract(CONS_GOLD, cost, WHERE_PARTY)) { - item._bonusFlags &= ~ITEMFLAG_BROKEN; + item._state._broken = false; } } break; @@ -1016,11 +979,11 @@ void ItemsDialog::itemToGold(Character &c, int itemIndex, ItemCategory category, Party &party = *_vm->_party; Sound &sound = *_vm->_sound; - if (category == CATEGORY_WEAPON && item._id == 34) { + if (category == CATEGORY_WEAPON && item._id >= XEEN_SLAYER_SWORD) { sound.playFX(21); ErrorScroll::show(_vm, Common::String::format("\v012\t000\x03""c%s", Res.SPELL_FAILED)); - } else if (item._id != 0) { + } else if (!item.empty()) { // There is a valid item present // Calculate cost of item and add it to the party's total int cost = calcItemCost(&c, itemIndex, mode, 1, category); @@ -1032,4 +995,67 @@ void ItemsDialog::itemToGold(Character &c, int itemIndex, ItemCategory category, } } +/*------------------------------------------------------------------------*/ + +int ItemSelectionDialog::show(int actionIndex, InventoryItems &items) { + ItemSelectionDialog *dlg = new ItemSelectionDialog(g_vm, actionIndex, items); + int result = dlg->execute(); + delete dlg; + + return result; +} + +void ItemSelectionDialog::loadButtons() { + _icons.load("esc.icn"); + addButton(Common::Rect(235, 111, 259, 131), Common::KEYCODE_ESCAPE, &_icons); + addButton(Common::Rect(8, 20, 263, 28), Common::KEYCODE_1); + addButton(Common::Rect(8, 29, 263, 37), Common::KEYCODE_2); + addButton(Common::Rect(8, 38, 263, 46), Common::KEYCODE_3); + addButton(Common::Rect(8, 47, 263, 55), Common::KEYCODE_4); + addButton(Common::Rect(8, 56, 263, 64), Common::KEYCODE_5); + addButton(Common::Rect(8, 65, 263, 73), Common::KEYCODE_6); + addButton(Common::Rect(8, 74, 263, 82), Common::KEYCODE_7); + addButton(Common::Rect(8, 83, 263, 91), Common::KEYCODE_8); + addButton(Common::Rect(8, 92, 263, 100), Common::KEYCODE_9); +} + +int ItemSelectionDialog::execute() { + EventsManager &events = *g_vm->_events; + Windows &windows = *g_vm->_windows; + Window &w = windows[13]; + + w.open(); + w.writeString(Common::String::format(Res.WHICH_ITEM, Res.ITEM_ACTIONS[_actionIndex])); + _icons.draw(0, 0, Common::Point(235, 111)); + w.update(); + + int itemIndex = -1; + while (!_vm->shouldExit()) { + _buttonValue = 0; + while (!_buttonValue) { + events.pollEventsAndWait(); + checkEvents(_vm); + if (_vm->shouldExit()) + return false; + } + + if (_buttonValue == Common::KEYCODE_ESCAPE) { + itemIndex = -1; + break; + } + else if (_buttonValue >= Common::KEYCODE_1 && _buttonValue <= Common::KEYCODE_9) { + // Check whether there's an item at the selected index + int selectedIndex = _buttonValue - Common::KEYCODE_1; + if (!_items[selectedIndex]._id) + continue; + + itemIndex = selectedIndex; + break; + } + } + + w.close(); + return itemIndex; +} + } // End of namespace Xeen diff --git a/engines/xeen/dialogs/dialogs_items.h b/engines/xeen/dialogs/dialogs_items.h index 33930640f1..d3632dc8dc 100644 --- a/engines/xeen/dialogs/dialogs_items.h +++ b/engines/xeen/dialogs/dialogs_items.h @@ -30,9 +30,10 @@ namespace Xeen { enum ItemsMode { - ITEMMODE_CHAR_INFO = 0, ITEMMODE_BLACKSMITH = 1, ITEMMODE_2 = 2, ITEMMODE_3 = 3, + ITEMMODE_CHAR_INFO = 0, ITEMMODE_BUY = 1, ITEMMODE_SELL = 2, ITEMMODE_3 = 3, ITEMMODE_RECHARGE = 4, ITEMMODE_5 = 5, ITEMMODE_ENCHANT = 6, ITEMMODE_COMBAT = 7, ITEMMODE_8 = 8, - ITEMMODE_REPAIR = 9, ITEMMODE_IDENTIFY = 10, ITEMMODE_TO_GOLD = 11 + ITEMMODE_REPAIR = 9, ITEMMODE_IDENTIFY = 10, ITEMMODE_TO_GOLD = 11, + ITEMMODE_INVALID = -1 }; class ItemsDialog : public ButtonContainer { @@ -50,7 +51,7 @@ private: /** * Load the buttons for the dialog */ - void loadButtons(ItemsMode mode, Character *&c); + void loadButtons(ItemsMode mode, Character *&c, ItemCategory category); /** * Sets the equipment icon to use for each item for display @@ -58,7 +59,7 @@ private: void setEquipmentIcons(); /** - * Calculate the cost of an item + * Calculate the cost of an item, or charges renaming for Misc items as appropriate */ int calcItemCost(Character *c, int itemIndex, ItemsMode mode, int skillLevel, ItemCategory category); @@ -71,6 +72,38 @@ public: static Character *show(XeenEngine *vm, Character *c, ItemsMode mode); }; +class ItemSelectionDialog : public ButtonContainer { +private: + SpriteResource _icons; + int _actionIndex; + InventoryItems &_items; + + ItemSelectionDialog(XeenEngine *vm, int actionIndex, InventoryItems &items) : ButtonContainer(vm), + _actionIndex(actionIndex), _items(items) { + loadButtons(); + } + + /** + * Executes the dialog + * @returns Selected item index + */ + int execute(); + + /** + * Loads buttons + */ + void loadButtons(); +public: + /** + * Shows the dialog + * @param actionIndex Current action type + * @param items Currently active items category + * @returns Selected item index + */ + static int show(int actionIndex, InventoryItems &items); +}; + + } // End of namespace Xeen #endif /* XEEN_DIALOGS_ITEMS_H */ diff --git a/engines/xeen/dialogs/dialogs_message.cpp b/engines/xeen/dialogs/dialogs_message.cpp index df8afea34c..f571e6e811 100644 --- a/engines/xeen/dialogs/dialogs_message.cpp +++ b/engines/xeen/dialogs/dialogs_message.cpp @@ -51,7 +51,7 @@ void MessageDialog::execute(const Common::String &msg, MessageWaitType waitType) break; case WT_ANIMATED_WAIT: - if (windows[11]._enabled || _vm->_mode == MODE_17) { + if (windows[11]._enabled || _vm->_mode == MODE_INTERACTIVE7) { g_vm->_locations->wait(); break; } diff --git a/engines/xeen/dialogs/dialogs_party.cpp b/engines/xeen/dialogs/dialogs_party.cpp index c3d6843ffd..cf49007f30 100644 --- a/engines/xeen/dialogs/dialogs_party.cpp +++ b/engines/xeen/dialogs/dialogs_party.cpp @@ -45,6 +45,7 @@ void PartyDialog::show(XeenEngine *vm) { void PartyDialog::execute() { EventsManager &events = *_vm->_events; + FileManager &files = *_vm->_files; Interface &intf = *_vm->_interface; Map &map = *_vm->_map; Party &party = *_vm->_party; @@ -54,18 +55,19 @@ void PartyDialog::execute() { bool modeFlag = false; int startingChar = 0; + sound.playSong(files._ccNum ? "newbrigh.m" : "inn.m"); loadButtons(); setupBackground(); while (!_vm->shouldExit()) { - _vm->_mode = MODE_1; + _vm->_mode = MODE_INTERACTIVE; // Build up a list of available characters in the Roster that are on the // same side of Xeen as the player is currently on _charList.clear(); for (int i = 0; i < XEEN_TOTAL_CHARACTERS; ++i) { Character &player = party._roster[i]; - if (player._name.empty() || player._xeenSide != (map._loadDarkSide ? 1 : 0)) + if (player._name.empty() || player._xeenSide != map._loadCcNum) continue; _charList.push_back(i); diff --git a/engines/xeen/dialogs/dialogs_query.cpp b/engines/xeen/dialogs/dialogs_query.cpp index 79f46826cd..876ddafc2b 100644 --- a/engines/xeen/dialogs/dialogs_query.cpp +++ b/engines/xeen/dialogs/dialogs_query.cpp @@ -68,8 +68,11 @@ bool Confirm::execute(const Common::String &msg, int mode) { bool result = false; while (!_vm->shouldExit()) { - events.pollEvents(); - checkEvents(_vm); + _buttonValue = 0; + while (!_vm->shouldExit() && !_buttonValue) { + events.pollEvents(); + checkEvents(_vm); + } if ((mode & 0x80) || _buttonValue == Common::KEYCODE_ESCAPE || _buttonValue == Common::KEYCODE_n) @@ -81,6 +84,7 @@ bool Confirm::execute(const Common::String &msg, int mode) { } } + events.clearEvents(); w.close(); return result; } @@ -108,6 +112,7 @@ bool YesNo::execute(bool type, bool townFlag) { Mode oldMode = _vm->_mode; _vm->_mode = oldMode == MODE_7 ? MODE_8 : MODE_7; + events.clearEvents(); if (!type) { confirmSprites.load("confirm.icn"); diff --git a/engines/xeen/dialogs/dialogs_quests.cpp b/engines/xeen/dialogs/dialogs_quests.cpp index e4f62270ef..94834c1c02 100644 --- a/engines/xeen/dialogs/dialogs_quests.cpp +++ b/engines/xeen/dialogs/dialogs_quests.cpp @@ -44,6 +44,9 @@ void Quests::execute() { int count = 0; bool headerShown = false; int topRow = 0; + const char **questItemNames = (g_vm->getGameID() == GType_Swords) ? Res.QUEST_ITEM_NAMES_SWORDS : Res.QUEST_ITEM_NAMES; + int itemsCount = (g_vm->getGameID() == GType_Swords) ? TOTAL_QUEST_ITEMS_SWORDS : TOTAL_QUEST_ITEMS; + const char *title1 = (g_vm->getGameID() == GType_Swords) ? Res.SWORDS_OF_XEEN_LINE : Res.CLOUDS_OF_XEEN_LINE; addButtons(); loadQuestNotes(); @@ -66,37 +69,49 @@ void Quests::execute() { switch (mode) { case QUEST_ITEMS: - for (int idx = 0; idx < TOTAL_QUEST_ITEMS; ++idx) + for (int idx = 0; idx < itemsCount; ++idx) lines[idx] = "\b \b*"; count = 0; headerShown = false; - for (int idx = 0; idx < TOTAL_QUEST_ITEMS; ++idx) { + for (int idx = 0; idx < itemsCount; ++idx) { if (party._questItems[idx]) { - if (!count && !headerShown && idx < 35) { - lines[count++] = Res.CLOUDS_OF_XEEN_LINE; - } - if (idx >= 35 && !headerShown) { + if (!count ) { + if (_vm->getGameID() == GType_Swords) + lines[count++] = Res.SWORDS_OF_XEEN_LINE; + else if (idx < 35) + lines[count++] = Res.CLOUDS_OF_XEEN_LINE; + } else if (_vm->getGameID() != GType_Swords && idx >= 35 && !headerShown) { lines[count++] = Res.DARKSIDE_OF_XEEN_LINE; headerShown = true; } - switch (idx) { - case 17: - case 26: - case 79: - case 80: - case 81: - case 82: - case 83: - case 84: + bool multiFlag = false; + if (_vm->getGameID() == GType_Swords) { + multiFlag = (idx == 20) || (idx == 27) || (idx == 41); + } else { + switch (idx) { + case 17: + case 26: + case 79: + case 80: + case 81: + case 82: + case 83: + case 84: + multiFlag = true; + break; + default: + break; + } + } + + if (multiFlag) { lines[count++] = Common::String::format("%d %s%c", - party._questItems[idx], Res.QUEST_ITEM_NAMES[idx], + party._questItems[idx], questItemNames[idx], party._questItems[idx] == 1 ? ' ' : 's'); - break; - default: - lines[count++] = Res.QUEST_ITEM_NAMES[idx]; - break; + } else { + lines[count++] = questItemNames[idx]; } } } @@ -115,17 +130,17 @@ void Quests::execute() { break; case CURRENT_QUESTS: - for (int idx = 0; idx < TOTAL_QUEST_ITEMS; ++idx) + for (int idx = 0; idx < itemsCount; ++idx) lines[idx] = ""; count = 0; headerShown = false; for (int idx = 0; idx < TOTAL_QUEST_FLAGS; ++idx) { - if (party._questFlags[(idx + 1) / 30][(idx + 1) % 30]) { - if (!count && !headerShown && idx < 29) { - lines[count++] = Res.CLOUDS_OF_XEEN_LINE; + if (party._questFlags[idx + 1]) { + if (!count && !headerShown && (_vm->getGameID() == GType_Swords || idx < 29)) { + lines[count++] = title1; } - if (idx > 28 && !headerShown) { + if (_vm->getGameID() != GType_Swords && idx > 28 && !headerShown) { lines[count++] = Res.DARKSIDE_OF_XEEN_LINE; headerShown = true; } @@ -141,23 +156,39 @@ void Quests::execute() { lines[topRow].c_str(), lines[topRow + 1].c_str(), lines[topRow + 2].c_str())); break; - case AUTO_NOTES: - for (int idx = 0; idx < MAX_DIALOG_LINES; ++idx) + case AUTO_NOTES: { + int max, offset; + switch (_vm->getGameID()) { + case GType_Swords: + max = 49; + offset = 51; + break; + case GType_Clouds: + max = MAX_DIALOG_LINES; + offset = 31; + break; + default: + max = MAX_DIALOG_LINES; + offset = 56; + break; + } + + for (int idx = 0; idx < max; ++idx) lines[idx] = ""; count = 0; headerShown = false; - for (int idx = 0; idx < MAX_DIALOG_LINES; ++idx) { - if (party._worldFlags[idx]) { - if (!count && !headerShown && idx < 72) { - lines[count++] = Res.CLOUDS_OF_XEEN_LINE; + for (int idx = 0; idx < max; ++idx) { + if (party._worldFlags[idx + (_vm->getGameID() != GType_Swords ? 1 : 0)]) { + if (!count && !headerShown && (_vm->getGameID() == GType_Swords || idx < 72)) { + lines[count++] = title1; } if (idx >= 72 && !headerShown) { lines[count++] = Res.DARKSIDE_OF_XEEN_LINE; headerShown = true; } - lines[count++] = _questNotes[idx + 56]; + lines[count++] = _questNotes[idx + offset]; } } @@ -173,6 +204,7 @@ void Quests::execute() { )); break; } + } windows[30].writeString("\v000\t000"); windows[24].update(); @@ -245,7 +277,7 @@ void Quests::addButtons() { } void Quests::loadQuestNotes() { - File f("qnotes.bin"); + File f("qnotes.bin", 1); while (f.pos() < f.size()) _questNotes.push_back(f.readString()); f.close(); diff --git a/engines/xeen/dialogs/dialogs_spells.cpp b/engines/xeen/dialogs/dialogs_spells.cpp index 3da5a5149e..27329e48df 100644 --- a/engines/xeen/dialogs/dialogs_spells.cpp +++ b/engines/xeen/dialogs/dialogs_spells.cpp @@ -31,37 +31,40 @@ namespace Xeen { Character *SpellsDialog::show(XeenEngine *vm, ButtonContainer *priorDialog, - Character *c, int isCasting) { + Character *c, SpellDialogMode mode) { SpellsDialog *dlg = new SpellsDialog(vm); - Character *result = dlg->execute(priorDialog, c, isCasting); + Character *result = dlg->execute(priorDialog, c, mode); delete dlg; return result; } -Character *SpellsDialog::execute(ButtonContainer *priorDialog, Character *c, int isCasting) { +Character *SpellsDialog::execute(ButtonContainer *priorDialog, Character *c, int mode) { EventsManager &events = *_vm->_events; Interface &intf = *_vm->_interface; Party &party = *_vm->_party; Sound &sound = *_vm->_sound; Spells &spells = *_vm->_spells; Windows &windows = *_vm->_windows; - bool isDarkCc = _vm->_files->_isDarkCc; + Window &w = windows[25]; + int ccNum = _vm->_files->_ccNum; + loadButtons(); + loadStrings("spldesc.bin"); - int castingCopy = isCasting; - isCasting &= 0x7f; + int modeCopy = mode; + mode &= 0x7f; int selection = -1; int topIndex = 0; int newSelection; - windows[25].open(); + w.open(); do { - if (!isCasting) { + if (!mode) { if (!c->guildMember()) { sound.stopSound(); intf._overallFrame = 5; - sound.playSound(isDarkCc ? "skull1.voc" : "guild11.voc", 1); + sound.playSound(ccNum ? "skull1.voc" : "guild11.voc", 1); break; } @@ -69,13 +72,12 @@ Character *SpellsDialog::execute(ButtonContainer *priorDialog, Character *c, int Common::String msg = Common::String::format(Res.GUILD_OPTIONS, title.c_str(), XeenEngine::printMil(party._gold).c_str()); windows[10].writeString(msg); - - warning("TODO: Sprite draw using previously used button sprites"); + priorDialog->drawButtons(&windows[10]); } _spells.clear(); - const char *errorMsg = setSpellText(c, castingCopy); - windows[25].writeString(Common::String::format(Res.SPELLS_FOR, + const char *errorMsg = setSpellText(c, modeCopy); + w.writeString(Common::String::format(Res.SPELLS_FOR, errorMsg == nullptr ? Res.SPELL_LINES_0_TO_9 : "", c->_name.c_str())); @@ -103,18 +105,19 @@ Character *SpellsDialog::execute(ButtonContainer *priorDialog, Character *c, int colors[3], names[3], colors[4], names[4], colors[5], names[5], colors[6], names[6], colors[7], names[7], colors[8], names[8], colors[9], names[9], - isCasting ? Res.SPELL_PTS : Res.GOLD, - isCasting ? c->_currentSp : party._gold + mode ? Res.SPELL_PTS : Res.GOLD, + mode ? c->_currentSp : party._gold )); _scrollSprites.draw(0, 4, Common::Point(39, 26)); _scrollSprites.draw(0, 0, Common::Point(187, 26)); _scrollSprites.draw(0, 2, Common::Point(187, 111)); - if (isCasting) - _scrollSprites.draw(windows[25], 5, Common::Point(132, 123)); + if (mode) + _scrollSprites.draw(w, 5, Common::Point(132, 123)); - windows[25].update(); + w.update(); + _buttonValue = 0; do { events.pollEventsAndWait(); checkEvents(_vm); @@ -134,27 +137,14 @@ Character *SpellsDialog::execute(ButtonContainer *priorDialog, Character *c, int spells._lastCaster = _buttonValue; intf.highlightChar(_buttonValue); - if (_vm->_mode == MODE_17) { + if (_vm->_mode == MODE_INTERACTIVE7) { windows[10].writeString(Common::String::format(Res.GUILD_OPTIONS, XeenEngine::printMil(party._gold).c_str(), Res.GUILD_TEXT, c->_name.c_str())); } else { - int category; - switch (c->_class) { - case CLASS_ARCHER: - case CLASS_SORCERER: - category = 1; - break; - case CLASS_DRUID: - case CLASS_RANGER: - category = 2; - break; - default: - category = 0; - break; - } + SpellsCategory category = c->getSpellsCategory(); + int spellIndex = (c->_currentSpell == -1) ? SPELLS_PER_CLASS : c->_currentSpell; + int spellId = (category == SPELLCAT_INVALID) ? NO_SPELL : Res.SPELLS_ALLOWED[category][spellIndex]; - int spellIndex = (c->_currentSpell == -1) ? 39 : c->_currentSpell; - int spellId = Res.SPELLS_ALLOWED[category][spellIndex]; windows[10].writeString(Common::String::format(Res.CAST_SPELL_DETAILS, c->_name.c_str(), spells._spellNames[spellId].c_str(), spells.calcSpellPoints(spellId, c->getCurrentLevel()), @@ -194,53 +184,31 @@ Character *SpellsDialog::execute(ButtonContainer *priorDialog, Character *c, int (_buttonValue - Common::KEYCODE_1)); if (newSelection < (int)_spells.size()) { - int expenseFactor = 0; - int category = 0; - - switch (c->_class) { - case CLASS_PALADIN: - expenseFactor = 1; - category = 0; - break; - case CLASS_ARCHER: - expenseFactor = 1; - category = 1; - break; - case CLASS_CLERIC: - category = 0; - break; - case CLASS_SORCERER: - category = 1; - break; - case CLASS_DRUID: - category = 2; - break; - case CLASS_RANGER: - expenseFactor = 1; - category = 2; - break; - default: - break; - } + SpellsCategory category = c->getSpellsCategory(); + int expenseFactor = c->getSpellsExpenseFactor(); int spellIndex = _spells[newSelection]._spellIndex; int spellId = Res.SPELLS_ALLOWED[category][spellIndex]; int spellCost = spells.calcSpellCost(spellId, expenseFactor); - if (isCasting) { + if (mode) { + // Casting selection = newSelection; } else { - Common::String spellName = _spells[newSelection]._name; - Common::String msg = (castingCopy & 0x80) ? - Common::String::format(Res.SPELLS_PRESS_A_KEY, spellName.c_str()) : - Common::String::format(Res.SPELLS_PURCHASE, spellName.c_str(), spellCost); + // Guild spells dialog: Spells Info or Buy + const Common::String &spellName = spells._spellNames[spellId]; + const Common::String &spellDesc = _textStrings[spellId]; - if (Confirm::show(_vm, msg, castingCopy + 1)) { + Common::String msg = (modeCopy & 0x80) ? + Common::String::format(Res.SPELL_INFO, spellName.c_str(), spellDesc.c_str()) : + Common::String::format(Res.SPELL_PURCHASE, spellDesc.c_str(), spellName.c_str(), spellCost); + + if (Confirm::show(_vm, msg, modeCopy + 1)) { if (party.subtract(CONS_GOLD, spellCost, WHERE_PARTY, WT_FREEZE_WAIT)) { c->_spells[spellIndex] = true; sound.stopSound(); intf._overallFrame = 0; - sound.playSound(isDarkCc ? "guild12.voc" : "parrot2.voc", 1); + sound.playSound(ccNum ? "parrot2.voc" : "guild12.voc", 1); } else { sound.playFX(21); } @@ -273,11 +241,11 @@ Character *SpellsDialog::execute(ButtonContainer *priorDialog, Character *c, int } } while (!_vm->shouldExit() && _buttonValue != Common::KEYCODE_ESCAPE); - windows[25].close(); + w.close(); if (_vm->shouldExit()) selection = -1; - if (isCasting && selection != -1) + if (mode && selection != -1) c->_currentSpell = _spells[selection]._spellIndex; return c; @@ -305,52 +273,41 @@ void SpellsDialog::loadButtons() { addPartyButtons(_vm); } -const char *SpellsDialog::setSpellText(Character *c, int isCasting) { +const char *SpellsDialog::setSpellText(Character *c, int mode) { Party &party = *_vm->_party; Spells &spells = *_vm->_spells; - bool isDarkCc = _vm->_files->_isDarkCc; - int expenseFactor = 0; + int ccNum = _vm->_files->_ccNum; int currLevel = c->getCurrentLevel(); - int category; + SpellsCategory category = c->getSpellsCategory(); + int expenseFactor = c->getSpellsExpenseFactor(); - if ((isCasting & 0x7f) == 0) { - switch (c->_class) { - case CLASS_PALADIN: - expenseFactor = 1; - category = 0; - break; - case CLASS_ARCHER: - expenseFactor = 1; - category = 1; - break; - case CLASS_CLERIC: - category = 0; - break; - case CLASS_SORCERER: - category = 1; - break; - case CLASS_DRUID: - category = 2; - break; - case CLASS_RANGER: - expenseFactor = 1; - category = 2; - break; - default: - category = -1; - break; - } + if ((mode & 0x7f) == 0) { + if (category != SPELLCAT_INVALID) { + if (_vm->getGameID() == GType_Swords && party._mazeId == 49) { + for (int spellId = 0; spellId < 10; ++spellId) { + int idx = 0; + while (idx < SPELLS_PER_CLASS && Res.SPELLS_ALLOWED[category][idx] != + Res.DARK_SPELL_OFFSETS[category][spellId]) + ++idx; - if (category != -1) { - if (party._mazeId == 49 || party._mazeId == 37) { - for (uint spellId = 0; spellId < 76; ++spellId) { + if (idx < SPELLS_PER_CLASS) { + if (!c->_spells[idx] || (mode & 0x80)) { + int cost = spells.calcSpellCost(Res.SPELLS_ALLOWED[category][idx], expenseFactor); + _spells.push_back(SpellEntry(Common::String::format("\x3l%s\x3r\x9""000%u", + spells._spellNames[Res.SPELLS_ALLOWED[category][idx]].c_str(), cost), + idx, spellId)); + } + } + } + } else if (party._mazeId == 49 || party._mazeId == 37) { + for (uint spellId = 0; spellId < TOTAL_SPELLS; ++spellId) { int idx = 0; - while (idx < MAX_SPELLS_PER_CLASS && Res.SPELLS_ALLOWED[category][idx] != (int)spellId) + while (idx < SPELLS_PER_CLASS && Res.SPELLS_ALLOWED[category][idx] != (int)spellId) ++idx; // Handling if the spell is appropriate for the character's class - if (idx < MAX_SPELLS_PER_CLASS) { - if (!c->_spells[idx] || (isCasting & 0x80)) { + if (idx < SPELLS_PER_CLASS) { + if (!c->_spells[idx] || (mode & 0x80)) { int cost = spells.calcSpellCost(Res.SPELLS_ALLOWED[category][idx], expenseFactor); _spells.push_back(SpellEntry(Common::String::format("\x3l%s\x3r\x9""000%u", spells._spellNames[Res.SPELLS_ALLOWED[category][idx]].c_str(), cost), @@ -358,16 +315,38 @@ const char *SpellsDialog::setSpellText(Character *c, int isCasting) { } } } - } else if (isDarkCc) { - int groupIndex = (party._mazeId - 29) / 2; - for (int spellId = Res.DARK_SPELL_RANGES[groupIndex][0]; - spellId < Res.DARK_SPELL_RANGES[groupIndex][1]; ++spellId) { + } else if (ccNum) { + const int *RANGE; + + if (_vm->getGameID() == GType_Swords) { + // Set subset of spells to sell in each Swords of Xeen guild + int groupIndex; + switch (party._mazeId) { + case 92: + groupIndex = 1; + break; + case 63: + groupIndex = 2; + break; + case 53: + default: + groupIndex = 0; + break; + } + RANGE = Res.SWORDS_SPELL_RANGES[category * 4 + groupIndex]; + } else { + int groupIndex = (party._mazeId - 29) / 2; + RANGE = Res.DARK_SPELL_RANGES[category * 4 + groupIndex]; + } + + for (int spellId = RANGE[0]; spellId < RANGE[1]; ++spellId) { int idx = 0; - while (idx < 40 && Res.SPELLS_ALLOWED[category][idx] == - Res.DARK_SPELL_OFFSETS[category][spellId]); + while (idx < SPELLS_PER_CLASS && Res.SPELLS_ALLOWED[category][idx] != + Res.DARK_SPELL_OFFSETS[category][spellId]) + ++idx; - if (idx < 40) { - if (!c->_spells[idx] || (isCasting & 0x80)) { + if (idx < SPELLS_PER_CLASS) { + if (!c->_spells[idx] || (mode & 0x80)) { int cost = spells.calcSpellCost(Res.SPELLS_ALLOWED[category][idx], expenseFactor); _spells.push_back(SpellEntry(Common::String::format("\x3l%s\x3r\x9""000%u", spells._spellNames[Res.SPELLS_ALLOWED[category][idx]].c_str(), cost), @@ -378,11 +357,12 @@ const char *SpellsDialog::setSpellText(Character *c, int isCasting) { } else { for (int spellId = 0; spellId < 20; ++spellId) { int idx = 0; - while (Res.CLOUDS_SPELL_OFFSETS[party._mazeId - 29][spellId] != - (int)Res.SPELLS_ALLOWED[category][idx] && idx < 40) ; + while (idx < SPELLS_PER_CLASS && Res.CLOUDS_GUILD_SPELLS[party._mazeId - 28][spellId] != + (int)Res.SPELLS_ALLOWED[category][idx]) + ++idx; - if (idx < 40) { - if (!c->_spells[idx] || (isCasting & 0x80)) { + if (idx < SPELLS_PER_CLASS) { + if (!c->_spells[idx] || (mode & 0x80)) { int cost = spells.calcSpellCost(Res.SPELLS_ALLOWED[category][idx], expenseFactor); _spells.push_back(SpellEntry(Common::String::format("\x3l%s\x3r\x9""000%u", spells._spellNames[Res.SPELLS_ALLOWED[category][idx]].c_str(), cost), @@ -396,27 +376,11 @@ const char *SpellsDialog::setSpellText(Character *c, int isCasting) { if (c->getMaxSP() == 0) return Res.NOT_A_SPELL_CASTER; - } else if ((isCasting & 0x7f) == 1) { - switch (c->_class) { - case CLASS_ARCHER: - case CLASS_SORCERER: - category = 1; - break; - case CLASS_DRUID: - case CLASS_RANGER: - category = 2; - break; - case CLASS_PALADIN: - case CLASS_CLERIC: - default: - category = 0; - break; - } - + } else if ((mode & 0x7f) == 1) { if (c->getMaxSP() == 0) { return Res.NOT_A_SPELL_CASTER; } else { - for (int spellIndex = 0; spellIndex < MAX_SPELLS_PER_CLASS; ++spellIndex) { + for (int spellIndex = 0; spellIndex < SPELLS_PER_CLASS; ++spellIndex) { if (c->_spells[spellIndex]) { int spellId = Res.SPELLS_ALLOWED[category][spellIndex]; int gemCost = Res.SPELL_GEM_COST[spellId]; @@ -430,7 +394,7 @@ const char *SpellsDialog::setSpellText(Character *c, int isCasting) { } } - return nullptr; + return _spells.empty() ? Res.SPELLS_LEARNED_ALL : nullptr; } /*------------------------------------------------------------------------*/ @@ -460,6 +424,7 @@ int CastSpell::show(XeenEngine *vm) { Interface &intf = *vm->_interface; Party &party = *vm->_party; Spells &spells = *vm->_spells; + int result = 0, spellId = 0; int charNum; // Get which character is doing the casting @@ -476,23 +441,17 @@ int CastSpell::show(XeenEngine *vm) { } } + // Highlight the character Character *c = &party._activeParty[charNum]; - intf.highlightChar(charNum); - - return show(vm, c); -} + intf.highlightChar(c); -int CastSpell::show(XeenEngine *vm, Character *&c) { - Spells &spells = *vm->_spells; CastSpell *dlg = new CastSpell(vm); - int spellId; - int result = -1; - do { spellId = dlg->execute(c); if (g_vm->shouldExit() || spellId == -1) { - result = 0; + result = -1; + break; } else { result = spells.castSpell(c, (MagicSpell)spellId); } @@ -514,9 +473,10 @@ int CastSpell::execute(Character *&c) { bool redrawFlag = true; do { if (redrawFlag) { - int category = c->getClassCategory(); + SpellsCategory category = c->getSpellsCategory(); + int spellIndex = c->_currentSpell != -1 ? c->_currentSpell : 39; - spellId = Res.SPELLS_ALLOWED[category][spellIndex]; + spellId = (category == SPELLCAT_INVALID) ? NO_SPELL : Res.SPELLS_ALLOWED[category][spellIndex]; int gemCost = Res.SPELL_GEM_COST[spellId]; int spCost = spells.calcSpellPoints(spellId, c->getCurrentLevel()); @@ -553,6 +513,7 @@ int CastSpell::execute(Character *&c) { if (_buttonValue < (int)party._activeParty.size()) { c = &party._activeParty[_buttonValue]; intf.highlightChar(_buttonValue); + spells._lastCaster = _buttonValue; redrawFlag = true; break; } @@ -572,7 +533,7 @@ int CastSpell::execute(Character *&c) { case Common::KEYCODE_n: // Select new spell _vm->_mode = (Mode)_oldMode; - c = SpellsDialog::show(_vm, this, c, 1); + c = SpellsDialog::show(_vm, this, c, SPELLS_DIALOG_SELECT); redrawFlag = true; break; @@ -698,7 +659,7 @@ int SelectElement::execute(int spellId) { while (result == 999) { do { events.updateGameCounter(); - intf.draw3d(true); + intf.draw3d(true, false); w.frame(); w.writeString(Res.WHICH_ELEMENT2); drawButtons(&windows[0]); @@ -797,14 +758,14 @@ bool LloydsBeacon::execute() { Sound &sound = *_vm->_sound; Windows &windows = *_vm->_windows; Window &w = windows[10]; - bool isDarkCc = _vm->_files->_isDarkCc; + int ccNum = _vm->_files->_ccNum; Character &c = *combat._oldCharacter; loadButtons(); if (!c._lloydMap) { // No destination previously set, so have a default ready - if (isDarkCc) { + if (ccNum) { c._lloydSide = 1; c._lloydPosition = Common::Point(25, 21); c._lloydMap = 29; @@ -815,19 +776,14 @@ bool LloydsBeacon::execute() { } } - // Open up the text file for the destination map and read in it's name - File textFile(Common::String::format("%s%c%03d.txt", - c._lloydSide == 0 ? "xeen" : "dark", - c._lloydMap >= 100 ? 'x' : '0', - c._lloydMap)); - Common::String mapName = textFile.readString(); - textFile.close(); + // Get the destination map name + Common::String mapName = Map::getMazeName(c._lloydMap, c._lloydSide); // Display the dialog w.open(); - w.writeString(Common::String::format(Res.LLOYDS_BEACON, - mapName.c_str(), c._lloydPosition.x, c._lloydPosition.y)); - drawButtons(&windows[0]); + w.writeString(Common::String::format(Res.LLOYDS_BEACON, mapName.c_str(), + c._lloydPosition.x, c._lloydPosition.y)); + drawButtons(&w); w.update(); bool result = true; @@ -847,12 +803,13 @@ bool LloydsBeacon::execute() { switch (_buttonValue) { case Common::KEYCODE_r: - if (!isDarkCc && c._lloydMap >= 75 && c._lloydMap <= 78 && !party._cloudsEnd) { + if (!ccNum && c._lloydMap >= XEEN_CASTLE1 && c._lloydMap <= XEEN_CASTLE4 && party._cloudsCompleted) { + // Xeen's Castle has already been destroyed result = false; } else { sound.playFX(51); - map._loadDarkSide = isDarkCc; - if (c._lloydMap != party._mazeId || c._lloydSide != (isDarkCc ? 1 : 0)) { + if (c._lloydMap != party._mazeId || c._lloydSide != ccNum) { + map._loadCcNum = c._lloydSide; map.load(c._lloydMap); } @@ -867,7 +824,7 @@ bool LloydsBeacon::execute() { sound.playFX(20); c._lloydMap = party._mazeId; c._lloydPosition = party._mazePosition; - c._lloydSide = isDarkCc ? 1 : 0; + c._lloydSide = ccNum; _buttonValue = Common::KEYCODE_ESCAPE; break; @@ -932,10 +889,9 @@ int Teleport::execute() { break; } - v = map.mazeLookup(pt, map._isOutdoors ? 0xF : 0xFFFF, 0); + v = map.mazeLookup(pt, 0, map._isOutdoors ? 0xF : 0xFFFF); - if ((v != (map._isOutdoors ? 0 : INVALID_CELL)) && - (!map._isOutdoors || v == SURFTYPE_DWATER)) { + if ((v != (map._isOutdoors ? 0 : INVALID_CELL)) && (!map._isOutdoors || v != SURFTYPE_DWATER)) { party._mazePosition = pt; return 1; } else { @@ -961,20 +917,35 @@ int TownPortal::execute() { Mode oldMode = _vm->_mode; _vm->_mode = MODE_FF; - // Build up a lsit of the names of the towns on the current side of Xeen - for (int idx = 0; idx < 5; ++idx) { - File f(Common::String::format("%s%04d.txt", - map._sideTownPortal ? "dark" : "xeen", - Res.TOWN_MAP_NUMBERS[map._sideTownPortal][idx])); - townNames[idx] = f.readString(); - f.close(); + w.open(); + + if (_vm->getGameID() == GType_Swords) { + // Build up a lsit of the names of the towns on the current side of Xeen + for (int idx = 0; idx < 3; ++idx) { + Common::String txtName = Common::String::format("%s%04d.txt", "dark", Res.TOWN_MAP_NUMBERS[2][idx]); + File f(txtName, 1); + townNames[idx] = f.readString(); + f.close(); + } + + w.writeString(Common::String::format(Res.TOWN_PORTAL_SWORDS, townNames[0].c_str(), townNames[1].c_str(), + townNames[2].c_str())); + } else { + // Build up a lsit of the names of the towns on the current side of Xeen + for (int idx = 0; idx < 5; ++idx) { + Common::String txtName = Common::String::format("%s%04d.txt", map._sideTownPortal ? "dark" : "xeen", + Res.TOWN_MAP_NUMBERS[map._sideTownPortal][idx]); + File f(txtName, 1); + townNames[idx] = f.readString(); + f.close(); + } + + w.writeString(Common::String::format(Res.TOWN_PORTAL, + townNames[0].c_str(), townNames[1].c_str(), townNames[2].c_str(), + townNames[3].c_str(), townNames[4].c_str() + )); } - w.open(); - w.writeString(Common::String::format(Res.TOWN_PORTAL, - townNames[0].c_str(), townNames[1].c_str(), townNames[2].c_str(), - townNames[3].c_str(), townNames[4].c_str() - )); w.update(); // Get the town number @@ -983,7 +954,7 @@ int TownPortal::execute() { do { int result = Input::show(_vm, &w, num, 1, 160, true); townNumber = !result ? 0 : atoi(num.c_str()); - } while (townNumber > 5); + } while (townNumber > (_vm->getGameID() == GType_Swords ? 3 : 5)); w.close(); _vm->_mode = oldMode; @@ -1032,7 +1003,7 @@ void IdentifyMonster::execute() { do { events.updateGameCounter(); - intf.draw3d(false); + intf.draw3d(false, false); w.frame(); windows[3].update(); @@ -1042,4 +1013,62 @@ void IdentifyMonster::execute() { w.close(); } + +/*------------------------------------------------------------------------*/ + +void DetectMonsters::show(XeenEngine *vm) { + DetectMonsters *dlg = new DetectMonsters(vm); + dlg->execute(); + delete dlg; +} + +void DetectMonsters::execute() { + EventsManager &events = *_vm->_events; + Interface &intf = *_vm->_interface; + Map &map = *_vm->_map; + Party &party = *_vm->_party; + Resources &res = *_vm->_resources; + Sound &sound = *_vm->_sound; + Windows &windows = *_vm->_windows; + Window &w = windows[19]; + int ccNum = _vm->_files->_ccNum; + int grid[7][7]; + + SpriteResource sprites(ccNum ? "detectmn.icn" : "detctmon.icn"); + Common::fill(&grid[0][0], &grid[6][6], 0); + + w.open(); + w.writeString(Res.DETECT_MONSTERS); + sprites.draw(w, 0, Common::Point(243, 80)); + + for (int yDiff = 3; yDiff >= -3; --yDiff) { + for (int xDiff = -3; xDiff <= 3; ++xDiff) { + for (uint monIndex = 0; monIndex < map._mobData._monsters.size(); ++monIndex) { + MazeMonster &monster = map._mobData._monsters[monIndex]; + Common::Point pt = party._mazePosition + Common::Point(xDiff, yDiff); + if (monster._position == pt) { + int &gridEntry = grid[yDiff + 3][xDiff + 3]; + if (++gridEntry > 3) + gridEntry = 3; + + sprites.draw(w, gridEntry, Common::Point(271 + xDiff * 9, 102 - yDiff * 7)); + } + } + } + } + + res._globalSprites.draw(w, party._mazeDirection + 1, Common::Point(270, 101)); + sound.playFX(20); + w.update(); + + while (!g_vm->shouldExit() && !events.isKeyMousePressed()) { + events.updateGameCounter(); + intf.draw3d(true); + + events.wait(1, false); + } + + w.close(); +} + } // End of namespace Xeen diff --git a/engines/xeen/dialogs/dialogs_spells.h b/engines/xeen/dialogs/dialogs_spells.h index 2bcaef43e5..26c3f00428 100644 --- a/engines/xeen/dialogs/dialogs_spells.h +++ b/engines/xeen/dialogs/dialogs_spells.h @@ -29,6 +29,10 @@ namespace Xeen { +enum SpellDialogMode { + SPELLS_DIALOG_BUY = 0, SPELLS_DIALOG_SELECT = 1, SPELLS_DIALOG_INFO = 0x80 +}; + struct SpellEntry { Common::String _name; int _spellIndex; @@ -39,22 +43,41 @@ struct SpellEntry { _name(name), _spellIndex(spellIndex), _spellId(spellId), _color(9) {} }; +/** + * Spells list dialog. Used for both selecting spells to cast, as well as the + * spells listing when visiting Guild locations + */ class SpellsDialog : public ButtonContainer { private: SpriteResource _iconSprites; SpriteResource _scrollSprites; Common::Array<SpellEntry> _spells; + /** + * Constructor + */ SpellsDialog(XeenEngine *vm) : ButtonContainer(vm) {} - Character *execute(ButtonContainer *priorDialog, Character *c, int isCasting); + /** + * Executes the dialog + */ + Character *execute(ButtonContainer *priorDialog, Character *c, int mode); + /** + * Loads buttons for the dialog + */ void loadButtons(); + /** + * Sets the spell text + */ const char *setSpellText(Character *c, int isCasting); public: + /** + * Show the spells list dialog + */ static Character *show(XeenEngine *vm, ButtonContainer *priorDialog, - Character *c, int isCasting); + Character *c, SpellDialogMode mode); }; class CastSpell : public ButtonContainer { @@ -70,7 +93,6 @@ private: void loadButtons(); public: static int show(XeenEngine *vm); - static int show(XeenEngine *vm, Character *&c); }; class SpellOnWho : public ButtonContainer { @@ -146,6 +168,15 @@ public: static void show(XeenEngine *vm); }; +class DetectMonsters : public ButtonContainer { +private: + DetectMonsters(XeenEngine *vm) : ButtonContainer(vm) {} + + void execute(); +public: + static void show(XeenEngine *vm); +}; + } // End of namespace Xeen #endif /* XEEN_DIALOGS_SPELLS_H */ diff --git a/engines/xeen/events.cpp b/engines/xeen/events.cpp index 6ceef4406b..17306a32fc 100644 --- a/engines/xeen/events.cpp +++ b/engines/xeen/events.cpp @@ -31,9 +31,9 @@ namespace Xeen { -EventsManager::EventsManager(XeenEngine *vm) : _vm(vm), _playTime(0), - _frameCounter(0), _priorFrameCounterTime(0), _gameCounter(0), - _leftButton(false), _rightButton(false), _sprites("mouse.icn") { +EventsManager::EventsManager(XeenEngine *vm) : _vm(vm), _playTime(0), _gameCounter(0), + _frameCounter(0), _priorFrameCounterTime(0), _priorScreenRefreshTime(0), + _mousePressed(false), _sprites("mouse.icn") { Common::fill(&_gameCounters[0], &_gameCounters[6], 0); } @@ -62,7 +62,15 @@ bool EventsManager::isCursorVisible() { void EventsManager::pollEvents() { uint32 timer = g_system->getMillis(); + + if (timer >= (_priorScreenRefreshTime + SCREEN_UPDATE_TIME)) { + // Refresh the screen at a higher frame rate than the game's own frame rate + // to allow for more responsive mouse movement + _priorScreenRefreshTime = timer; + g_vm->_screen->update(); + } if (timer >= (_priorFrameCounterTime + GAME_FRAME_TIME)) { + // Time to build up next game frame _priorFrameCounterTime = timer; nextFrame(); } @@ -80,24 +88,24 @@ void EventsManager::pollEvents() { _vm->_debugger->attach(); _vm->_debugger->onFrame(); } else { - _keys.push(event.kbd); + addEvent(event.kbd); } break; case Common::EVENT_MOUSEMOVE: _mousePos = event.mouse; break; case Common::EVENT_LBUTTONDOWN: - _leftButton = true; - return; - case Common::EVENT_LBUTTONUP: - _leftButton = false; + _mousePressed = true; + addEvent(true, false); return; case Common::EVENT_RBUTTONDOWN: - _rightButton = true; + _mousePressed = true; + addEvent(false, true); return; + case Common::EVENT_LBUTTONUP: case Common::EVENT_RBUTTONUP: - _rightButton = false; - break; + _mousePressed = false; + return; default: break; } @@ -110,31 +118,38 @@ void EventsManager::pollEventsAndWait() { } void EventsManager::clearEvents() { - _keys.clear(); - _leftButton = _rightButton = false; - + _pendingEvents.clear(); + _mousePressed = false; } void EventsManager::debounceMouse() { - while (_leftButton && !_vm->shouldExit()) { + while (_mousePressed && !_vm->shouldExit()) { pollEventsAndWait(); } } -bool EventsManager::getKey(Common::KeyState &key) { - if (_keys.empty()) { + +void EventsManager::addEvent(const Common::KeyState &keyState) { + if (_pendingEvents.size() < MAX_PENDING_EVENTS) + _pendingEvents.push(PendingEvent(keyState)); +} + +void EventsManager::addEvent(bool leftButton, bool rightButton) { + if (_pendingEvents.size() < MAX_PENDING_EVENTS) + _pendingEvents.push(PendingEvent(leftButton, rightButton)); +} + + +bool EventsManager::getEvent(PendingEvent &pe) { + if (_pendingEvents.empty()) { return false; } else { - key = _keys.pop(); + pe = _pendingEvents.pop(); return true; } } -bool EventsManager::isKeyPending() const { - return !_keys.empty(); -} - bool EventsManager::isKeyMousePressed() { - bool result = _leftButton || _rightButton || isKeyPending(); + bool result = isEventPending(); debounceMouse(); clearEvents(); @@ -144,7 +159,7 @@ bool EventsManager::isKeyMousePressed() { bool EventsManager::wait(uint numFrames, bool interruptable) { while (!_vm->shouldExit() && timeElapsed() < numFrames) { pollEventsAndWait(); - if (interruptable && (_leftButton || _rightButton || isKeyPending())) + if (interruptable && isEventPending()) return true; } @@ -200,9 +215,4 @@ void EventsManager::nextFrame() { _vm->_screen->update(); } -/*------------------------------------------------------------------------*/ - -GameEvent::GameEvent() { -} - } // End of namespace Xeen diff --git a/engines/xeen/events.h b/engines/xeen/events.h index 0ef2c3a9e7..9913b2fbf1 100644 --- a/engines/xeen/events.h +++ b/engines/xeen/events.h @@ -32,30 +32,52 @@ namespace Xeen { #define GAME_FRAME_RATE (1000 / 50) #define GAME_FRAME_TIME 50 +#define SCREEN_UPDATE_TIME 10 +#define MAX_PENDING_EVENTS 5 class XeenEngine; +struct PendingEvent { + Common::KeyState _keyState; + bool _leftButton; + bool _rightButton; + + PendingEvent() : _leftButton(false), _rightButton(false) {} + PendingEvent(const Common::KeyState &keyState) : _keyState(keyState), _leftButton(false), _rightButton(false) {} + PendingEvent(bool leftButton, bool rightButton) : _leftButton(leftButton), _rightButton(rightButton) {} + + /** + * Returns true if a keyboard event is pending + */ + bool isKeyboard() const { return _keyState.keycode != Common::KEYCODE_INVALID; } + + /** + * Returns ture if a mouse button event is pending + */ + bool isMouse() const { return _leftButton || _rightButton; } +}; + class EventsManager { private: XeenEngine *_vm; uint32 _frameCounter; uint32 _priorFrameCounterTime; + uint32 _priorScreenRefreshTime; uint32 _gameCounter; uint32 _gameCounters[6]; uint32 _playTime; - Common::Queue<Common::KeyState> _keys; + Common::Queue<PendingEvent> _pendingEvents; SpriteResource _sprites; + bool _mousePressed; /** * Handles moving to the next game frame */ void nextFrame(); public: - bool _leftButton, _rightButton; Common::Point _mousePos; public: EventsManager(XeenEngine *vm); - ~EventsManager(); /* @@ -78,17 +100,45 @@ public: */ bool isCursorVisible(); + /** + * Polls the ScummVM backend for any pending events + */ void pollEvents(); + /** + * Polls for events, and wait a slight delay. This ensures the game doesn't use up 100% of the CPU + */ void pollEventsAndWait(); + /** + * Clears all pending events + */ void clearEvents(); + /** + * Waits for a mouse press to be released + */ void debounceMouse(); - bool getKey(Common::KeyState &key); + /** + * Adds a keyboard event to the queue + */ + void addEvent(const Common::KeyState &keyState); - bool isKeyPending() const; + /** + * Adds a mouse button event to the queue + */ + void addEvent(bool leftButton, bool rightButton); + + /** + * Returns the next pending key/mouse press, if any + */ + bool getEvent(PendingEvent &pe); + + /** + * Returns true if a key or mouse event is pending + */ + bool isEventPending() const { return !_pendingEvents.empty(); } /** * Returns true if a key or mouse press is pending @@ -141,11 +191,6 @@ public: void waitForPress(); }; -class GameEvent { -public: - GameEvent(); -}; - } // End of namespace Xeen #endif /* XEEN_EVENTS_H */ diff --git a/engines/xeen/files.cpp b/engines/xeen/files.cpp index 83a4ca9072..d18b5c4dd6 100644 --- a/engines/xeen/files.cpp +++ b/engines/xeen/files.cpp @@ -218,7 +218,7 @@ Common::SeekableReadStream *CCArchive::createReadStreamForMember(const Common::S /*------------------------------------------------------------------------*/ FileManager::FileManager(XeenEngine *vm) { - _isDarkCc = vm->getGameID() == GType_DarkSide; + _ccNum = vm->getGameID() == GType_DarkSide; File::_xeenCc = File::_darkCc = File::_introCc = nullptr; File::_xeenSave = File::_darkSave = nullptr; File::_currentSave = nullptr; @@ -277,7 +277,7 @@ void FileManager::setGameCc(int ccMode) { } File::setCurrentArchive(ccMode); - _isDarkCc = ccMode != 0; + _ccNum = ccMode != 0; } void FileManager::load(Common::SeekableReadStream &stream) { @@ -285,7 +285,7 @@ void FileManager::load(Common::SeekableReadStream &stream) { } void FileManager::save(Common::WriteStream &s) { - s.writeByte(_isDarkCc ? 1 : 0); + s.writeByte(_ccNum ? 1 : 0); } /*------------------------------------------------------------------------*/ @@ -330,7 +330,7 @@ bool File::open(const Common::String &filename, Common::Archive &archive) { bool File::open(const Common::String &filename, int ccMode) { FileManager &files = *g_vm->_files; - int oldMode = files._isDarkCc ? 1 : 0; + int oldNum = files._ccNum; files.setGameCc(ccMode); if (File::exists(filename, *_currentArchive)) @@ -338,7 +338,7 @@ bool File::open(const Common::String &filename, int ccMode) { else File::open(filename); - files.setGameCc(oldMode); + files.setGameCc(oldNum); return true; } @@ -390,11 +390,11 @@ bool File::exists(const Common::String &filename) { bool File::exists(const Common::String &filename, int ccMode) { FileManager &files = *g_vm->_files; - int oldMode = files._isDarkCc ? 1 : 0; + int oldNum = files._ccNum; files.setGameCc(ccMode); bool result = exists(filename); - files.setGameCc(oldMode); + files.setGameCc(oldNum); return result; } @@ -480,6 +480,7 @@ Common::SeekableReadStream *SaveArchive::createReadStreamForMember(uint16 id) co } void SaveArchive::load(Common::SeekableReadStream &stream) { + _newData.clear(); loadIndex(stream); delete[] _data; @@ -487,8 +488,10 @@ void SaveArchive::load(Common::SeekableReadStream &stream) { _data = new byte[_dataSize]; stream.seek(0); stream.read(_data, _dataSize); +} - // Load in the character stats and active party +void SaveArchive::loadParty() { + // Load in the character roster and active party Common::SeekableReadStream *chr = createReadStreamForMember("maze.chr"); Common::Serializer sChr(chr, nullptr); _party->_roster.synchronize(sChr); @@ -503,6 +506,7 @@ void SaveArchive::load(Common::SeekableReadStream &stream) { void SaveArchive::reset(CCArchive *src) { Common::MemoryWriteStreamDynamic saveFile(DisposeAfterUse::YES); File fIn; + _newData.clear(); g_vm->_files->setGameCc(g_vm->getGameID() == GType_DarkSide ? 1 : 0); const int RESOURCES[6] = { 0x2A0C, 0x2A1C, 0x2A2C, 0x2A3C, 0x284C, 0x2A5C }; diff --git a/engines/xeen/files.h b/engines/xeen/files.h index 306ec96657..0d421547fb 100644 --- a/engines/xeen/files.h +++ b/engines/xeen/files.h @@ -77,13 +77,13 @@ struct CCEntry { */ class FileManager { public: - bool _isDarkCc; + int _ccNum; public: /** * Constructor */ FileManager(XeenEngine *vm); - + /** * Destructor */ @@ -352,6 +352,11 @@ public: void save(Common::WriteStream &s); /** + * Load the character roster and party + */ + void loadParty(); + + /** * Sets a new resource entry */ void replaceEntry(uint16 id, const byte *data, size_t size); @@ -373,7 +378,7 @@ public: /** * Finishes any pending writes, pushing out the written data */ - void finalize(); + void finalize() override; /** * Writes data diff --git a/engines/xeen/font.cpp b/engines/xeen/font.cpp index 58381e1516..d5f05bac0a 100644 --- a/engines/xeen/font.cpp +++ b/engines/xeen/font.cpp @@ -218,7 +218,7 @@ const char *FontSurface::writeString(const Common::String &s, const Common::Rect } else if (c == 10) { // Newline if (newLine(bounds)) - break; + return _displayString; } else if (c == 11) { // Set y position int yp = fontAtoi(); @@ -330,6 +330,7 @@ void FontSurface::writeChar(char c, const Common::Rect &clipRect) { int y = _writePos.y; if (c == 'g' || c == 'p' || c == 'q' || c == 'y') ++y; + int yStart = y; // Get pointers into font data and surface to write pixels to int charIndex = (int)c + (_fontReduced ? 0x80 : 0); @@ -354,8 +355,8 @@ void FontSurface::writeChar(char c, const Common::Rect &clipRect) { } } - addDirtyRect(Common::Rect(_writePos.x, _writePos.y, _writePos.x + FONT_WIDTH, - _writePos.y + FONT_HEIGHT)); + addDirtyRect(Common::Rect(_writePos.x, yStart, _writePos.x + FONT_WIDTH, + yStart + FONT_HEIGHT)); _writePos.x += _fontData[0x1000 + charIndex]; } diff --git a/engines/xeen/interface.cpp b/engines/xeen/interface.cpp index 3d9f05b5a2..82a2cbd7d1 100644 --- a/engines/xeen/interface.cpp +++ b/engines/xeen/interface.cpp @@ -103,6 +103,7 @@ void PartyDrawer::drawParty(bool updateFlag) { void PartyDrawer::highlightChar(int charId) { Resources &res = *_vm->_resources; Windows &windows = *_vm->_windows; + assert(charId < MAX_ACTIVE_PARTY); if (charId != _hiliteChar && _hiliteChar != HILIGHT_CHAR_DISABLED) { // Handle deselecting any previusly selected char @@ -118,6 +119,12 @@ void PartyDrawer::highlightChar(int charId) { } } +void PartyDrawer::highlightChar(const Character *c) { + int charNum = _vm->_party->_activeParty.indexOf(*c); + if (charNum != -1) + highlightChar(charNum); +} + void PartyDrawer::unhighlightChar() { Resources &res = *_vm->_resources; Windows &windows = *_vm->_windows; @@ -156,6 +163,7 @@ Interface::Interface(XeenEngine *vm) : ButtonContainer(vm), InterfaceScene(vm), _upDoorText = false; _tillMove = 0; Common::fill(&_charFX[0], &_charFX[MAX_ACTIVE_PARTY], 0); + _waitBounds = Common::Rect(8, 8, 224, 140); } void Interface::setup() { @@ -202,7 +210,7 @@ void Interface::mainIconsPrint() { Windows &windows = *_vm->_windows; windows[38].close(); windows[12].close(); - + res._globalSprites.draw(0, 7, Common::Point(232, 74)); drawButtons(&windows[0]); windows[34].update(); @@ -255,23 +263,22 @@ void Interface::perform() { Party &party = *_vm->_party; Scripts &scripts = *_vm->_scripts; Sound &sound = *_vm->_sound; - Spells &spells = *_vm->_spells; - const Common::Rect WAIT_BOUNDS(8, 8, 224, 140); - events.updateGameCounter(); - draw3d(true); - - // Wait for a frame or a user event do { - events.pollEventsAndWait(); - checkEvents(_vm); + // Draw the next frame + events.updateGameCounter(); + draw3d(true); - if (events._leftButton && WAIT_BOUNDS.contains(events._mousePos)) - _buttonValue = Common::KEYCODE_SPACE; - } while (!_buttonValue && events.timeElapsed() < 1 && !_vm->_party->_partyDead); + // Wait for a frame or a user event + _buttonValue = 0; + do { + events.pollEventsAndWait(); + if (g_vm->shouldExit() || g_vm->isLoadPending() || party._dead) + return; - if (!_buttonValue && !_vm->_party->_partyDead) - return; + checkEvents(g_vm); + } while (!_buttonValue && events.timeElapsed() < 1); + } while (!_buttonValue); if (_buttonValue == Common::KEYCODE_SPACE) { int lookupId = map.mazeLookup(party._mazePosition, @@ -281,28 +288,24 @@ void Interface::perform() { switch (lookupId) { case 1: if (!map._isOutdoors) { - scripts.openGrate(13, 1); - eventsFlag = _buttonValue != 0; + eventsFlag = !scripts.openGrate(13, 1); } break; case 6: // Open grate being closed if (!map._isOutdoors) { - scripts.openGrate(9, 0); - eventsFlag = _buttonValue != 0; + eventsFlag = !scripts.openGrate(9, 0); } break; case 9: // Closed grate being opened if (!map._isOutdoors) { - scripts.openGrate(6, 0); - eventsFlag = _buttonValue != 0; + eventsFlag = !scripts.openGrate(6, 0); } break; case 13: if (!map._isOutdoors) { - scripts.openGrate(1, 1); - eventsFlag = _buttonValue != 0; + eventsFlag = !scripts.openGrate(1, 1); } break; default: @@ -312,6 +315,8 @@ void Interface::perform() { scripts.checkEvents(); if (_vm->shouldExit()) return; + } else { + clearEvents(); } } @@ -460,6 +465,13 @@ void Interface::perform() { } break; + case (Common::KBD_CTRL << 16) | Common::KEYCODE_DOWN: + party._mazeDirection = (Direction)((int)party._mazeDirection ^ 2); + _flipSky = !_flipSky; + _isAnimReset = true; + stepTime(); + break; + case Common::KEYCODE_F1: case Common::KEYCODE_F2: case Common::KEYCODE_F3: @@ -508,25 +520,18 @@ void Interface::perform() { } break; - case Common::KEYCODE_c: { + case Common::KEYCODE_c: // Cast spell if (_tillMove) { combat.moveMonsters(); draw3d(true); } - Character *c = &party._activeParty[(spells._lastCaster < 0 || - spells._lastCaster >= (int)party._activeParty.size()) ? - (int)party._activeParty.size() - 1 : spells._lastCaster]; - - int result = CastSpell::show(_vm, c); - - if (result == 1) { + if (CastSpell::show(_vm) != -1) { chargeStep(); doStepCode(); } break; - } case Common::KEYCODE_i: // Show Info dialog @@ -562,7 +567,7 @@ void Interface::perform() { if (combat._attackMonsters[0] != -1 || combat._attackMonsters[1] != -1 || combat._attackMonsters[2] != -1) { - if ((_vm->_mode == MODE_1 || _vm->_mode == MODE_SLEEPING) + if ((_vm->_mode == MODE_INTERACTIVE || _vm->_mode == MODE_SLEEPING) && !combat._monstersAttacking && !_charsShooting) { doCombat(); } @@ -585,7 +590,7 @@ void Interface::perform() { } void Interface::chargeStep() { - if (!_vm->_party->_partyDead) { + if (!_vm->_party->_dead) { _vm->_party->changeTime(_vm->_map->_isOutdoors ? 10 : 1); if (_tillMove) { _vm->_combat->moveMonsters(); @@ -628,7 +633,7 @@ void Interface::doStepCode() { switch (surfaceId) { case SURFTYPE_SPACE: // Wheeze.. can't breathe in space! Explosive decompression, here we come - party._partyDead = true; + party._dead = true; break; case SURFTYPE_LAVA: // It burns, it burns! @@ -657,7 +662,7 @@ void Interface::doStepCode() { break; } - if (_vm->_files->_isDarkCc && party._gameFlags[1][118]) { + if (_vm->getGameID() != GType_Swords && _vm->_files->_ccNum && party._gameFlags[1][118]) { _falling = FALL_NONE; } else { if (_falling != FALL_NONE) @@ -678,7 +683,7 @@ void Interface::doStepCode() { combat._combatTarget = oldTarget; _flipGround = !_flipGround; - } else if (party._partyDead) { + } else if (party._dead) { draw3d(true); } } @@ -688,9 +693,9 @@ void Interface::startFalling(bool flag) { Combat &combat = *_vm->_combat; Map &map = *_vm->_map; Party &party = *_vm->_party; - bool isDarkCc = _vm->_files->_isDarkCc; + int ccNum = _vm->_files->_ccNum; - if (isDarkCc && party._gameFlags[1][118]) { + if (ccNum && party._gameFlags[1][118]) { _falling = FALL_NONE; return; } @@ -700,10 +705,10 @@ void Interface::startFalling(bool flag) { _falling = FALL_START; draw3d(false); - if (flag && (!isDarkCc || party._fallMaze != 0)) { + if (flag && (!ccNum || party._fallMaze != 0)) { party._mazeId = party._fallMaze; party._mazePosition = party._fallPosition; - } else if (!isDarkCc) { + } else if (!ccNum) { switch (party._mazeId - 25) { case 0: case 26: @@ -768,8 +773,8 @@ void Interface::startFalling(bool flag) { break; } } else { - if (party._mazeId > 89 && party._mazeId < 113) { - party._mazeId += 168; + if (party._mazeId > 88 && party._mazeId < 114) { + party._mazeId -= 88; } else { switch (party._mazeId - 25) { case 0: @@ -848,7 +853,7 @@ void Interface::startFalling(bool flag) { break; case 103: case 104: - map._loadDarkSide = false; + map._loadCcNum = 0; party._mazeId = 8; party._mazePosition = Common::Point(11, 15); party._mazeDirection = DIR_NORTH; @@ -893,11 +898,18 @@ void Interface::startFalling(bool flag) { } bool Interface::checkMoveDirection(int key) { + Debugger &debugger = *g_vm->_debugger; Map &map = *_vm->_map; Party &party = *_vm->_party; Sound &sound = *_vm->_sound; - Direction dir = party._mazeDirection; + // If intangibility is turned on in the debugger, allow any movement + if (debugger._intangible) + return true; + + // For strafing or moving backwards, temporarily move to face the direction being checked, + // since the call to getCell will the adjacent cell details in the direction being faced + Direction dir = party._mazeDirection; switch (key) { case (Common::KBD_CTRL << 16) | Common::KEYCODE_LEFT: party._mazeDirection = (party._mazeDirection == DIR_NORTH) ? DIR_WEST : @@ -914,16 +926,19 @@ bool Interface::checkMoveDirection(int key) { break; } + // Get next facing tile information map.getCell(7); + int startSurfaceId = map._currentSurfaceId; int surfaceId; if (map._isOutdoors) { + // Reset direction back to original facing, if it was changed for strafing checks party._mazeDirection = dir; switch (map._currentWall) { case 5: - if (_vm->_files->_isDarkCc) + if (_vm->_files->_ccNum) goto check; // fall through @@ -965,18 +980,16 @@ bool Interface::checkMoveDirection(int key) { } } else { surfaceId = map.getCell(2); + + // Reset direction back to original facing, if it was changed for strafing checks + party._mazeDirection = dir; + if (surfaceId >= map.mazeData()._difficulties._wallNoPass) { - party._mazeDirection = dir; sound.playFX(46); return false; } else { - party._mazeDirection = dir; - - if (startSurfaceId == SURFTYPE_SWAMP || party.checkSkill(SWIMMING) || + if (startSurfaceId != SURFTYPE_SWAMP || party.checkSkill(SWIMMING) || party._walkOnWaterActive) { - sound.playFX(46); - return false; - } else { if (_buttonValue == Common::KEYCODE_UP && _wo[107]) { _openDoor = true; sound.playFX(47); @@ -984,6 +997,9 @@ bool Interface::checkMoveDirection(int key) { _openDoor = false; } return true; + } else { + sound.playFX(46); + return false; } } } @@ -998,7 +1014,7 @@ void Interface::rest() { map.cellFlagLookup(party._mazePosition); if ((map._currentCantRest || (map.mazeData()._mazeFlags & RESTRICTION_REST)) - && _vm->_mode != MODE_12) { + && _vm->_mode != MODE_INTERACTIVE2) { ErrorScroll::show(_vm, Res.TOO_DANGEROUS_TO_REST, WT_NONFREEZED_WAIT); } else { // Check whether any character is in danger of dying @@ -1024,14 +1040,14 @@ void Interface::rest() { Mode oldMode = _vm->_mode; _vm->_mode = MODE_SLEEPING; - if (oldMode == MODE_12) { + if (oldMode == MODE_INTERACTIVE2) { party.changeTime(8 * 60); } else { for (int idx = 0; idx < 10; ++idx) { chargeStep(); draw3d(true); - if (_vm->_mode == MODE_1) { + if (_vm->_mode == MODE_INTERACTIVE) { _vm->_mode = oldMode; return; } @@ -1068,6 +1084,10 @@ void Interface::rest() { c._conditions[UNCONSCIOUS] = 0; c._currentHp = c.getMaxHP(); c._currentSp = c.getMaxSP(); + + // WORKAROUND: Resting curing weakness only originally worked due to a bug in changeTime + // resetting WEAK if party wasn't drunk. With that resolved, we have to reset WEAK here + c._conditions[WEAK] = 0; } } } @@ -1189,7 +1209,7 @@ void Interface::draw3d(bool updateFlag, bool pauseFlag) { _flipUIFrame = (_flipUIFrame + 1) % 4; if (_flipUIFrame == 0) _flipWater = !_flipWater; - if (_tillMove && (_vm->_mode == MODE_1 || _vm->_mode == MODE_COMBAT) && + if (_tillMove && (_vm->_mode == MODE_INTERACTIVE || _vm->_mode == MODE_COMBAT) && !combat._monstersAttacking && combat._moveMonsters) { if (--_tillMove == 0) combat.moveMonsters(); @@ -1225,7 +1245,7 @@ void Interface::draw3d(bool updateFlag, bool pauseFlag) { if (combat._attackMonsters[0] != -1 || combat._attackMonsters[1] != -1 || combat._attackMonsters[2] != -1) { - if ((_vm->_mode == MODE_1 || _vm->_mode == MODE_SLEEPING) && + if ((_vm->_mode == MODE_INTERACTIVE || _vm->_mode == MODE_SLEEPING) && !combat._monstersAttacking && !_charsShooting && combat._moveMonsters) { doCombat(); if (scripts._eventSkipped) @@ -1357,9 +1377,9 @@ void Interface::assembleBorder() { _face2UIFrame = (_face2UIFrame + 1) % 4 + 12; if (_face2State == 0) - _face2UIFrame += 252; + _face2UIFrame -= 3; else if (_face2State == 2) - _face2UIFrame = 0; + _face2UIFrame = 8; if (!_vm->_party->_clairvoyanceActive) { _face1UIFrame = 0; @@ -1422,8 +1442,7 @@ void Interface::assembleBorder() { // Draw direction character if direction sense is active if (_vm->_party->checkSkill(DIRECTION_SENSE) && !_vm->_noDirectionSense) { const char *dirText = Res.DIRECTION_TEXT_UPPER[_vm->_party->_mazeDirection]; - Common::String msg = Common::String::format( - "\002""08\003""c\013""139\011""116%c\014""d\001", *dirText); + Common::String msg = Common::String::format("\x2\f08\x3""c\v139\t116%c\fd\x1", *dirText); windows[0].writeString(msg); } @@ -1438,11 +1457,11 @@ void Interface::doCombat() { Map &map = *_vm->_map; Party &party = *_vm->_party; Scripts &scripts = *_vm->_scripts; - Spells &spells = *_vm->_spells; Sound &sound = *_vm->_sound; Windows &windows = *_vm->_windows; bool upDoorText = _upDoorText; bool reloadMap = false; + int index = 0; _upDoorText = false; combat._combatMode = COMBATMODE_2; @@ -1454,7 +1473,6 @@ void Interface::doCombat() { mainIconsPrint(); combat._combatParty.clear(); - combat._charsGone.clear(); combat.clearBlocked(); combat._pow[0]._duration = 0; combat._pow[1]._duration = 0; @@ -1467,9 +1485,8 @@ void Interface::doCombat() { combat.setSpeedTable(); // Initialize arrays for character/monster states - combat._charsGone.resize(combat._speedTable.size()); - Common::fill(&combat._charsGone[0], &combat._charsGone[0] + combat._speedTable.size(), 0); - Common::fill(&combat._charsBlocked[0], &combat._charsBlocked[0] + combat._speedTable.size(), false); + Common::fill(&combat._charsGone[0], &combat._charsGone[PARTY_AND_MONSTERS], 0); + Common::fill(&combat._charsBlocked[0], &combat._charsBlocked[PARTY_AND_MONSTERS], false); combat._whosSpeed = -1; combat._whosTurn = -1; @@ -1489,18 +1506,24 @@ void Interface::doCombat() { w.open(); bool breakFlag = false; - while (!_vm->shouldExit() && !breakFlag) { + while (!_vm->shouldExit() && !breakFlag && !party._dead && _vm->_mode == MODE_COMBAT) { + // FIXME: I've had a rare issue where the loop starts with a non-party _whosTurn. Unfortunately, + // I haven't been able to consistently replicate and diagnose the problem, so for now, + // I'm simply detecting if it happens and resetting the combat round + if (combat._whosTurn >= (int)party._activeParty.size()) + goto new_round; + highlightChar(combat._whosTurn); combat.setSpeedTable(); // Write out the description of the monsters being battled w.writeString(combat.getMonsterDescriptions()); _combatIcons.draw(0, 32, Common::Point(233, combat._attackDurationCtr * 10 + 27), - SPRFLAG_800, 1); + SPRFLAG_800, 0); w.update(); // Wait for keypress - int index = 0; + index = 0; do { events.updateGameCounter(); draw3d(true); @@ -1517,7 +1540,7 @@ void Interface::doCombat() { } while (!_vm->shouldExit() && events.timeElapsed() < 1 && !_buttonValue); } while (!_vm->shouldExit() && !_buttonValue); if (_vm->shouldExit()) - return; + goto exit; switch (_buttonValue) { case Common::KEYCODE_TAB: @@ -1554,13 +1577,10 @@ void Interface::doCombat() { case Common::KEYCODE_c: { // Cast spell - int spellId = CastSpell::show(_vm); - if (spellId != -1) { - Character *c = combat._combatParty[combat._whosTurn]; - spells.castSpell(c, (MagicSpell)spellId); + if (CastSpell::show(_vm) != -1) { nextChar(); } else { - highlightChar(combat._combatParty[combat._whosTurn]->_rosterId); + highlightChar(combat._whosTurn); } break; } @@ -1594,7 +1614,7 @@ void Interface::doCombat() { combat.run(); nextChar(); - if (_vm->_mode == MODE_1) { + if (_vm->_mode == MODE_INTERACTIVE) { party._treasure._gems = 0; party._treasure._gold = 0; party._treasure._hasItems = false; @@ -1649,7 +1669,8 @@ void Interface::doCombat() { // Handling for if the combat turn is complete if (combat.allHaveGone()) { - Common::fill(&combat._charsGone[0], &combat._charsGone[0] + combat._charsGone.size(), false); +new_round: + Common::fill(&combat._charsGone[0], &combat._charsGone[PARTY_AND_MONSTERS], false); combat.clearBlocked(); combat.setSpeedTable(); combat._whosTurn = -1; @@ -1681,11 +1702,9 @@ void Interface::doCombat() { } party.checkPartyDead(); - if (party._dead || _vm->_mode != MODE_COMBAT) - break; } - _vm->_mode = MODE_1; + _vm->_mode = MODE_INTERACTIVE; if (combat._partyRan && (combat._attackMonsters[0] != -1 || combat._attackMonsters[1] != -1 || combat._attackMonsters[2] != -1)) { party.checkPartyDead(); @@ -1699,14 +1718,14 @@ void Interface::doCombat() { } } } - +exit: w.close(); events.clearEvents(); _vm->_mode = MODE_COMBAT; draw3d(true); party.giveTreasure(); - _vm->_mode = MODE_1; + _vm->_mode = MODE_INTERACTIVE; party._stepped = true; unhighlightChar(); @@ -1719,21 +1738,23 @@ void Interface::doCombat() { mainIconsPrint(); combat._monster2Attack = -1; - if (upDoorText) { - map.cellFlagLookup(party._mazePosition); - if (map._currentIsEvent) - scripts.checkEvents(); - } + if (!g_vm->isLoadPending()) { + if (upDoorText) { + map.cellFlagLookup(party._mazePosition); + if (map._currentIsEvent) + scripts.checkEvents(); + } - if (reloadMap) { - sound.playFX(51); - map._loadDarkSide = _vm->getGameID() != GType_WorldOfXeen; - map.load(_vm->getGameID() == GType_WorldOfXeen ? 28 : 29); - party._mazeDirection = _vm->getGameID() == GType_WorldOfXeen ? - DIR_EAST : DIR_SOUTH; + if (reloadMap) { + sound.playFX(51); + map._loadCcNum = _vm->getGameID() != GType_WorldOfXeen ? 1 : 0; + map.load(_vm->getGameID() == GType_WorldOfXeen ? 28 : 29); + party._mazeDirection = _vm->getGameID() == GType_WorldOfXeen ? + DIR_EAST : DIR_SOUTH; + } } - combat._combatMode = COMBATMODE_1; + combat._combatMode = COMBATMODE_INTERACTIVE; } void Interface::nextChar() { @@ -1744,7 +1765,7 @@ void Interface::nextChar() { return; if ((combat._attackMonsters[0] == -1 && combat._attackMonsters[1] == -1 && combat._attackMonsters[2] == -1) || combat._combatParty.size() == 0) { - _vm->_mode = MODE_1; + _vm->_mode = MODE_INTERACTIVE; return; } @@ -1754,7 +1775,7 @@ void Interface::nextChar() { // Check if party is dead party.checkPartyDead(); if (party._dead) { - _vm->_mode = MODE_1; + _vm->_mode = MODE_INTERACTIVE; break; } @@ -1788,7 +1809,7 @@ void Interface::nextChar() { combat.setSpeedTable(); combat._whosTurn = -1; combat._whosSpeed = -1; - Common::fill(&combat._charsGone[0], &combat._charsGone[0] + combat._charsGone.size(), 0); + Common::fill(&combat._charsGone[0], &combat._charsGone[PARTY_AND_MONSTERS], false); continue; } diff --git a/engines/xeen/interface.h b/engines/xeen/interface.h index bbc2a77f1e..5639171c40 100644 --- a/engines/xeen/interface.h +++ b/engines/xeen/interface.h @@ -71,8 +71,18 @@ public: void drawParty(bool updateFlag); + /** + * Highlights the specified character in the party display at the bottom of the screen + * @param charId Character number + */ void highlightChar(int charId); + /** + * Highlights the specified character in the party display at the bottom of the screen + * @param c Character to highlight + */ + void highlightChar(const Character *c); + void unhighlightChar(); void resetHighlight(); @@ -154,7 +164,6 @@ private: void nextChar(); public: Obscurity _obscurity; - Common::String _interfaceText; FallState _falling; int _face1State, _face2State; int _face1UIFrame, _face2UIFrame; diff --git a/engines/xeen/interface_minimap.cpp b/engines/xeen/interface_minimap.cpp index 5ab10696bc..5805047d9e 100644 --- a/engines/xeen/interface_minimap.cpp +++ b/engines/xeen/interface_minimap.cpp @@ -141,7 +141,7 @@ void InterfaceMinimap::drawIndoorsMinimap() { } // Draw the specific surface type for each cell - for (int yp = MINIMAP_YSTART + (TILE_HEIGHT / 2), mazeY = pt.y + MINIMAP_DIFF; + for (int yp = MINIMAP_YSTART + (TILE_HEIGHT / 2) + 1, mazeY = pt.y + MINIMAP_DIFF; mazeY >= (pt.y - MINIMAP_DIFF); yp += TILE_HEIGHT, --mazeY) { for (int xp = MINIMAP_XSTART + (TILE_WIDTH / 2), mazeX = pt.x - MINIMAP_DIFF; mazeX <= (pt.x + MINIMAP_DIFF); xp += TILE_WIDTH, ++mazeX) { @@ -161,13 +161,13 @@ void InterfaceMinimap::drawIndoorsMinimap() { (map._currentSteppedOn || party._wizardEyeActive)) { map._tileSprites.draw(1, map.mazeData()._surfaceTypes[map._currentSurfaceId] + 36, - Common::Point(MINIMAP_XSTART - (TILE_WIDTH / 2), + Common::Point(MINIMAP_XSTART - (TILE_WIDTH / 2), MINIMAP_YSTART - (TILE_HEIGHT / 2) + 1)); } // Handle drawing surface sprites partially clipped at the left edge - for (int yp = MINIMAP_YSTART, mazeY = pt.y + MINIMAP_DIFF; mazeY >= (pt.y - MINIMAP_DIFF); - yp += TILE_HEIGHT, --mazeY) { + for (int yp = MINIMAP_YSTART + (TILE_HEIGHT / 2) + 1, mazeY = pt.y + MINIMAP_DIFF; + mazeY >= (pt.y - MINIMAP_DIFF); yp += TILE_HEIGHT, --mazeY) { v = map.mazeLookup(Common::Point(pt.x - MINIMAP_DIFF - 1, mazeY), 0, 0xffff); if (v != INVALID_CELL && map._currentSurfaceId && @@ -192,7 +192,7 @@ void InterfaceMinimap::drawIndoorsMinimap() { } // Handle drawing partially clip top row and left column - for (int xp = MINIMAP_XSTART, yp = MINIMAP_YSTART + (MINIMAP_SIZE - 1) * TILE_HEIGHT, + for (int xp = MINIMAP_XSTART, yp = MINIMAP_YSTART + (MINIMAP_SIZE - 1) * TILE_HEIGHT, mazeX = pt.x - MINIMAP_DIFF, mazeY = pt.y + MINIMAP_DIFF; mazeX <= (pt.x - MINIMAP_DIFF); xp += TILE_WIDTH, yp -= TILE_HEIGHT, ++mazeX, --mazeY) { diff --git a/engines/xeen/interface_scene.cpp b/engines/xeen/interface_scene.cpp index 51cb6d8b13..0fdf867448 100644 --- a/engines/xeen/interface_scene.cpp +++ b/engines/xeen/interface_scene.cpp @@ -63,11 +63,11 @@ OutdoorDrawList::OutdoorDrawList() : _sky1(_data[0]), _sky2(_data[1]), _data[25] = DrawStruct(0, 8, 109); _data[26] = DrawStruct(0, 201, 109); _data[27] = DrawStruct(0, 8, 109); - _data[28] = DrawStruct(1, -64, 61, 14, SPRFLAG_SCENE_CLIPPED); + _data[28] = DrawStruct(1, -64, 61, 14); _data[29] = DrawStruct(1, -40, 61, 14, 0); _data[30] = DrawStruct(1, -16, 61, 14, 0); _data[31] = DrawStruct(1, 8, 61, 14, 0); - _data[32] = DrawStruct(1, 128, 61, 14, SPRFLAG_HORIZ_FLIPPED | SPRFLAG_SCENE_CLIPPED); + _data[32] = DrawStruct(1, 128, 61, 14, SPRFLAG_HORIZ_FLIPPED); _data[33] = DrawStruct(1, 104, 61, 14, SPRFLAG_HORIZ_FLIPPED); _data[34] = DrawStruct(1, 80, 61, 14, SPRFLAG_HORIZ_FLIPPED); _data[35] = DrawStruct(1, 56, 61, 14, SPRFLAG_HORIZ_FLIPPED); @@ -123,10 +123,10 @@ OutdoorDrawList::OutdoorDrawList() : _sky1(_data[0]), _sky2(_data[1]), _data[85] = DrawStruct(2, 146, 40, 0, SPRFLAG_HORIZ_FLIPPED); _data[86] = DrawStruct(1, 32, 40, 6, 0); _data[87] = DrawStruct(0, -7, 30, 7, 0); - _data[88] = DrawStruct(0, -112, 30, 7, SPRFLAG_SCENE_CLIPPED); - _data[89] = DrawStruct(0, 98, 30, 7, SPRFLAG_SCENE_CLIPPED); - _data[90] = DrawStruct(0, -112, 30, 8, SPRFLAG_SCENE_CLIPPED); - _data[91] = DrawStruct(0, 98, 30, 8, SPRFLAG_SCENE_CLIPPED); + _data[88] = DrawStruct(0, -112, 30, 7); + _data[89] = DrawStruct(0, 98, 30, 7); + _data[90] = DrawStruct(0, -112, 30, 8); + _data[91] = DrawStruct(0, 98, 30, 8); _data[92] = DrawStruct(0, -38, 30, 8, 0); _data[93] = DrawStruct(0, 25, 30, 8, 0); _data[94] = DrawStruct(0, -7, 30, 8, 0); @@ -141,22 +141,22 @@ OutdoorDrawList::OutdoorDrawList() : _sky1(_data[0]), _sky2(_data[1]), _data[103] = DrawStruct(0, 8, 24); _data[104] = DrawStruct(0, 169, 24, 0, SPRFLAG_HORIZ_FLIPPED); _data[105] = DrawStruct(1, 32, 24); - _data[106] = DrawStruct(0, -23, 40, 0, SPRFLAG_SCENE_CLIPPED); - _data[107] = DrawStruct(0, 200, 40, 0, SPRFLAG_HORIZ_FLIPPED | SPRFLAG_SCENE_CLIPPED); + _data[106] = DrawStruct(0, -23, 40, 0); + _data[107] = DrawStruct(0, 200, 40, 0, SPRFLAG_HORIZ_FLIPPED); _data[108] = DrawStruct(0, 8, 47); _data[109] = DrawStruct(0, 169, 47, 0, SPRFLAG_HORIZ_FLIPPED); - _data[110] = DrawStruct(1, -56, -4, SCALE_ENLARGE, SPRFLAG_BOTTOM_CLIPPED | SPRFLAG_SCENE_CLIPPED); - _data[111] = DrawStruct(0, -5, 2, 0, SPRFLAG_BOTTOM_CLIPPED | SPRFLAG_SCENE_CLIPPED); - _data[112] = DrawStruct(0, -67, 2, 0, SPRFLAG_BOTTOM_CLIPPED | SPRFLAG_SCENE_CLIPPED); + _data[110] = DrawStruct(1, -56, -4, SCALE_ENLARGE, SPRFLAG_BOTTOM_CLIPPED); + _data[111] = DrawStruct(0, -5, 2, 0, SPRFLAG_BOTTOM_CLIPPED); + _data[112] = DrawStruct(0, -67, 2, 0, SPRFLAG_BOTTOM_CLIPPED); _data[113] = DrawStruct(0, 44, 73); _data[114] = DrawStruct(0, 44, 73); - _data[115] = DrawStruct(0, 58, 14, 0, SPRFLAG_BOTTOM_CLIPPED | SPRFLAG_SCENE_CLIPPED); + _data[115] = DrawStruct(0, 58, 14, 0, SPRFLAG_BOTTOM_CLIPPED); _data[116] = DrawStruct(0, 169, 73); _data[117] = DrawStruct(0, 169, 73); - _data[118] = DrawStruct(0, -5, 14, 0, SPRFLAG_BOTTOM_CLIPPED | SPRFLAG_SCENE_CLIPPED); + _data[118] = DrawStruct(0, -5, 14, 0, SPRFLAG_BOTTOM_CLIPPED); _data[119] = DrawStruct(0, 110, 73); _data[120] = DrawStruct(0, 110, 73); - _data[121] = DrawStruct(0, -5, 14, 0, SPRFLAG_BOTTOM_CLIPPED | SPRFLAG_SCENE_CLIPPED); + _data[121] = DrawStruct(0, -5, 14, 0, SPRFLAG_BOTTOM_CLIPPED); _data[122] = DrawStruct(0, 110, 73); _data[123] = DrawStruct(0, 110, 73); _data[124] = DrawStruct(0, 72, 43); @@ -167,6 +167,18 @@ OutdoorDrawList::OutdoorDrawList() : _sky1(_data[0]), _sky2(_data[1]), _data[129] = DrawStruct(0, 47, 36, 0, SPRFLAG_HORIZ_FLIPPED); _data[130] = DrawStruct(0, 118, 42); _data[131] = DrawStruct(0, 26, 42, 0, SPRFLAG_HORIZ_FLIPPED); + + for (int idx = 0; idx < 132; ++idx) + _data[idx]._flags |= SPRFLAG_SCENE_CLIPPED; +} + +void OutdoorDrawList::draw() { + // Mark all items to be drawn as being clipped to the scene area + for (int idx = 0; idx < size(); ++idx) + _data[idx]._flags |= SPRFLAG_SCENE_CLIPPED; + + // Draw the list + (*g_vm->_windows)[3].drawList(_data, size()); } /*------------------------------------------------------------------------*/ @@ -283,16 +295,16 @@ IndoorDrawList::IndoorDrawList() : _data[84] = DrawStruct(0, 71, 53, 12, SPRFLAG_HORIZ_FLIPPED); _data[85] = DrawStruct(0, 80, 57, 12, 0); _data[86] = DrawStruct(0, 64, 57, 12, SPRFLAG_HORIZ_FLIPPED); - _data[87] = DrawStruct(7, -24, 52, 0, SPRFLAG_SCENE_CLIPPED); + _data[87] = DrawStruct(7, -24, 52, 0); _data[88] = DrawStruct(7, 32, 52); _data[89] = DrawStruct(7, 88, 52); _data[90] = DrawStruct(0, 144, 52); - _data[91] = DrawStruct(0, 200, 52, 0, SPRFLAG_SCENE_CLIPPED); - _data[92] = DrawStruct(0, -79, 52, 11, SPRFLAG_SCENE_CLIPPED); + _data[91] = DrawStruct(0, 200, 52, 0); + _data[92] = DrawStruct(0, -79, 52, 11); _data[93] = DrawStruct(0, -27, 52, 11, 0); _data[94] = DrawStruct(0, 32, 52, 11, 0); _data[95] = DrawStruct(0, 89, 52, 11, 0); - _data[96] = DrawStruct(0, 145, 52, 11, SPRFLAG_SCENE_CLIPPED); + _data[96] = DrawStruct(0, 145, 52, 11); _data[97] = DrawStruct(0, -8, 50, 12, 0); _data[98] = DrawStruct(0, -65, 50, 12, 0); _data[99] = DrawStruct(0, 49, 50, 12, 0); @@ -315,17 +327,17 @@ IndoorDrawList::IndoorDrawList() : _data[116] = DrawStruct(0, 63, 47, 8, SPRFLAG_HORIZ_FLIPPED); _data[117] = DrawStruct(0, 94, 52, 8, 0); _data[118] = DrawStruct(0, 50, 52, 8, SPRFLAG_HORIZ_FLIPPED); - _data[119] = DrawStruct(6, -40, 40, 0, SPRFLAG_SCENE_CLIPPED); + _data[119] = DrawStruct(6, -40, 40, 0); _data[120] = DrawStruct(6, 64, 40); - _data[121] = DrawStruct(0, 168, 40, 0, SPRFLAG_SCENE_CLIPPED); - _data[122] = DrawStruct(0, -72, 40, 6, SPRFLAG_SCENE_CLIPPED); + _data[121] = DrawStruct(0, 168, 40, 0); + _data[122] = DrawStruct(0, -72, 40, 6); _data[123] = DrawStruct(0, 32, 40, 6, 0); - _data[124] = DrawStruct(0, 137, 40, 6, SPRFLAG_SCENE_CLIPPED); + _data[124] = DrawStruct(0, 137, 40, 6); _data[125] = DrawStruct(0, -7, 25, 7, 0); - _data[126] = DrawStruct(0, -112, 25, 7, SPRFLAG_SCENE_CLIPPED); - _data[127] = DrawStruct(0, 98, 25, 7, SPRFLAG_SCENE_CLIPPED); - _data[128] = DrawStruct(0, -112, 29, 8, SPRFLAG_SCENE_CLIPPED); - _data[129] = DrawStruct(0, 98, 29, 8, SPRFLAG_SCENE_CLIPPED); + _data[126] = DrawStruct(0, -112, 25, 7); + _data[127] = DrawStruct(0, 98, 25, 7); + _data[128] = DrawStruct(0, -112, 29, 8); + _data[129] = DrawStruct(0, 98, 29, 8); _data[130] = DrawStruct(0, -38, 29, 8, 0); _data[131] = DrawStruct(0, 25, 29, 8, 0); _data[132] = DrawStruct(0, -7, 29, 8, 0); @@ -339,23 +351,23 @@ IndoorDrawList::IndoorDrawList() : _data[140] = DrawStruct(0, 55, 41, 4, SPRFLAG_HORIZ_FLIPPED); _data[141] = DrawStruct(0, 106, 47, 4, 0); _data[142] = DrawStruct(0, 38, 47, 4, SPRFLAG_HORIZ_FLIPPED); - _data[143] = DrawStruct(0, -136, 24, 0, SPRFLAG_SCENE_CLIPPED); + _data[143] = DrawStruct(0, -136, 24, 0); _data[144] = DrawStruct(0, 8, 12); _data[145] = DrawStruct(0, 32, 24); _data[146] = DrawStruct(0, 200, 12, 0, SPRFLAG_HORIZ_FLIPPED); - _data[147] = DrawStruct(0, 200, 24, 0, SPRFLAG_SCENE_CLIPPED); + _data[147] = DrawStruct(0, 200, 24, 0); _data[148] = DrawStruct(0, 32, 24); - _data[149] = DrawStruct(0, -5, 2, 0, SPRFLAG_BOTTOM_CLIPPED | SPRFLAG_SCENE_CLIPPED); - _data[150] = DrawStruct(0, -67, 10, 0, SPRFLAG_BOTTOM_CLIPPED | SPRFLAG_SCENE_CLIPPED); + _data[149] = DrawStruct(0, -5, 2, 0, SPRFLAG_BOTTOM_CLIPPED); + _data[150] = DrawStruct(0, -67, 10, 0, SPRFLAG_BOTTOM_CLIPPED); _data[151] = DrawStruct(0, 44, 73); _data[152] = DrawStruct(0, 44, 73); - _data[153] = DrawStruct(0, 58, 14, 0, SPRFLAG_BOTTOM_CLIPPED | SPRFLAG_SCENE_CLIPPED); + _data[153] = DrawStruct(0, 58, 14, 0, SPRFLAG_BOTTOM_CLIPPED); _data[154] = DrawStruct(0, 169, 73); _data[155] = DrawStruct(0, 169, 73); - _data[156] = DrawStruct(0, -5, 14, 0, SPRFLAG_BOTTOM_CLIPPED | SPRFLAG_SCENE_CLIPPED); + _data[156] = DrawStruct(0, -5, 14, 0, SPRFLAG_BOTTOM_CLIPPED); _data[157] = DrawStruct(0, 110, 73); _data[158] = DrawStruct(0, 110, 73); - _data[159] = DrawStruct(0, -5, 14, 0, SPRFLAG_BOTTOM_CLIPPED | SPRFLAG_SCENE_CLIPPED); + _data[159] = DrawStruct(0, -5, 14, 0, SPRFLAG_BOTTOM_CLIPPED); _data[160] = DrawStruct(0, 110, 73); _data[161] = DrawStruct(0, 110, 73); _data[162] = DrawStruct(0, 72, 43); @@ -368,6 +380,15 @@ IndoorDrawList::IndoorDrawList() : _data[169] = DrawStruct(0, 26, 42, 0, SPRFLAG_HORIZ_FLIPPED); } +void IndoorDrawList::draw() { + // Mark all items to be drawn as being clipped to the scene area + for (int idx = 0; idx < size(); ++idx) + _data[idx]._flags |= SPRFLAG_SCENE_CLIPPED; + + // Draw the list + (*g_vm->_windows)[3].drawList(_data, size()); +} + /*------------------------------------------------------------------------*/ InterfaceScene::InterfaceScene(XeenEngine *vm): _vm(vm) { @@ -380,7 +401,7 @@ InterfaceScene::InterfaceScene(XeenEngine *vm): _vm(vm) { _flipDefaultGround = false; _isAttacking = false; _charsShooting = false; - _objNumber = 0; + _objNumber = -1; _combatFloatCounter = 0; _thinWall = false; _isAnimReset = false; @@ -392,9 +413,8 @@ void InterfaceScene::drawScene() { Map &map = *_vm->_map; Scripts &scripts = *_vm->_scripts; - MazeObject &objObject = map._mobData._objects[_objNumber]; + MazeObject *obj = (_objNumber == -1) ? nullptr : &map._mobData._objects[_objNumber]; Direction partyDirection = _vm->_party->_mazeDirection; - int objNum = _objNumber - 1; // Loop to update the frame numbers for each maze object, applying the animation frame // limits as specified by the map's _animationInfo listing @@ -407,9 +427,9 @@ void InterfaceScene::drawScene() { mazeObject._frame = animEntry._frame1._frames[directionIndex]; } else { ++mazeObject._frame; - if ((int)idx == objNum && scripts._animCounter > 0 && ( - objObject._spriteId == (_vm->_files->_isDarkCc ? 15 : 16) || - objObject._spriteId == 58 || objObject._spriteId == 73)) { + if ((int)idx == _objNumber && scripts._animCounter > 0 && ( + obj->_spriteId == (_vm->_files->_ccNum ? 15 : 16) || + obj->_spriteId == 58 || obj->_spriteId == 73)) { if (mazeObject._frame > 4 || mazeObject._spriteId == 58) mazeObject._frame = 1; } else if (mazeObject._frame >= animEntry._frame2._frames[directionIndex]) { @@ -570,7 +590,7 @@ void InterfaceScene::drawIndoorsScene() { _indoorList[idx]._frame = -1; if (combat._monstersAttacking) { - for (int idx = 0; idx < 96; ++idx) { + for (int idx = 0; idx < 8; ++idx) { if (_indoorList[79 + idx]._sprites != nullptr) { _indoorList[79 + idx]._frame = 0; } else if (_indoorList[111 + idx]._sprites != nullptr) { @@ -599,7 +619,7 @@ void InterfaceScene::drawIndoorsScene() { _isAnimReset = false; // Code in the original that's not being used - //MazeObject &objObject = map._mobData._objects[_objNumber - 1]; + //MazeObject &objObject = map._mobData._objects[_objNumber]; // Only the front rank of pow points result in a Pow splatter effect for (int idx = 0; idx < 3; ++idx) { @@ -2652,7 +2672,7 @@ void InterfaceScene::setIndoorsObjects() { Common::Point mazePos = _vm->_party->_mazePosition; Direction dir = _vm->_party->_mazeDirection; Common::Point pt; - _objNumber = 0; + _objNumber = -1; Common::Array<MazeObject> &objects = _vm->_map->_mobData._objects; for (uint idx = 0; idx < objects.size(); ++idx) { @@ -2660,7 +2680,7 @@ void InterfaceScene::setIndoorsObjects() { // Determine which half of the X/Y lists to use int listOffset; - if (_vm->_files->_isDarkCc) { + if (_vm->_files->_ccNum) { listOffset = mazeObject._spriteId == 47 ? 1 : 0; } else { listOffset = mazeObject._spriteId == 113 ? 1 : 0; @@ -3361,11 +3381,12 @@ void InterfaceScene::setOutdoorsObjects() { const Common::Point &pt = party._mazePosition; Direction dir = party._mazeDirection; int posIndex; + _objNumber = -1; for (uint idx = 0; idx < map._mobData._objects.size(); ++idx) { MazeObject &obj = map._mobData._objects[idx]; - if (_vm->_files->_isDarkCc) { + if (_vm->_files->_ccNum) { posIndex = obj._spriteId == 47 ? 1 : 0; } else { posIndex = obj._spriteId == 113 ? 1 : 0; @@ -3544,7 +3565,6 @@ void InterfaceScene::setOutdoorsObjects() { void InterfaceScene::drawIndoors() { Map &map = *_vm->_map; - Windows &windows = *_vm->_windows; int surfaceId; // Draw any surface tiles on top of the default ground @@ -3667,8 +3687,9 @@ void InterfaceScene::drawIndoors() { _indoorList._fwl_4F4R._frame = 9; else if (_wo[35]) _indoorList._fwl_4F4R._frame = 0; + /* TODO: Duplicated switch in the original executable.. original bug meant to check some other index? else if (_wo[79]) - _indoorList._fwl_4F4R._frame = 15; + _indoorList._fwl_4F4R._frame = 15;*/ else if (_wo[213]) _indoorList._fwl_4F4R._frame = 14; else if (_wo[233]) @@ -3930,7 +3951,9 @@ void InterfaceScene::drawIndoors() { _indoorList._fwl_4F1R._frame = 13; } - if (_wo[27] || _wo[22] || _wo[15] || _wo[96]) { + if (_wo[27] || _wo[22] || _wo[15]) { + } else if (_wo[96]) { + _indoorList._fwl_4F._frame = 7; } else if (_wo[50]) { _indoorList._fwl_4F._frame = 16; } else if (_wo[156]) { @@ -4377,7 +4400,7 @@ void InterfaceScene::drawIndoors() { _indoorList._horizon._frame = 7; // Finally draw the darn indoor scene - windows[3].drawList(&_indoorList[0], _indoorList.size()); + _indoorList.draw(); // Check for any character shooting _isAttacking = false; @@ -4392,7 +4415,6 @@ void InterfaceScene::drawIndoors() { void InterfaceScene::drawOutdoors() { Map &map = *_vm->_map; Party &party = *_vm->_party; - Windows &windows = *_vm->_windows; int surfaceId; // Draw any surface tiles on top of the default ground @@ -4458,7 +4480,7 @@ void InterfaceScene::drawOutdoors() { _outdoorList._groundSprite._flags = _flipWater ? SPRFLAG_HORIZ_FLIPPED : 0; // Finally render the outdoor scene - windows[3].drawList(&_outdoorList[0], _outdoorList.size()); + _outdoorList.draw(); // Check for any character shooting _isAttacking = false; diff --git a/engines/xeen/interface_scene.h b/engines/xeen/interface_scene.h index 0c181522bb..18f1f58a36 100644 --- a/engines/xeen/interface_scene.h +++ b/engines/xeen/interface_scene.h @@ -42,14 +42,28 @@ public: DrawStruct * const _attackImgs3; DrawStruct * const _attackImgs4; public: + /** + * Constructor + */ OutdoorDrawList(); + /** + * Get a draw list entry + */ DrawStruct &operator[](int idx) { assert(idx < size()); return _data[idx]; } + /** + * Return the size of the list + */ int size() const { return 132; } + + /** + * Draw the list to the scene + */ + void draw(); }; class IndoorDrawList { @@ -80,12 +94,23 @@ public: public: IndoorDrawList(); + /** + * Get a draw list entry + */ DrawStruct &operator[](int idx) { assert(idx < size()); return _data[idx]; } + /** + * Return the size of the list + */ int size() const { return 170; } + + /** + * Draw the list to the scene + */ + void draw(); }; class InterfaceScene { diff --git a/engines/xeen/item.cpp b/engines/xeen/item.cpp index 7a4b459d41..a364dad928 100644 --- a/engines/xeen/item.cpp +++ b/engines/xeen/item.cpp @@ -27,25 +27,50 @@ namespace Xeen { +void ItemState::synchronize(Common::Serializer &s) { + byte b = _counter | (_cursed ? 0x40 : 0) | (_broken ? 0x80 : 0); + s.syncAsByte(b); + + if (s.isLoading()) { + _counter = b & 63; + _cursed = (b & 0x40) != 0; + _broken = (b & 0x80) != 0; + } +} + +void ItemState::operator=(byte val) { + _counter = val & 63; + _cursed = (val & 0x40) != 0; + _broken = (val & 0x80) != 0; +} + +/*------------------------------------------------------------------------*/ + XeenItem::XeenItem() { clear(); } void XeenItem::clear() { - _material = _id = _bonusFlags = 0; + _material = _id = 0; + _state.clear(); _frame = 0; } void XeenItem::synchronize(Common::Serializer &s) { s.syncAsByte(_material); s.syncAsByte(_id); - s.syncAsByte(_bonusFlags); + _state.synchronize(s); s.syncAsByte(_frame); } ElementalCategory XeenItem::getElementalCategory() const { + assert(_material < 36); + return getElementalCategory(_material); +} + +ElementalCategory XeenItem::getElementalCategory(int material) { int idx; - for (idx = 0; Res.ELEMENTAL_CATEGORIES[idx] < _material; ++idx) + for (idx = 0; Res.ELEMENTAL_CATEGORIES[idx] < material; ++idx) ; return (ElementalCategory)idx; @@ -61,21 +86,36 @@ AttributeCategory XeenItem::getAttributeCategory() const { } const char *XeenItem::getItemName(ItemCategory category, uint id) { - if (id < 82) - return Res.ITEM_NAMES[category][id]; - - switch (category) { - case CATEGORY_WEAPON: - return Res.QUEST_ITEM_NAMES[id - 82]; - - case CATEGORY_ARMOR: - return Res.QUEST_ITEM_NAMES[id - 82 + 35]; - - case CATEGORY_ACCESSORY: - return Res.QUEST_ITEM_NAMES[id - 82 + 35 + 14]; - - default: - return Res.QUEST_ITEM_NAMES[id - 82 + 35 + 14 + 11]; + const char **questItems = (g_vm->getGameID() == GType_Swords) ? Res.QUEST_ITEM_NAMES_SWORDS : Res.QUEST_ITEM_NAMES; + const uint QUEST_OFFSET = g_vm->getGameID() == GType_Swords ? 88 : 82; + + if (id < QUEST_OFFSET) { + switch (category) { + case CATEGORY_WEAPON: + assert(id < 41); + return Res.WEAPON_NAMES[id]; + case CATEGORY_ARMOR: + assert(id < 14); + return Res.ARMOR_NAMES[id]; + case CATEGORY_ACCESSORY: + assert(id < 11); + return Res.ACCESSORY_NAMES[id]; + default: + assert(id < 22); + return Res.MISC_NAMES[id]; + } + } else { + switch (category) { + case CATEGORY_WEAPON: + return questItems[id - QUEST_OFFSET]; + case CATEGORY_ARMOR: + return questItems[id - QUEST_OFFSET + 35]; + case CATEGORY_ACCESSORY: + return questItems[id - QUEST_OFFSET + 35 + 14]; + default: + assert(g_vm->getGameID() != GType_Swords); + return questItems[id - QUEST_OFFSET + 35 + 14 + 11]; + } } } @@ -93,6 +133,14 @@ void InventoryItems::clear() { operator[](idx).clear(); } +InventoryItems &InventoryItems::operator=(const InventoryItems &src) { + Common::Array<XeenItem>::clear(); + assert(src.size() == INV_ITEMS_TOTAL); + for (uint idx = 0; idx < INV_ITEMS_TOTAL; ++idx) + push_back(src[idx]); + return *this; +} + bool InventoryItems::passRestrictions(int itemId, bool suppressError) const { CharacterClass charClass = _character->_class; @@ -156,7 +204,7 @@ bool InventoryItems::discardItem(int itemIndex) { XeenItem &item = operator[](itemIndex); XeenEngine *vm = Party::_vm; - if (item._bonusFlags & ITEMFLAG_CURSED) { + if (item._state._cursed) { ErrorScroll::show(vm, Res.CANNOT_DISCARD_CURSED_ITEM); } else { Common::String itemDesc = getFullDescription(itemIndex, 4); @@ -175,7 +223,7 @@ bool InventoryItems::discardItem(int itemIndex) { void InventoryItems::sort() { for (uint idx = 0; idx < size(); ++idx) { - if (operator[](idx)._id == 0) { + if (operator[](idx).empty()) { // Found empty slot operator[](idx).clear(); @@ -196,7 +244,7 @@ void InventoryItems::removeItem(int itemIndex) { XeenItem &item = operator[](itemIndex); XeenEngine *vm = Party::_vm; - if (item._bonusFlags & ITEMFLAG_CURSED) + if (item._state._cursed) ErrorScroll::show(vm, Res.CANNOT_REMOVE_CURSED_ITEM); else item._frame = 0; @@ -215,7 +263,7 @@ void InventoryItems::equipError(int itemIndex1, ItemCategory category1, int item Common::String itemName2 = _character->_items[category2].getName(itemIndex2); MessageDialog::show(vm, Common::String::format(Res.REMOVE_X_TO_EQUIP_Y, - itemName1.c_str(), itemName2.c_str())); + itemName2.c_str(), itemName1.c_str())); } else { MessageDialog::show(vm, Common::String::format(Res.EQUIPPED_ALL_YOU_CAN, (itemIndex1 == -1) ? Res.RING : Res.MEDAL)); @@ -230,7 +278,14 @@ void InventoryItems::enchantItem(int itemIndex, int amount) { bool InventoryItems::isFull() const { assert(size() == INV_ITEMS_TOTAL); - return operator[](size() - 1)._id != 0; + return !operator[](size() - 1).empty(); +} + +void InventoryItems::capitalizeItem(Common::String &name) { + if (name[3] == '\f') + name.setChar(toupper(name[6]), 6); + else + name.setChar(toupper(name[3]), 3); } /*------------------------------------------------------------------------*/ @@ -289,16 +344,17 @@ Common::String WeaponItems::getFullDescription(int itemIndex, int displayNum) { XeenItem &i = operator[](itemIndex); Resources &res = *getVm()->_resources; - return Common::String::format("\f%02u%s%s%s\f%02u%s%s%s", displayNum, - !i._bonusFlags ? res._maeNames[i._material].c_str() : "", - (i._bonusFlags & ITEMFLAG_BROKEN) ? Res.ITEM_BROKEN : "", - (i._bonusFlags & ITEMFLAG_CURSED) ? Res.ITEM_CURSED : "", + Common::String desc = Common::String::format("\f%02u%s%s%s\f%02u%s%s%s", displayNum, + i._state._cursed || i._state._broken ? "" : res._maeNames[i._material].c_str(), + i._state._broken ? Res.ITEM_BROKEN : "", + i._state._cursed ? Res.ITEM_CURSED : "", displayNum, Res.WEAPON_NAMES[i._id], - !i._bonusFlags ? "" : Res.BONUS_NAMES[i._bonusFlags & ITEMFLAG_BONUS_MASK], - (i._bonusFlags & (ITEMFLAG_BROKEN | ITEMFLAG_CURSED)) || - !i._bonusFlags ? "\b " : "" + !i._state._counter ? "" : Res.BONUS_NAMES[i._state._counter], + (i._state._cursed || i._state._broken) || !i._id ? "\b " : "" ); + capitalizeItem(desc); + return desc; } void WeaponItems::enchantItem(int itemIndex, int amount) { @@ -306,12 +362,12 @@ void WeaponItems::enchantItem(int itemIndex, int amount) { XeenItem &item = operator[](itemIndex); Character tempCharacter; - if (item._material == 0 && item._bonusFlags == 0 && item._id != 34) { + if (item._material == 0 && item._state.empty() && item._id < XEEN_SLAYER_SWORD) { tempCharacter.makeItem(amount, 0, 1); XeenItem &tempItem = tempCharacter._weapons[0]; item._material = tempItem._material; - item._bonusFlags = tempItem._bonusFlags; + item._state = tempItem._state; sound.playFX(19); } else { InventoryItems::enchantItem(itemIndex, amount); @@ -350,10 +406,9 @@ Common::String WeaponItems::getAttributes(XeenItem &item, const Common::String & } // Handle weapon effective against - int effective = item._bonusFlags & ITEMFLAG_BONUS_MASK; + Effectiveness effective = (Effectiveness)item._state._counter; if (effective) { - specialPower = Common::String::format(Res.EFFECTIVE_AGAINST, - Res.EFFECTIVENESS_NAMES[effective]); + specialPower = Common::String::format(Res.EFFECTIVE_AGAINST, Res.EFFECTIVENESS_NAMES[effective]); } return Common::String::format(Res.ITEM_DETAILS, classes.c_str(), @@ -362,6 +417,17 @@ Common::String WeaponItems::getAttributes(XeenItem &item, const Common::String & ); } +bool WeaponItems::hasElderWeapon() const { + if (g_vm->getGameID() == GType_Swords) { + for (uint idx = 0; idx < size(); ++idx) { + if ((*this)[idx]._id >= 34) + return true; + } + } + + return false; +} + /*------------------------------------------------------------------------*/ void ArmorItems::equipItem(int itemIndex) { @@ -371,7 +437,7 @@ void ArmorItems::equipItem(int itemIndex) { if (passRestrictions(item._id)) { for (uint idx = 0; idx < size(); ++idx) { XeenItem &i = operator[](idx); - if (i._frame == 9) { + if (i._frame == 3) { equipError(itemIndex, CATEGORY_ARMOR, idx, CATEGORY_ARMOR); return; } @@ -446,15 +512,16 @@ Common::String ArmorItems::getFullDescription(int itemIndex, int displayNum) { XeenItem &i = operator[](itemIndex); Resources &res = *getVm()->_resources; - return Common::String::format("\f%02u%s%s%s\f%02u%s%s", displayNum, - !i._bonusFlags ? "" : res._maeNames[i._material].c_str(), - (i._bonusFlags & ITEMFLAG_BROKEN) ? Res.ITEM_BROKEN : "", - (i._bonusFlags & ITEMFLAG_CURSED) ? Res.ITEM_CURSED : "", + Common::String desc = Common::String::format("\f%02u%s%s%s\f%02u%s%s", displayNum, + i._state._cursed || i._state._broken ? "" : res._maeNames[i._material].c_str(), + i._state._broken ? Res.ITEM_BROKEN : "", + i._state._cursed ? Res.ITEM_CURSED : "", displayNum, Res.ARMOR_NAMES[i._id], - (i._bonusFlags & (ITEMFLAG_BROKEN | ITEMFLAG_CURSED)) || - !i._bonusFlags ? "\b " : "" + (i._state._cursed || i._state._broken) || !i._id ? "\b " : "" ); + capitalizeItem(desc); + return desc; } void ArmorItems::enchantItem(int itemIndex, int amount) { @@ -462,12 +529,12 @@ void ArmorItems::enchantItem(int itemIndex, int amount) { XeenItem &item = operator[](itemIndex); Character tempCharacter; - if (item._material == 0 && item._bonusFlags == 0) { + if (item._material == 0 && item._state.empty()) { tempCharacter.makeItem(amount, 0, 2); XeenItem &tempItem = tempCharacter._armor[0]; item._material = tempItem._material; - item._bonusFlags = tempItem._bonusFlags; + item._state = tempItem._state; sound.playFX(19); } else { InventoryItems::enchantItem(itemIndex, amount); @@ -529,6 +596,8 @@ void AccessoryItems::equipItem(int itemIndex) { return; } } + + item._frame = 12; } else if (item._id <= 7) { int count = 0; for (uint idx = 0; idx < size(); ++idx) { @@ -558,15 +627,16 @@ Common::String AccessoryItems::getFullDescription(int itemIndex, int displayNum) XeenItem &i = operator[](itemIndex); Resources &res = *getVm()->_resources; - return Common::String::format("\f%02u%s%s%s\f%02u%s%s", displayNum, - !i._bonusFlags ? "" : res._maeNames[i._material].c_str(), - (i._bonusFlags & ITEMFLAG_BROKEN) ? Res.ITEM_BROKEN : "", - (i._bonusFlags & ITEMFLAG_CURSED) ? Res.ITEM_CURSED : "", + Common::String desc = Common::String::format("\f%02u%s%s%s\f%02u%s%s", displayNum, + i._state._cursed || i._state._broken ? "" : res._maeNames[i._material].c_str(), + i._state._broken ? Res.ITEM_BROKEN : "", + i._state._cursed ? Res.ITEM_CURSED : "", displayNum, - Res.ARMOR_NAMES[i._id], - (i._bonusFlags & (ITEMFLAG_BROKEN | ITEMFLAG_CURSED)) || - !i._bonusFlags ? "\b " : "" + Res.ACCESSORY_NAMES[i._id], + (i._state._cursed || i._state._broken) || !i._id ? "\b " : "" ); + capitalizeItem(desc); + return desc; } /* @@ -602,17 +672,18 @@ Common::String AccessoryItems::getAttributes(XeenItem &item, const Common::Strin Common::String MiscItems::getFullDescription(int itemIndex, int displayNum) { XeenItem &i = operator[](itemIndex); - Resources &res = *getVm()->_resources; - return Common::String::format("\f%02u%s%s%s\f%02u%s%s", displayNum, - !i._bonusFlags ? "" : res._maeNames[i._material].c_str(), - (i._bonusFlags & ITEMFLAG_BROKEN) ? Res.ITEM_BROKEN : "", - (i._bonusFlags & ITEMFLAG_CURSED) ? Res.ITEM_CURSED : "", + Common::String desc = Common::String::format("\f%02u%s%s\f%02u%s%s%s%s", displayNum, + i._state._broken ? Res.ITEM_BROKEN : "", + i._state._cursed ? Res.ITEM_CURSED : "", displayNum, - Res.ARMOR_NAMES[i._id], - (i._bonusFlags & (ITEMFLAG_BROKEN | ITEMFLAG_CURSED)) || - !i._id ? "\b " : "" + Res.MISC_NAMES[i._material], + (i._state._cursed || i._state._broken) || !i._id ? "" : Res.ITEM_OF, + (i._state._cursed || i._state._broken) ? "" : Res.SPECIAL_NAMES[i._id], + (i._state._cursed || i._state._broken) || !i._id ? "\b " : "" ); + capitalizeItem(desc); + return desc; } Common::String MiscItems::getAttributes(XeenItem &item, const Common::String &classes) { @@ -629,31 +700,65 @@ Common::String MiscItems::getAttributes(XeenItem &item, const Common::String &cl } /*------------------------------------------------------------------------*/ -InventoryItemsGroup::InventoryItemsGroup(InventoryItems &weapons, InventoryItems &armor, - InventoryItems &accessories, InventoryItems &misc) { - _itemSets[0] = &weapons; - _itemSets[1] = &armor; - _itemSets[2] = &accessories; - _itemSets[3] = &misc; +InventoryItems &InventoryItemsGroup::operator[](ItemCategory category) { + switch (category) { + case CATEGORY_WEAPON: + return _owner->_weapons; + case CATEGORY_ARMOR: + return _owner->_armor; + case CATEGORY_ACCESSORY: + return _owner->_accessories; + default: + return _owner->_misc; + } } -InventoryItems &InventoryItemsGroup::operator[](ItemCategory category) { - return *_itemSets[category]; +const InventoryItems &InventoryItemsGroup::operator[](ItemCategory category) const { + switch (category) { + case CATEGORY_WEAPON: + return _owner->_weapons; + case CATEGORY_ARMOR: + return _owner->_armor; + case CATEGORY_ACCESSORY: + return _owner->_accessories; + default: + return _owner->_misc; + } } void InventoryItemsGroup::breakAllItems() { for (int idx = 0; idx < INV_ITEMS_TOTAL; ++idx) { - if ((*_itemSets[0])[idx]._id != 34) { - (*_itemSets[0])[idx]._bonusFlags |= ITEMFLAG_BROKEN; - (*_itemSets[0])[idx]._frame = 0; + if (_owner->_weapons[idx]._id < XEEN_SLAYER_SWORD) { + _owner->_weapons[idx]._state._broken = true; + _owner->_weapons[idx]._frame = 0; } - (*_itemSets[1])[idx]._bonusFlags |= ITEMFLAG_BROKEN; - (*_itemSets[2])[idx]._bonusFlags |= ITEMFLAG_BROKEN; - (*_itemSets[3])[idx]._bonusFlags |= ITEMFLAG_BROKEN; - (*_itemSets[1])[idx]._frame = 0; - (*_itemSets[2])[idx]._frame = 0; + _owner->_armor[idx]._state._broken = true; + _owner->_accessories[idx]._state._broken = true; + _owner->_misc[idx]._state._broken = true; + _owner->_armor[idx]._frame = 0; + _owner->_accessories[idx]._frame = 0; + } +} + +void InventoryItemsGroup::curseUncurse(bool curse) { + for (int idx = 0; idx < INV_ITEMS_TOTAL; ++idx) { + _owner->_weapons[idx]._state._cursed = curse && _owner->_weapons[idx]._id < XEEN_SLAYER_SWORD; + _owner->_armor[idx]._state._cursed = curse; + _owner->_accessories[idx]._state._cursed = curse; + _owner->_misc[idx]._state._cursed = curse; } } +bool InventoryItemsGroup::hasCursedItems() const { + for (int idx = 0; idx < INV_ITEMS_TOTAL; ++idx) { + for (ItemCategory cat = CATEGORY_WEAPON; cat <= CATEGORY_MISC; cat = (ItemCategory)((int)cat + 1)) { + if ((*this)[cat][idx]._state._cursed) + return true; + } + } + + return false; +} + } // End of namespace Xeen diff --git a/engines/xeen/item.h b/engines/xeen/item.h index 871bd1c919..df13456cd7 100644 --- a/engines/xeen/item.h +++ b/engines/xeen/item.h @@ -52,11 +52,54 @@ enum ElementalCategory { ELEM_ENERGY = 4, ELEM_MAGIC = 5 }; +enum WeaponId { + XEEN_SLAYER_SWORD = 34 +}; + +enum Effectiveness { + EFFECTIVE_NONE = 0, EFFECTIVE_DRAGON = 1, EFFECTIVE_UNDEAD = 2, EFFECTIVE_GOLEM = 3, + EFFECTIVE_INSECT = 4, EFFEctIVE_MONSTERS = 5, EFFECTIVE_ANIMAL = 6 +}; + +struct ItemState { + byte _counter : 6; // Stores charges for Misc items, and the effective against for weapons + bool _cursed : 1; + bool _broken : 1; + + /** + * Constructor + */ + ItemState() : _counter(0), _cursed(false), _broken(false) {} + + /** + * Clear the state + */ + void clear() { + _counter = 0; + _cursed = _broken = false; + } + + /** + * Returns true if the state is empty + */ + bool empty() const { return !_counter && !_cursed && !_broken; } + + /** + * Synchronizes the item's state + */ + void synchronize(Common::Serializer &s); + + /** + * Set the entire state value + */ + void operator=(byte val); +}; + class XeenItem { public: int _material; uint _id; - int _bonusFlags; + ItemState _state; int _frame; public: /** @@ -64,6 +107,9 @@ public: */ static const char *getItemName(ItemCategory category, uint id); public: + /** + * Constructor + */ XeenItem(); /** @@ -77,6 +123,16 @@ public: bool empty() const { return _id == 0; } /** + * Returns true if the item is cursed or broken + */ + bool isBad() const { return _state._cursed || _state._broken; } + + /** + * Returns true for weapons if it's equipped + */ + bool isEquipped() const { return _frame != 0; } + + /** * Synchronizes the data for the item */ void synchronize(Common::Serializer &s); @@ -87,6 +143,11 @@ public: ElementalCategory getElementalCategory() const; /** + * Gets the elemental category for a given material + */ + static ElementalCategory getElementalCategory(int material); + + /** * Gets the attribute category for the item */ AttributeCategory getAttributeCategory() const; @@ -106,6 +167,11 @@ protected: * Returns a text string listing all the stats/attributes of a given item */ virtual Common::String getAttributes(XeenItem &item, const Common::String &classes) = 0; + + /** + * Capitalizes a passed description string that includes embedded formatting for the Items dialog + */ + void capitalizeItem(Common::String &name); public: InventoryItems(Character *character, ItemCategory category); virtual ~InventoryItems() {} @@ -116,6 +182,11 @@ public: void clear(); /** + * Handles copying items from one character to another + */ + InventoryItems &operator=(const InventoryItems &src); + + /** * Return whether a given item passes class-based usage restrictions * @param itemId Item Index * @param suppressError If true, no dialog is shown if the item doesn't pass restrictions @@ -190,6 +261,11 @@ public: * Enchants a weapon */ virtual void enchantItem(int itemIndex, int amount); + + /** + * Returns true if the character has an Elder weapon in Swords of Xeen + */ + bool hasElderWeapon() const; }; class ArmorItems : public InventoryItems { @@ -259,10 +335,9 @@ public: class InventoryItemsGroup { private: - InventoryItems *_itemSets[4]; + Character *_owner; public: - InventoryItemsGroup(InventoryItems &weapons, InventoryItems &armor, - InventoryItems &accessories, InventoryItems &misc); + InventoryItemsGroup(Character *owner) : _owner(owner) {} /** * Returns the inventory items for a given category @@ -270,9 +345,24 @@ public: InventoryItems &operator[](ItemCategory category); /** + * Returns the inventory items for a given category + */ + const InventoryItems &operator[](ItemCategory category) const; + + /** * Breaks all the items in a given character's inventory */ void breakAllItems(); + + /** + * Curses or curses all the items + */ + void curseUncurse(bool curse); + + /** + * Returns true if the character has any cursed items + */ + bool hasCursedItems() const; }; } // End of namespace Xeen diff --git a/engines/xeen/locations.cpp b/engines/xeen/locations.cpp index 2690bf1ebc..7cbfc2bab3 100644 --- a/engines/xeen/locations.cpp +++ b/engines/xeen/locations.cpp @@ -35,12 +35,12 @@ namespace Xeen { namespace Locations { BaseLocation::BaseLocation(LocationAction action) : ButtonContainer(g_vm), - _locationActionId(action), _isDarkCc(g_vm->_files->_isDarkCc), + _locationActionId(action), _ccNum(g_vm->_files->_ccNum), _vocName("hello1.voc"), _exitToUi(false) { - _townMaxId = (action >= SPHINX) ? 0 : Res.TOWN_MAXES[_isDarkCc][action]; + _townMaxId = (action >= SPHINX) ? 0 : Res.TOWN_MAXES[_ccNum][action]; if (action < NO_ACTION) { - _songName = Res.TOWN_ACTION_MUSIC[_isDarkCc][action]; - _townSprites.resize(Res.TOWN_ACTION_FILES[_isDarkCc][action]); + _songName = Res.TOWN_ACTION_MUSIC[_ccNum][action]; + _townSprites.resize(Res.TOWN_ACTION_FILES[_ccNum][action]); } _animFrame = 0; @@ -149,7 +149,7 @@ void BaseLocation::drawAnim(bool flag) { // TODO: Figure out a clean way to split method into individual location classes if (_locationActionId == BLACKSMITH) { if (sound.isSoundPlaying()) { - if (_isDarkCc) { + if (_ccNum) { _townSprites[_drawFrameIndex / 8].draw(0, _drawFrameIndex % 8, _animPos); _townSprites[2].draw(0, _vm->getRandomNumber(11) == 1 ? 9 : 10, Common::Point(34, 33)); @@ -158,20 +158,20 @@ void BaseLocation::drawAnim(bool flag) { } } else { _townSprites[_drawFrameIndex / 8].draw(0, _drawFrameIndex % 8, _animPos); - if (_isDarkCc) { + if (_ccNum) { _townSprites[2].draw(0, _vm->getRandomNumber(11) == 1 ? 9 : 10, Common::Point(34, 33)); } } - } else if (!_isDarkCc || _locationActionId != TRAINING) { + } else if (!_ccNum || _locationActionId != TRAINING) { if (!_townSprites[_drawFrameIndex / 8].empty()) _townSprites[_drawFrameIndex / 8].draw(0, _drawFrameIndex % 8, _animPos); } switch (_locationActionId) { case BANK: - if (sound.isSoundPlaying() || (_isDarkCc && _animFrame)) { - if (_isDarkCc) { + if (sound.isSoundPlaying() || (_ccNum && _animFrame)) { + if (_ccNum) { if (sound.isSoundPlaying() || _animFrame == 1) { _townSprites[4].draw(0, _vm->getRandomNumber(13, 18), Common::Point(8, 30)); @@ -189,7 +189,7 @@ void BaseLocation::drawAnim(bool flag) { case GUILD: if (!sound.isSoundPlaying()) { - if (_isDarkCc) { + if (_ccNum) { if (_animFrame) { _animFrame ^= 1; _townSprites[6].draw(0, _animFrame, Common::Point(8, 106)); @@ -201,7 +201,7 @@ void BaseLocation::drawAnim(bool flag) { break; case TAVERN: - if (sound.isSoundPlaying() && _isDarkCc) { + if (sound.isSoundPlaying() && _ccNum) { _townSprites[4].draw(0, _vm->getRandomNumber(7), Common::Point(153, 49)); } break; @@ -215,11 +215,11 @@ void BaseLocation::drawAnim(bool flag) { case TRAINING: if (sound.isSoundPlaying()) { - if (_isDarkCc) { + if (_ccNum) { _townSprites[_drawFrameIndex / 8].draw(0, _drawFrameIndex % 8, _animPos); } } else { - if (_isDarkCc) { + if (_ccNum) { _townSprites[0].draw(0, ++_animFrame % 8, Common::Point(8, 8)); _townSprites[5].draw(0, _vm->getRandomNumber(5), Common::Point(61, 74)); } else { @@ -257,7 +257,7 @@ void BaseLocation::drawAnim(bool flag) { _drawFrameIndex = (_drawFrameIndex + 1) % _townMaxId; } - if (_isDarkCc) { + if (_ccNum) { if (_locationActionId == BLACKSMITH && (_drawFrameIndex == 4 || _drawFrameIndex == 13)) sound.playFX(45); @@ -307,7 +307,7 @@ BankLocation::BankLocation() : BaseLocation(BANK) { addButton(Common::Rect(288, 108, 312, 128), Common::KEYCODE_ESCAPE, &_icons1); _animFrame = 1; - _vocName = _isDarkCc ? "bank1.voc" : "banker.voc"; + _vocName = _ccNum ? "bank1.voc" : "banker.voc"; } Common::String BankLocation::createLocationText(Character &ch) { @@ -320,7 +320,7 @@ Common::String BankLocation::createLocationText(Character &ch) { } void BankLocation::drawBackground() { - if (_isDarkCc) { + if (_ccNum) { _townSprites[4].draw(0, _vm->getRandomNumber(13, 18), Common::Point(8, 30)); } @@ -342,6 +342,7 @@ void BankLocation::depositWithdrawl(PartyBank whereId) { Party &party = *g_vm->_party; Sound &sound = *g_vm->_sound; Windows &windows = *g_vm->_windows; + Window &w = windows[35]; int gold, gems; if (whereId == WHERE_BANK) { @@ -363,26 +364,24 @@ void BankLocation::depositWithdrawl(PartyBank whereId) { XeenEngine::printMil(gold).c_str(), XeenEngine::printMil(gems).c_str()); - windows[35].open(); - windows[35].writeString(msg); - drawButtons(&windows[35]); - windows[35].update(); + w.open(); + w.writeString(msg); + drawButtons(&w); + w.update(); sound.stopSound(); File voc("coina.voc"); ConsumableType consType = CONS_GOLD; do { - switch (wait()) { - case Common::KEYCODE_o: + wait(); + if (_buttonValue == Common::KEYCODE_o) { consType = CONS_GOLD; - break; - case Common::KEYCODE_e: + } else if (_buttonValue == Common::KEYCODE_e) { consType = CONS_GEMS; + } else if (_buttonValue == Common::KEYCODE_ESCAPE) { break; - case Common::KEYCODE_ESCAPE: - break; - default: + } else { continue; } @@ -392,7 +391,7 @@ void BankLocation::depositWithdrawl(PartyBank whereId) { (whereId == WHERE_PARTY && !party._gold && consType == CONS_GOLD)) { party.notEnough(consType, whereId, WHERE_BANK, WT_LOC_WAIT); } else { - windows[35].writeString(Res.AMOUNT); + w.writeString(Res.AMOUNT); int amount = NumericInput::show(_vm, 35, 10, 77); if (amount) { @@ -426,16 +425,19 @@ void BankLocation::depositWithdrawl(PartyBank whereId) { sound.playSound(voc); msg = Common::String::format(Res.GOLD_GEMS_2, Res.DEPOSIT_WITHDRAWL[whereId], XeenEngine::printMil(gold).c_str(), XeenEngine::printMil(gems).c_str()); - windows[35].writeString(msg); - windows[35].update(); + w.writeString(msg); + w.update(); } - } while (!g_vm->shouldExit() && _buttonValue != Common::KEYCODE_ESCAPE); + } while (!g_vm->shouldExit()); for (uint idx = 0; idx < _buttons.size(); ++idx) _buttons[idx]._sprites = &_icons1; _buttons[0]._value = Common::KEYCODE_d; _buttons[1]._value = Common::KEYCODE_w; _buttons[2]._value = Common::KEYCODE_ESCAPE; + + w.close(); + clearEvents(); } /*------------------------------------------------------------------------*/ @@ -448,7 +450,7 @@ BlacksmithLocation::BlacksmithLocation() : BaseLocation(BLACKSMITH) { addButton(Common::Rect(234, 74, 308, 82), 0); addButton(Common::Rect(234, 84, 308, 92), 0); - _vocName = _isDarkCc ? "see2.voc" : "whaddayo.voc"; + _vocName = _ccNum ? "see2.voc" : "whaddayo.voc"; } Common::String BlacksmithLocation::createLocationText(Character &ch) { @@ -469,7 +471,7 @@ Character *BlacksmithLocation::doOptions(Character *c) { intf.highlightChar(_buttonValue); } } else if (_buttonValue == Common::KEYCODE_b) { - c = ItemsDialog::show(_vm, c, ITEMMODE_BLACKSMITH); + c = ItemsDialog::show(_vm, c, ITEMMODE_BUY); _buttonValue = 0; } @@ -479,7 +481,7 @@ Character *BlacksmithLocation::doOptions(Character *c) { void BlacksmithLocation::farewell() { Sound &sound = *g_vm->_sound; - if (_isDarkCc) { + if (_ccNum) { sound.stopSound(); sound.playVoice("come1.voc", 1); } @@ -495,9 +497,9 @@ GuildLocation::GuildLocation() : BaseLocation(GUILD) { addButton(Common::Rect(234, 64, 308, 72), Common::KEYCODE_b); addButton(Common::Rect(234, 74, 308, 82), Common::KEYCODE_s); addButton(Common::Rect(234, 84, 308, 92), 0); - g_vm->_mode = MODE_17; + g_vm->_mode = MODE_INTERACTIVE7; - _vocName = _isDarkCc ? "parrot1.voc" : "guild10.voc"; + _vocName = _ccNum ? "parrot1.voc" : "guild10.voc"; } Common::String GuildLocation::createLocationText(Character &ch) { @@ -524,17 +526,17 @@ Character *GuildLocation::doOptions(Character *c) { if (!c->guildMember()) { sound.stopSound(); _animFrame = 5; - sound.playSound(_isDarkCc ? "skull1.voc" : "guild11.voc", 1); + sound.playSound(_ccNum ? "skull1.voc" : "guild11.voc", 1); } } } else if (_buttonValue == Common::KEYCODE_s) { if (c->guildMember()) - c = SpellsDialog::show(_vm, nullptr, c, 0x80); + c = SpellsDialog::show(_vm, this, c, SPELLS_DIALOG_INFO); _buttonValue = 0; - } else if (_buttonValue == Common::KEYCODE_c) { + } else if (_buttonValue == Common::KEYCODE_b) { if (!c->noActions()) { if (c->guildMember()) - c = SpellsDialog::show(_vm, nullptr, c, 0); + c = SpellsDialog::show(_vm, this, c, SPELLS_DIALOG_BUY); _buttonValue = 0; } } @@ -558,9 +560,9 @@ TavernLocation::TavernLocation() : BaseLocation(TAVERN) { addButton(Common::Rect(234, 64, 308, 72), Common::KEYCODE_f); addButton(Common::Rect(234, 74, 308, 82), Common::KEYCODE_t); addButton(Common::Rect(234, 84, 308, 92), Common::KEYCODE_r); - g_vm->_mode = MODE_17; + g_vm->_mode = MODE_INTERACTIVE7; - _vocName = _isDarkCc ? "hello1.voc" : "hello.voc"; + _vocName = _ccNum ? "hello1.voc" : "hello.voc"; } Common::String TavernLocation::createLocationText(Character &ch) { @@ -570,6 +572,7 @@ Common::String TavernLocation::createLocationText(Character &ch) { } Character *TavernLocation::doOptions(Character *c) { + EventsManager &events = *g_vm->_events; Interface &intf = *g_vm->_interface; Map &map = *g_vm->_map; Party &party = *g_vm->_party; @@ -620,19 +623,19 @@ Character *TavernLocation::doOptions(Character *c) { case Common::KEYCODE_f: { // Food - if (party._mazeId == (_isDarkCc ? 29 : 28)) { + if (party._mazeId == (_ccNum ? 29 : 28)) { _v22 = party._activeParty.size() * 15; _v23 = 10; idx = 0; - } else if (_isDarkCc && party._mazeId == 31) { + } else if (_ccNum && party._mazeId == 31) { _v22 = party._activeParty.size() * 60; _v23 = 100; idx = 1; - } else if (!_isDarkCc && party._mazeId == 30) { + } else if (!_ccNum && party._mazeId == 30) { _v22 = party._activeParty.size() * 50; _v23 = 50; idx = 1; - } else if (_isDarkCc) { + } else if (_ccNum) { _v22 = party._activeParty.size() * 120; _v23 = 250; idx = 2; @@ -646,7 +649,7 @@ Character *TavernLocation::doOptions(Character *c) { idx = 0; } - Common::String msg = _textStrings[(_isDarkCc ? 60 : 75) + idx]; + Common::String msg = _textStrings[(_ccNum ? 60 : 75) + idx]; windows[10].close(); windows[12].open(); windows[12].writeString(msg); @@ -658,7 +661,7 @@ Character *TavernLocation::doOptions(Character *c) { } else if (party.subtract(CONS_GOLD, _v23, WHERE_PARTY, WT_LOC_WAIT)) { party._food = _v22; sound.stopSound(); - sound.playSound(_isDarkCc ? "thanks2.voc" : "thankyou.voc", 1); + sound.playSound(_ccNum ? "thanks2.voc" : "thankyou.voc", 1); } } @@ -670,11 +673,11 @@ Character *TavernLocation::doOptions(Character *c) { case Common::KEYCODE_r: { // Rumors - if (party._mazeId == (_isDarkCc ? 29 : 28)) { + if (party._mazeId == (_ccNum ? 29 : 28)) { idx = 0; - } else if (party._mazeId == (_isDarkCc ? 31 : 30)) { + } else if (party._mazeId == (_ccNum ? 31 : 30)) { idx = 10; - } else if (_isDarkCc || party._mazeId == 49) { + } else if (_ccNum || party._mazeId == 49) { idx = 20; } @@ -693,12 +696,12 @@ Character *TavernLocation::doOptions(Character *c) { case Common::KEYCODE_s: { // Sign In // Set location and position for afterwards - idx = _isDarkCc ? (party._mazeId - 29) >> 1 : party._mazeId - 28; + idx = _ccNum ? (party._mazeId - 29) >> 1 : party._mazeId - 28; assert(idx >= 0); - party._mazePosition.x = Res.TAVERN_EXIT_LIST[_isDarkCc ? 1 : 0][_locationActionId][idx][0]; - party._mazePosition.y = Res.TAVERN_EXIT_LIST[_isDarkCc ? 1 : 0][_locationActionId][idx][1]; + party._mazePosition.x = Res.TAVERN_EXIT_LIST[_ccNum][_locationActionId][idx][0]; + party._mazePosition.y = Res.TAVERN_EXIT_LIST[_ccNum][_locationActionId][idx][1]; - if (!_isDarkCc || party._mazeId == 29) + if (!_ccNum || party._mazeId == 29) party._mazeDirection = DIR_WEST; else if (party._mazeId == 31) party._mazeDirection = DIR_EAST; @@ -708,13 +711,22 @@ Character *TavernLocation::doOptions(Character *c) { party._priorMazeId = party._mazeId; for (idx = 0; idx < (int)party._activeParty.size(); ++idx) { party._activeParty[idx]._savedMazeId = party._mazeId; - party._activeParty[idx]._xeenSide = map._loadDarkSide; + party._activeParty[idx]._xeenSide = map._loadCcNum; } - g_vm->_mode = MODE_17; + g_vm->_mode = MODE_INTERACTIVE7; party.addTime(1440); party._mazeId = 0; + // Say farewell + farewell(); + while (sound.isSoundPlaying()) + events.wait(1); + + // Animate closing a scroll + doScroll(true, false); + sound.stopAllAudio(); + // Show the party dialog PartyDialog::show(g_vm); @@ -744,23 +756,23 @@ Character *TavernLocation::doOptions(Character *c) { wait(); } else if (party.subtract(CONS_GOLD, 1, WHERE_PARTY, WT_LOC_WAIT)) { sound.stopSound(); - sound.playSound(_isDarkCc ? "thanks2.voc" : "thankyou.voc", 1); + sound.playSound(_ccNum ? "thanks2.voc" : "thankyou.voc", 1); - if (party._mazeId == (_isDarkCc ? 29 : 28)) { + if (party._mazeId == (_ccNum ? 29 : 28)) { _v24 = 30; - } else if (_isDarkCc && party._mazeId == 31) { + } else if (_ccNum && party._mazeId == 31) { _v24 = 40; - } else if (!_isDarkCc && party._mazeId == 45) { + } else if (!_ccNum && party._mazeId == 45) { _v24 = 45; - } else if (!_isDarkCc && party._mazeId == 49) { + } else if (!_ccNum && party._mazeId == 49) { _v24 = 60; - } else if (_isDarkCc) { + } else if (_ccNum) { _v24 = 50; } Common::String msg = _textStrings[map.mazeData()._tavernTips + _v24]; map.mazeData()._tavernTips = (map.mazeData()._tavernTips + 1) / - (_isDarkCc ? 10 : 15); + (_ccNum ? 10 : 15); Window &w = windows[12]; w.open(); @@ -786,7 +798,7 @@ void TavernLocation::farewell() { Sound &sound = *g_vm->_sound; sound.stopSound(); - sound.playVoice(_isDarkCc ? "gdluck1.voc" : "goodbye.voc", 1); + sound.playVoice(_ccNum ? "gdluck1.voc" : "goodbye.voc"); map.mazeData()._mazeNumber = party._mazeId; } @@ -802,7 +814,7 @@ TempleLocation::TempleLocation() : BaseLocation(TEMPLE) { _v10 = _v11 = 0; _v12 = _v13 = 0; _v14 = 0; - _flag1 = false; + _blessed = false; _v5 = _v6 = 0; _icons1.load("esc.icn"); @@ -812,28 +824,32 @@ TempleLocation::TempleLocation() : BaseLocation(TEMPLE) { addButton(Common::Rect(234, 74, 308, 82), Common::KEYCODE_u); addButton(Common::Rect(234, 84, 308, 92), 0); - _vocName = _isDarkCc ? "help2.voc" : "maywe2.voc"; + _vocName = _ccNum ? "help2.voc" : "maywe2.voc"; } Common::String TempleLocation::createLocationText(Character &ch) { Party &party = *g_vm->_party; + _donation = 0; + _uncurseCost = 0; + _healCost = 0; + _v5 = _v6 = 0; - if (party._mazeId == (_isDarkCc ? 29 : 28)) { + if (party._mazeId == (_ccNum ? 29 : 28)) { _v10 = _v11 = _v12 = _v13 = 0; _v14 = 10; - } else if (party._mazeId == (_isDarkCc ? 31 : 30)) { + } else if (party._mazeId == (_ccNum ? 31 : 30)) { _v13 = 10; _v12 = 50; _v11 = 500; _v10 = 100; _v14 = 25; - } else if (party._mazeId == (_isDarkCc ? 37 : 73)) { + } else if (party._mazeId == (_ccNum ? 37 : 73)) { _v13 = 20; _v12 = 100; _v11 = 1000; _v10 = 200; _v14 = 50; - } else if (_isDarkCc || party._mazeId == 49) { + } else if (_ccNum || party._mazeId == 49) { _v13 = 100; _v12 = 500; _v11 = 5000; @@ -862,17 +878,11 @@ Common::String TempleLocation::createLocationText(Character &ch) { _v5 = (_currentCharLevel * 1000) + (ch._conditions[ERADICATED] * 500) + _v11; } - for (int idx = 0; idx < 9; ++idx) { - _uncurseCost |= ch._weapons[idx]._bonusFlags & 0x40; - _uncurseCost |= ch._armor[idx]._bonusFlags & 0x40; - _uncurseCost |= ch._accessories[idx]._bonusFlags & 0x40; - _uncurseCost |= ch._misc[idx]._bonusFlags & 0x40; - } - - if (_uncurseCost || ch._conditions[CURSED]) - _v5 = (_currentCharLevel * 20) + _v10; + bool isCursed = ch._items.hasCursedItems(); + if (isCursed || ch._conditions[CURSED]) + _uncurseCost = (_currentCharLevel * 20) + _v10; - _donation = _flag1 ? 0 : _v14; + _donation = _blessed ? 0 : _v14; _healCost += _v6 + _v5; return Common::String::format(Res.TEMPLE_TEXT, ch._name.c_str(), @@ -905,9 +915,9 @@ Character *TempleLocation::doOptions(Character *c) { if (_donation && party.subtract(CONS_GOLD, _donation, WHERE_PARTY, WT_LOC_WAIT)) { sound.stopSound(); sound.playSound("coina.voc", 1); - _dayOfWeek = (_dayOfWeek + 1) / 10; + _dayOfWeek = (_dayOfWeek + 1) % 10; - if (_dayOfWeek == (party._day / 10)) { + if (_dayOfWeek == (party._day % 10)) { party._clairvoyanceActive = true; party._lightCount = 1; @@ -920,7 +930,7 @@ Character *TempleLocation::doOptions(Character *c) { intf.drawParty(true); sound.stopSound(); sound.playSound("ahh.voc"); - _flag1 = true; + _blessed = true; _donation = 0; } } @@ -955,13 +965,8 @@ Character *TempleLocation::doOptions(Character *c) { case Common::KEYCODE_u: if (_uncurseCost && party.subtract(CONS_GOLD, _uncurseCost, WHERE_PARTY, WT_LOC_WAIT)) { - for (int idx = 0; idx < 9; ++idx) { - c->_weapons[idx]._bonusFlags &= ~ITEMFLAG_CURSED; - c->_armor[idx]._bonusFlags &= ~ITEMFLAG_CURSED; - c->_accessories[idx]._bonusFlags &= ~ITEMFLAG_CURSED; - c->_misc[idx]._bonusFlags &= ~ITEMFLAG_CURSED; - } - + c->_items.curseUncurse(false); + c->_conditions[CURSED] = 0; _farewellTime = 1440; intf.drawParty(true); sound.stopSound(); @@ -988,12 +993,12 @@ TrainingLocation::TrainingLocation() : BaseLocation(TRAINING) { addButton(Common::Rect(281, 108, 305, 128), Common::KEYCODE_ESCAPE, &_icons1); addButton(Common::Rect(242, 108, 266, 128), Common::KEYCODE_t, &_icons1); - _vocName = _isDarkCc ? "youtrn1.voc" : "training.voc"; + _vocName = _ccNum ? "youtrn1.voc" : "training.voc"; } Common::String TrainingLocation::createLocationText(Character &ch) { Party &party = *g_vm->_party; - if (_isDarkCc) { + if (_ccNum) { switch (party._mazeId) { case 29: // Castleview @@ -1040,11 +1045,12 @@ Common::String TrainingLocation::createLocationText(Character &ch) { } else if (ch._level._permanent >= _maxLevel) { // At maximum level _experienceToNextLevel = 1; - msg = Common::String::format(Res.LEARNED_ALL, ch._name.c_str()); + msg = Common::String::format(Res.TRAINING_LEARNED_ALL, ch._name.c_str()); } else { // Eligble for level increase + uint cost = ch._level._permanent * ch._level._permanent * 10; msg = Common::String::format(Res.ELIGIBLE_FOR_LEVEL, - ch._name.c_str(), ch._level._permanent + 1); + ch._name.c_str(), ch._level._permanent + 1, cost); } return Common::String::format(Res.TRAINING_TEXT, msg.c_str(), @@ -1079,9 +1085,9 @@ Character *TrainingLocation::doOptions(Character *c) { Common::String name; if (c->_level._permanent >= _maxLevel) { - name = _isDarkCc ? "gtlost.voc" : "trainin1.voc"; + name = _ccNum ? "gtlost.voc" : "trainin1.voc"; } else { - name = _isDarkCc ? "gtlost.voc" : "trainin0.voc"; + name = _ccNum ? "gtlost.voc" : "trainin0.voc"; } sound.playSound(name); @@ -1090,7 +1096,7 @@ Character *TrainingLocation::doOptions(Character *c) { if (party.subtract(CONS_GOLD, (c->_level._permanent * c->_level._permanent) * 10, WHERE_PARTY, WT_LOC_WAIT)) { _drawFrameIndex = 0; sound.stopSound(); - sound.playSound(_isDarkCc ? "prtygd.voc" : "trainin2.voc", 1); + sound.playSound(_ccNum ? "prtygd.voc" : "trainin2.voc", 1); c->_experience -= c->nextExperienceLevel() - (c->getCurrentExperience() - c->_experience); @@ -1147,10 +1153,10 @@ int ArenaLocation::show() { } Common::String format = map._events._text[3]; - Common::String count = Common::String::format("%05u", party._activeParty[0]._awards[WARZONE_AWARD]); - int numIdx = count[3] == '1' ? 0 : count[4] - '0'; - Common::String msg = Common::String::format(format.c_str(), count.c_str(), SUFFIXES[numIdx]); - + int count = party._activeParty[0]._awards[WARZONE_AWARD]; + int suffixNum = (count < 10) ? count : 0; + Common::String msg = Common::String::format(format.c_str(), count, SUFFIXES[suffixNum]); + LocationMessage::show(27, Res.WARZONE_BATTLE_MASTER, msg, 1); map.load(28); @@ -1165,8 +1171,7 @@ int ArenaLocation::show() { } } - check = LocationMessage::show(27, Res.WARZONE_BATTLE_MASTER, - map._events._text[0].c_str(), 300); + check = LocationMessage::show(27, Res.WARZONE_BATTLE_MASTER, map._events._text[0].c_str(), 0); if (!check) { LocationMessage::show(27, Res.WARZONE_BATTLE_MASTER, map._events._text[1].c_str(), 300); @@ -1191,7 +1196,7 @@ int ArenaLocation::show() { if (howMany == 0) goto exit; - LocationMessage::show(27, Res.WARZONE_BATTLE_MASTER, map._events._text[2], 300); + LocationMessage::show(27, Res.WARZONE_BATTLE_MASTER, map._events._text[2], 1); // Clear monsters array party._mazeDirection = DIR_EAST; @@ -1236,7 +1241,7 @@ exit: /*------------------------------------------------------------------------*/ -CutsceneLocation::CutsceneLocation(LocationAction action) : BaseLocation(action), _mazeFlag(false) { +CutsceneLocation::CutsceneLocation(LocationAction action) : BaseLocation(action), _keyFound(false) { Party &party = *g_vm->_party; _mazeId = party._mazeId; _mazePos = party._mazePosition; @@ -1279,16 +1284,17 @@ int ReaperCutscene::show() { Sound &sound = *g_vm->_sound; Windows &windows = *g_vm->_windows; - SpriteResource sprites1(_isDarkCc ? "tower1.zom" : "tower.vga", _isDarkCc); - SpriteResource sprites2(_isDarkCc ? "tower2.zom" : "freap.vga", _isDarkCc); + SpriteResource sprites1(_ccNum ? "tower1.zom" : "tower.vga", _ccNum); + SpriteResource sprites2(_ccNum ? "tower2.zom" : "freap.vga", _ccNum); Graphics::ManagedSurface savedBg; savedBg.copyFrom(screen); + getNewLocation(); for (int idx = 13; idx >= 0; --idx) { events.updateGameCounter(); - sprites1.draw(0, 0, Common::Point(REAPER_X1[_isDarkCc][idx], REAPER_Y1[_isDarkCc][idx]), 0, idx); - if (_isDarkCc) { + sprites1.draw(0, 0, Common::Point(REAPER_X1[_ccNum][idx], REAPER_Y1[_ccNum][idx]), 0, idx); + if (_ccNum) { sprites1.draw(0, 1, Common::Point(REAPER_X2[idx], REAPER_Y1[1][idx]), 0, idx); sprites1.draw(0, party._isNight ? 3 : 2, Common::Point(REAPER_X3[idx], REAPER_Y1[1][idx]), 0, idx); } @@ -1296,7 +1302,7 @@ int ReaperCutscene::show() { WAIT(1); } - if (_isDarkCc) { + if (_ccNum) { for (int idx = -200; idx < 0; idx += 16) { events.updateGameCounter(); sprites1.draw(0, 0, Common::Point(0, 0)); @@ -1319,18 +1325,23 @@ int ReaperCutscene::show() { sound.setMusicPercent(38); sprites1.draw(0, 0, Common::Point(0, 0)); - if (_isDarkCc) { + if (_ccNum) { sprites1.draw(0, 1, Common::Point(160, 0)); sprites1.draw(0, party._isNight ? 3 : 2); } - _subtitles.setLine(_mazeFlag ? 5 : 6); - sound.playVoice(_mazeFlag ? "reaper12.voc" : "reaper14.voc"); + if (!_ccNum) { + _subtitles.setLine(_keyFound ? 5 : 6); + sound.playVoice(_keyFound ? "reaper12.voc" : "reaper14.voc"); + } else if (_keyFound) { + _subtitles.setLine(2); + sound.playVoice("howdid1.voc"); + } do { events.updateGameCounter(); int frame = g_vm->getRandomNumber(4); - if (_isDarkCc) { + if (_ccNum) { sprites2.draw(0, frame); sprites2.draw(0, frame + 5, Common::Point(160, 0)); } else { @@ -1344,21 +1355,23 @@ int ReaperCutscene::show() { } while (sound.isSoundPlaying()); sprites2.draw(0, 0, Common::Point(0, 0)); - if (_isDarkCc) + if (_ccNum) sprites2.draw(0, 5, Common::Point(160, 0)); windows[0].update(); WAIT(7); - sound.playVoice(_mazeFlag ? "reaper12.voc" : "reaper14.voc"); - if (_mazeFlag) - sound.playVoice(_isDarkCc ? "goin1.voc" : "reaper13.voc"); - else - sound.playVoice(_isDarkCc ? "needkey1.voc" : "reaper15.voc"); + if (_keyFound) { + sound.playVoice(_ccNum ? "goin1.voc" : "reaper13.voc"); + } else { + if (_ccNum) + _subtitles.setLine(3); + sound.playVoice(_ccNum ? "needkey1.voc" : "reaper15.voc"); + } do { events.updateGameCounter(); int frame = g_vm->getRandomNumber(4); - if (_isDarkCc) { + if (_ccNum) { sprites2.draw(0, frame, Common::Point(0, 0)); sprites2.draw(0, frame + 5, Common::Point(160, 0)); } else { @@ -1370,18 +1383,18 @@ int ReaperCutscene::show() { } while (_subtitles.lineActive()); sprites2.draw(0, 0, Common::Point(0, 0)); - if (_isDarkCc) + if (_ccNum) sprites2.draw(0, 5, Common::Point(160, 0)); windows[0].update(); WAIT(1); - if (_mazeFlag) { + if (_keyFound) { for (int idx = 0; idx < 14; ++idx) { events.updateGameCounter(); screen.blitFrom(savedBg); - sprites1.draw(0, 0, Common::Point(REAPER_X1[_isDarkCc][idx], REAPER_Y1[_isDarkCc][idx]), 0, idx); - - if (_isDarkCc) { + sprites1.draw(0, 0, Common::Point(REAPER_X1[_ccNum][idx], REAPER_Y1[_ccNum][idx]), 0, idx); + + if (_ccNum) { sprites1.draw(0, 1, Common::Point(REAPER_X2[idx], REAPER_Y1[1][idx]), 0, idx); sprites1.draw(0, party._isNight ? 3 : 2, Common::Point(REAPER_X3[idx], REAPER_Y1[1][idx]), 0, idx); } @@ -1415,14 +1428,14 @@ void ReaperCutscene::getNewLocation() { Map &map = *g_vm->_map; Party &party = *g_vm->_party; - if (_isDarkCc) { + if (_ccNum) { switch (party._mazeId) { case 3: if (party._questItems[40]) { _mazeId = 57; _mazePos = Common::Point(11, 8); _mazeDir = DIR_WEST; - _mazeFlag = true; + _keyFound = true; } break; @@ -1431,7 +1444,7 @@ void ReaperCutscene::getNewLocation() { _mazeId = 55; _mazePos = Common::Point(3, 8); _mazeDir = DIR_EAST; - _mazeFlag = true; + _keyFound = true; } break; @@ -1440,7 +1453,16 @@ void ReaperCutscene::getNewLocation() { _mazeId = 69; _mazePos = Common::Point(7, 4); _mazeDir = DIR_NORTH; - _mazeFlag = true; + _keyFound = true; + } + break; + + case 16: + if (party._questItems[41]) { + _mazeId = 61; + _mazePos = Common::Point(7, 12); + _mazeDir = DIR_SOUTH; + _keyFound = true; } break; @@ -1449,7 +1471,7 @@ void ReaperCutscene::getNewLocation() { _mazeId = 65; _mazePos = Common::Point(3, 8); _mazeDir = DIR_EAST; - _mazeFlag = true; + _keyFound = true; } break; @@ -1458,7 +1480,7 @@ void ReaperCutscene::getNewLocation() { _mazeId = 53; _mazePos = Common::Point(11, 8); _mazeDir = DIR_WEST; - _mazeFlag = true; + _keyFound = true; } break; @@ -1468,12 +1490,12 @@ void ReaperCutscene::getNewLocation() { } else { switch (party._mazeId) { case 7: - if (party._questItems[30]) { - map._loadDarkSide = true; + if (party._questItems[46]) { + map._loadCcNum = 1; _mazeId = 113; _mazePos = Common::Point(7, 4); _mazeDir = DIR_NORTH; - _mazeFlag = true; + _keyFound = true; } break; @@ -1482,17 +1504,17 @@ void ReaperCutscene::getNewLocation() { _mazeId = 55; _mazePos = Common::Point(3, 8); _mazeDir = DIR_EAST; - _mazeFlag = true; + _keyFound = true; } break; case 13: - if (party._questItems[29]) { - map._loadDarkSide = true; + if (party._questItems[45]) { + map._loadCcNum = 1; _mazeId = 117; _mazePos = Common::Point(7, 4); _mazeDir = DIR_NORTH; - _mazeFlag = true; + _keyFound = true; } break; @@ -1501,7 +1523,7 @@ void ReaperCutscene::getNewLocation() { _mazeId = 59; _mazePos = Common::Point(11, 8); _mazeDir = DIR_WEST; - _mazeFlag = true; + _keyFound = true; } break; @@ -1510,7 +1532,7 @@ void ReaperCutscene::getNewLocation() { _mazeId = 51; _mazePos = Common::Point(7, 12); _mazeDir = DIR_SOUTH; - _mazeFlag = true; + _keyFound = true; } break; @@ -1545,28 +1567,29 @@ int GolemCutscene::show() { Sound &sound = *g_vm->_sound; Windows &windows = *g_vm->_windows; SpriteResource sprites1, sprites2[2]; - sprites1.load(_isDarkCc ? "dung1.zom" : "golmback.vga"); - sprites2[0].load(_isDarkCc ? "dung2.zom" : "golem.vga"); - if (_isDarkCc) + sprites1.load(_ccNum ? "dung1.zom" : "golmback.vga"); + sprites2[0].load(_ccNum ? "dung2.zom" : "golem.vga"); + if (_ccNum) sprites2[1].load("dung3.zom"); // Save the screen Graphics::ManagedSurface savedBg; savedBg.copyFrom(screen); + getNewLocation(); - for (int idx = (_isDarkCc ? 8 : 11); idx >= 0; --idx) { + for (int idx = (_ccNum ? 8 : 11); idx >= 0; --idx) { events.updateGameCounter(); screen.blitFrom(savedBg); sprites1.draw(0, 0, - Common::Point(GOLEM_X1[_isDarkCc][idx], GOLEM_Y1[_isDarkCc][idx]), 0, idx); + Common::Point(GOLEM_X1[_ccNum][idx], GOLEM_Y1[_ccNum][idx]), 0, idx); sprites1.draw(0, 1, - Common::Point(GOLEM_X2[_isDarkCc][idx], GOLEM_Y1[_isDarkCc][idx]), 0, idx); + Common::Point(GOLEM_X2[_ccNum][idx], GOLEM_Y1[_ccNum][idx]), 0, idx); windows[0].update(); WAIT(1); } - if (_isDarkCc) + if (!_ccNum) sound.playSound("ogre.voc"); for (int idx = -200; idx < 0; idx += 16) { @@ -1574,13 +1597,13 @@ int GolemCutscene::show() { sprites1.draw(0, 0, Common::Point(0, 0)); sprites1.draw(0, 1, Common::Point(160, 0)); sprites2[0].draw(0, 0, Common::Point(idx, 0), SPRFLAG_800); - sprites2[_isDarkCc].draw(0, 1, Common::Point(idx + 160, 0), SPRFLAG_800); + sprites2[_ccNum].draw(0, 1, Common::Point(idx + 160, 0), SPRFLAG_800); - if (!_isDarkCc) + if (!_ccNum) sprites2[0].draw(0, 2, Common::Point(idx + g_vm->getRandomNumber(9) - 5, g_vm->getRandomNumber(9) - 5), SPRFLAG_800); - - if (!_isDarkCc && !sound.isSoundPlaying()) + + if (!_ccNum && !sound.isSoundPlaying()) sound.playSound("ogre.voc"); WAIT(1); @@ -1589,28 +1612,34 @@ int GolemCutscene::show() { sprites1.draw(0, 0, Common::Point(0, 0)); sprites1.draw(0, 1, Common::Point(160, 0)); sprites2[0].draw(0, 0, Common::Point(0, 0)); - sprites2[_isDarkCc].draw(0, _isDarkCc ? 0 : 1, Common::Point(160, 0)); - if (!_isDarkCc) + sprites2[_ccNum].draw(0, 1 - _ccNum, Common::Point(160, 0)); + if (!_ccNum) sprites2[0].draw(0, 2); windows[0].update(); while (sound.isSoundPlaying()) { + _subtitles.show(); WAIT(1); } sound.setMusicPercent(38); - _subtitles.setLine(_mazeFlag ? 8 : 7); - sound.playVoice(_mazeFlag ? "golem15.voc" : "golem13.voc"); + if (_ccNum) { + _subtitles.setLine(_keyFound ? 5 : 4); + sound.playVoice("what2.voc"); + } else { + _subtitles.setLine(_keyFound ? 8 : 7); + sound.playVoice(_keyFound ? "golem15.voc" : "golem13.voc"); + } do { events.updateGameCounter(); sprites1.draw(0, 0, Common::Point(0, 0)); sprites1.draw(0, 1, Common::Point(160, 0)); - if (_isDarkCc) { + if (_ccNum) { int frame = g_vm->getRandomNumber(6); sprites2[0].draw(0, frame, Common::Point(0, 0)); - sprites2[1].draw(1, frame, Common::Point(160, 0)); + sprites2[1].draw(0, frame, Common::Point(160, 0)); } else { sprites2[0].draw(0, 0, Common::Point(0, 0)); sprites2[0].draw(0, 1, Common::Point(160, 0)); @@ -1618,28 +1647,30 @@ int GolemCutscene::show() { g_vm->getRandomNumber(9) - 3)); } + _subtitles.show(); WAIT(1); } while (sound.isSoundPlaying()); sprites1.draw(0, 0, Common::Point(0, 0)); sprites1.draw(0, 1, Common::Point(160, 0)); sprites2[0].draw(0, 0, Common::Point(0, 0)); - sprites2[_isDarkCc].draw(0, _isDarkCc ? 0 : 1, Common::Point(160, 0)); - if (!_isDarkCc) + sprites2[_ccNum].draw(0, 1 - _ccNum, Common::Point(160, 0)); + if (!_ccNum) sprites2[0].draw(0, 2); windows[0].update(); events.updateGameCounter(); - events.wait(_isDarkCc ? 10 : 1); + if (_subtitles.wait(_ccNum ? 10 : 1)) + goto exit; - if (!_isDarkCc) { + if (_ccNum) { + sound.playVoice(_keyFound ? "go2.voc" : "key2.voc"); + } else { sound.playVoice("ogre.voc"); while (sound.isSoundPlaying()) events.pollEventsAndWait(); - sound.playVoice(_mazeFlag ? "golem16.voc" : "golem14.voc"); - } else { - sound.playVoice(_mazeFlag ? "go2.voc" : "key2.voc"); + sound.playVoice(_keyFound ? "golem16.voc" : "golem14.voc"); } do { @@ -1647,10 +1678,10 @@ int GolemCutscene::show() { sprites1.draw(0, 0, Common::Point(0, 0)); sprites1.draw(0, 1, Common::Point(160, 0)); - if (_isDarkCc) { + if (_ccNum) { int frame = g_vm->getRandomNumber(6); sprites2[0].draw(0, frame, Common::Point(0, 0)); - sprites2[1].draw(1, frame, Common::Point(160, 0)); + sprites2[1].draw(0, frame, Common::Point(160, 0)); } else { sprites2[0].draw(0, 0, Common::Point(0, 0)); sprites2[0].draw(0, 1, Common::Point(160, 0)); @@ -1659,31 +1690,28 @@ int GolemCutscene::show() { } windows[0].update(); + _subtitles.show(); WAIT(1); - } while (sound.isSoundPlaying()); + } while (_subtitles.lineActive()); sprites1.draw(0, 0, Common::Point(0, 0)); sprites1.draw(0, 1, Common::Point(160, 0)); sprites2[0].draw(0, 0, Common::Point(0, 0)); - sprites2[_isDarkCc].draw(0, _isDarkCc ? 0 : 1, Common::Point(160, 0)); - if (!_isDarkCc) + sprites2[_ccNum].draw(0, 1 - _ccNum, Common::Point(160, 0)); + if (!_ccNum) sprites2[0].draw(0, 2); - windows[0].update(); - while (_subtitles.lineActive()) { - WAIT(1); - } sound.setMusicPercent(75); - if (!_mazeFlag) { - for (int idx = 0; !g_vm->shouldExit() && idx < (_isDarkCc ? 9 : 12); ++idx) { + if (!_keyFound) { + for (int idx = 0; !g_vm->shouldExit() && idx < (_ccNum ? 9 : 12); ++idx) { events.updateGameCounter(); screen.blitFrom(savedBg); sprites1.draw(0, 0, - Common::Point(GOLEM_X1[_isDarkCc][idx], GOLEM_Y1[_isDarkCc][idx]), 0, idx); + Common::Point(GOLEM_X1[_ccNum][idx], GOLEM_Y1[_ccNum][idx]), 0, idx); sprites1.draw(0, 1, - Common::Point(GOLEM_X2[_isDarkCc][idx], GOLEM_Y1[_isDarkCc][idx]), 0, idx); + Common::Point(GOLEM_X2[_ccNum][idx], GOLEM_Y1[_ccNum][idx]), 0, idx); windows[0].update(); WAIT(1); @@ -1712,14 +1740,14 @@ void GolemCutscene::getNewLocation() { Map &map = *g_vm->_map; Party &party = *g_vm->_party; - if (_isDarkCc) { + if (_ccNum) { switch (party._mazeId) { case 12: if (party._questItems[47]) { _mazeId = 73; _mazePos = Common::Point(0, 7); _mazeDir = DIR_NORTH; - _mazeFlag = true; + _keyFound = true; } break; @@ -1728,7 +1756,7 @@ void GolemCutscene::getNewLocation() { _mazeId = 83; _mazePos = Common::Point(11, 1); _mazeDir = DIR_NORTH; - _mazeFlag = true; + _keyFound = true; } break; @@ -1737,7 +1765,7 @@ void GolemCutscene::getNewLocation() { _mazeId = 121; _mazePos = Common::Point(18, 0); _mazeDir = DIR_NORTH; - _mazeFlag = true; + _keyFound = true; } break; @@ -1746,7 +1774,7 @@ void GolemCutscene::getNewLocation() { _mazeId = 78; _mazePos = Common::Point(8, 14); _mazeDir = DIR_SOUTH; - _mazeFlag = true; + _keyFound = true; } break; @@ -1760,7 +1788,7 @@ void GolemCutscene::getNewLocation() { _mazeId = 81; _mazePos = Common::Point(1, 17); _mazeDir = DIR_EAST; - _mazeFlag = true; + _keyFound = true; } break; @@ -1769,17 +1797,17 @@ void GolemCutscene::getNewLocation() { _mazeId = 80; _mazePos = Common::Point(29, 16); _mazeDir = DIR_WEST; - _mazeFlag = true; + _keyFound = true; } break; case 19: if (party._questItems[50]) { - map._loadDarkSide = true; + map._loadCcNum = 1; _mazeId = 121; _mazePos = Common::Point(18, 0); _mazeDir = DIR_NORTH; - _mazeFlag = true; + _keyFound = true; } break; @@ -1788,7 +1816,7 @@ void GolemCutscene::getNewLocation() { _mazeId = 79; _mazePos = Common::Point(5, 16); _mazeDir = DIR_EAST; - _mazeFlag = true; + _keyFound = true; } break; @@ -1833,9 +1861,9 @@ int DwarfCutscene::show() { Sound &sound = *g_vm->_sound; Windows &windows = *g_vm->_windows; - SpriteResource sprites1(_isDarkCc ? "town1.zom" : "dwarf1.vga"); - SpriteResource sprites2(_isDarkCc ? "town2.zom" : "dwarf3.vga"); - SpriteResource sprites3(_isDarkCc ? "town3.zom" : "dwarf2.vga"); + SpriteResource sprites1(_ccNum ? "town1.zom" : "dwarf1.vga"); + SpriteResource sprites2(_ccNum ? "town2.zom" : "dwarf3.vga"); + SpriteResource sprites3(_ccNum ? "town3.zom" : "dwarf2.vga"); getNewLocation(); // Save the screen contents @@ -1843,17 +1871,17 @@ int DwarfCutscene::show() { savedBg.copyFrom(screen); // Zoom in on the mine entrance - for (int idx = (_isDarkCc ? 10 : 12); idx >= 0; --idx) { + for (int idx = (_ccNum ? 10 : 12); idx >= 0; --idx) { events.updateGameCounter(); screen.blitFrom(savedBg); sprites1.draw(0, 0, - Common::Point(DWARF_X0[_isDarkCc][idx], DWARF_Y[_isDarkCc][idx]), 0, idx); + Common::Point(DWARF_X0[_ccNum][idx], DWARF_Y[_ccNum][idx]), 0, idx); sprites1.draw(0, 1, - Common::Point(DWARF_X1[_isDarkCc][idx], DWARF_Y[_isDarkCc][idx]), 0, idx); - if (_isDarkCc) + Common::Point(DWARF_X1[_ccNum][idx], DWARF_Y[_ccNum][idx]), 0, idx); + if (_ccNum) sprites1.draw(0, 2, - Common::Point(DWARF_X2[idx], DWARF_Y[_isDarkCc][idx]), 0, idx); + Common::Point(DWARF_X2[idx], DWARF_Y[_ccNum][idx]), 0, idx); windows[0].update(); WAIT(1); @@ -1867,7 +1895,7 @@ int DwarfCutscene::show() { events.updateGameCounter(); screen.blitFrom(savedBg); - sprites2.draw(0, 0, Common::Point(DWARF2_X[_isDarkCc][idx], DWARF2_Y[_isDarkCc][idx]), 0, idx); + sprites2.draw(0, 0, Common::Point(DWARF2_X[_ccNum][idx], DWARF2_Y[_ccNum][idx]), 0, idx); windows[0].update(); WAIT(1); } @@ -1876,16 +1904,19 @@ int DwarfCutscene::show() { screen.blitFrom(savedBg); sprites2.draw(0, 0); windows[0].update(); - _subtitles.setLine(_isDarkCc ? 0 : 4); + if (_ccNum) + _subtitles.setLine(_keyFound ? 7 : 8); + else + _subtitles.setLine(4); - for (int idx = 0; idx < (_isDarkCc ? 2 : 3); ++idx) { + for (int idx = 0; idx < (_ccNum ? 2 : 3); ++idx) { switch (idx) { case 0: - sound.playSound(_isDarkCc ? "pass2.voc" : "dwarf10.voc"); + sound.playSound(_ccNum ? "pass2.voc" : "dwarf10.voc"); break; case 1: - if (_isDarkCc) { + if (_ccNum) { sprites2.draw(0, 0); sprites3.draw(0, 0); _subtitles.show(); @@ -1895,7 +1926,7 @@ int DwarfCutscene::show() { WAIT(1); } - sound.playSound(_mazeFlag ? "ok2.voc" : "back2.voc"); + sound.playSound(_keyFound ? "ok2.voc" : "back2.voc"); } else { sound.playSound("dwarf11.voc"); } @@ -1909,7 +1940,7 @@ int DwarfCutscene::show() { events.updateGameCounter(); do { sprites2.draw(0, 0); - sprites3.draw(0, g_vm->getRandomNumber(_isDarkCc ? 8 : 9)); + sprites3.draw(0, g_vm->getRandomNumber(_ccNum ? 8 : 9)); _subtitles.show(); events.timeMark5(); @@ -1923,7 +1954,7 @@ int DwarfCutscene::show() { exit: sprites2.draw(0, 0); - if (!_isDarkCc) + if (!_ccNum) sprites3.draw(0, 1); windows[0].update(); @@ -1944,14 +1975,37 @@ exit: void DwarfCutscene::getNewLocation() { Party &party = *g_vm->_party; - if (_isDarkCc) { + if (g_vm->getGameID() == GType_Swords) { + switch (party._mazeId) { + case 1: + if (party._questItems[0]) { + _mazeId = 53; + _mazePos = Common::Point(8, 1); + _mazeDir = DIR_NORTH; + _keyFound = true; + } + break; + + case 7: + if (party._questItems[1]) { + _mazeId = 92; + _mazePos = Common::Point(8, 1); + _mazeDir = DIR_NORTH; + _keyFound = true; + } + break; + + default: + break; + } + } else if (_ccNum) { switch (party._mazeId) { case 4: if (party._questItems[35]) { _mazeId = 29; _mazePos = Common::Point(15, 31); _mazeDir = DIR_SOUTH; - _mazeFlag = true; + _keyFound = true; } break; @@ -1960,7 +2014,7 @@ void DwarfCutscene::getNewLocation() { _mazeId = 35; _mazePos = Common::Point(15, 8); _mazeDir = DIR_WEST; - _mazeFlag = true; + _keyFound = true; } break; @@ -1969,7 +2023,7 @@ void DwarfCutscene::getNewLocation() { _mazeId = 31; _mazePos = Common::Point(31, 16); _mazeDir = DIR_WEST; - _mazeFlag = true; + _keyFound = true; } break; @@ -1978,7 +2032,7 @@ void DwarfCutscene::getNewLocation() { _mazeId = 33; _mazePos = Common::Point(0, 3); _mazeDir = DIR_EAST; - _mazeFlag = true; + _keyFound = true; } break; @@ -1987,7 +2041,7 @@ void DwarfCutscene::getNewLocation() { _mazeId = 37; _mazePos = Common::Point(7, 0); _mazeDir = DIR_NORTH; - _mazeFlag = true; + _keyFound = true; } break; @@ -2000,7 +2054,7 @@ void DwarfCutscene::getNewLocation() { _mazeId = 37; _mazePos = Common::Point(1, 4); _mazeDir = DIR_EAST; - _mazeFlag = true; + _keyFound = true; break; case 18: @@ -2013,7 +2067,7 @@ void DwarfCutscene::getNewLocation() { _mazePos = Common::Point(7, 1); _mazeDir = DIR_NORTH; } - _mazeFlag = true; + _keyFound = true; break; case 23: @@ -2026,7 +2080,7 @@ void DwarfCutscene::getNewLocation() { _mazePos = Common::Point(7, 30); _mazeDir = DIR_SOUTH; } - _mazeFlag = true; + _keyFound = true; break; default: @@ -2057,7 +2111,7 @@ int SphinxCutscene::show() { // Save background Graphics::ManagedSurface bgSurface; bgSurface.copyFrom(screen); - + for (int idx = 8; idx >= 0; --idx) { events.updateGameCounter(); screen.blitFrom(bgSurface); @@ -2069,14 +2123,14 @@ int SphinxCutscene::show() { sound.setMusicPercent(38); - for (int idx = 0; idx < (_mazeFlag ? 3 : 2); ++idx) { + for (int idx = 0; idx < (_keyFound ? 3 : 2); ++idx) { switch (idx) { case 0: - _subtitles.setLine(_mazeFlag ? 9 : 10); - sound.playSound(_mazeFlag ? "sphinx10.voc" : "sphinx13.voc"); + _subtitles.setLine(_keyFound ? 9 : 10); + sound.playSound(_keyFound ? "sphinx10.voc" : "sphinx13.voc"); break; case 1: - sound.playSound(_mazeFlag ? "sphinx11.voc" : "sphinx14.voc"); + sound.playSound(_keyFound ? "sphinx11.voc" : "sphinx14.voc"); break; case 2: sound.playSound("sphinx12.voc"); @@ -2100,7 +2154,7 @@ int SphinxCutscene::show() { sound.setMusicPercent(75); - if (!_mazeFlag) { + if (!_keyFound) { for (int idx = 0; idx < 8; ++idx) { screen.blitFrom(bgSurface); sprites1.draw(0, 0, Common::Point(SPHINX_X1[idx], SPHINX_Y1[idx]), 0, idx); @@ -2135,11 +2189,11 @@ void SphinxCutscene::getNewLocation() { switch (party._mazeId) { case 2: if (party._questItems[51]) { - map._loadDarkSide = true; + map._loadCcNum = 1; _mazeId = 125; _mazePos = Common::Point(7, 6); _mazeDir = DIR_NORTH; - _mazeFlag = true; + _keyFound = true; } break; @@ -2148,7 +2202,7 @@ void SphinxCutscene::getNewLocation() { _mazeId = 82; _mazePos = Common::Point(7, 5); _mazeDir = DIR_NORTH; - _mazeFlag = true; + _keyFound = true; } break; @@ -2172,7 +2226,7 @@ int PyramidLocation::show() { Common::Point pt; if (g_vm->getGameID() == GType_WorldOfXeen) { - if (_isDarkCc) { + if (_ccNum) { if (party._mazeId == 52) { mapId = 49; pt = Common::Point(7, 14); @@ -2192,7 +2246,7 @@ int PyramidLocation::show() { } // Load the destination map and set position and direction - map._loadDarkSide = !_isDarkCc; + map._loadCcNum = _ccNum ? 0 : 1; map.load(mapId); party._mazePosition = pt; party._mazeDirection = dir; @@ -2200,7 +2254,7 @@ int PyramidLocation::show() { // Playing Clouds or Dark Side on it's own, so can't switch sides Window &win = windows[12]; Common::String msg = Common::String::format(Res.MOONS_NOT_ALIGNED, - _isDarkCc ? "Clouds" : "Darkside"); + _ccNum ? "Clouds" : "Darkside"); win.open(); win.writeString(msg); win.update(); @@ -2219,9 +2273,12 @@ int PyramidLocation::show() { LocationManager::LocationManager() : _location(nullptr) { } -int LocationManager::doAction(LocationAction actionId) { +int LocationManager::doAction(int actionId) { + LocationAction action = (g_vm->getGameID() == GType_Swords && actionId > 13 && actionId < 18) ? + BLACKSMITH : (LocationAction)actionId; + // Create the desired location - switch (actionId) { + switch (action) { case BANK: _location = new Locations::BankLocation(); break; @@ -2264,6 +2321,7 @@ int LocationManager::doAction(LocationAction actionId) { } // Show the location + g_vm->_events->clearEvents(); int result = _location->show(); delete _location; _location = nullptr; @@ -2355,21 +2413,18 @@ bool LocationMessage::execute(int portrait, const Common::String &name, const Co do { events.clearEvents(); - events.updateGameCounter(); - if (msgEnd) - clearButtons(); + clearEvents(); do { - events.pollEventsAndWait(); - checkEvents(_vm); - - if (_vm->shouldExit()) - return false; - - while (events.timeElapsed() >= 3) { - drawAnim(false); - events.updateGameCounter(); + events.updateGameCounter(); + while (!_buttonValue && events.timeElapsed() < 3) { + events.pollEventsAndWait(); + checkEvents(_vm); + if (g_vm->shouldExit()) + return false; } + + drawAnim(false); } while (!_buttonValue); if (msgEnd) diff --git a/engines/xeen/locations.h b/engines/xeen/locations.h index 9b3e36eed7..ad0a8e2cee 100644 --- a/engines/xeen/locations.h +++ b/engines/xeen/locations.h @@ -47,7 +47,7 @@ protected: Common::Array<SpriteResource> _townSprites; SpriteResource _icons1, _icons2; int _townMaxId; - const bool &_isDarkCc; + const int &_ccNum; int _animFrame; Common::String _vocName, _songName; Common::Point _animPos; @@ -198,7 +198,7 @@ private: int _dayOfWeek; int _v10, _v11, _v12; int _v13, _v14; - bool _flag1; + bool _blessed; int _v5, _v6; protected: /** @@ -253,7 +253,7 @@ protected: int _mazeId; Direction _mazeDir; Common::Point _mazePos; - bool _mazeFlag; + bool _keyFound; protected: /** * Sets the new location @@ -364,7 +364,7 @@ public: /** * Show a given location, and return any result */ - int doAction(LocationAction actionId); + int doAction(int actionId); /** * Returns true if a town location (bank, blacksmith, etc.) is currently active diff --git a/engines/xeen/map.cpp b/engines/xeen/map.cpp index d8afa4d2a3..9d384c7c8f 100644 --- a/engines/xeen/map.cpp +++ b/engines/xeen/map.cpp @@ -52,7 +52,7 @@ MonsterStruct::MonsterStruct() { _specialAttack = SA_NONE; _hitChance = 0; _rangeAttack = 0; - _monsterType = MONSTER_0; + _monsterType = MONSTER_MONSTERS; _fireResistence = 0; _electricityResistence = 0; _coldResistence = 0; @@ -463,8 +463,6 @@ void MonsterObjectData::synchronize(XeenSerializer &s, MonsterData &monsterData) if (obj._id < (int)_objectSprites.size()) { obj._spriteId = _objectSprites[obj._id]._spriteId; obj._sprites = &_objectSprites[obj._id]._sprites; - } else { - assert(!obj._id); } _objects.push_back(obj); @@ -525,7 +523,8 @@ void MonsterObjectData::clearMonsterSprites() { void MonsterObjectData::addMonsterSprites(MazeMonster &monster) { Map &map = *g_vm->_map; - int imgNumber = map._monsterData[monster._spriteId]._imageNumber; + monster._monsterData = &map._monsterData[monster._spriteId]; + int imgNumber = monster._monsterData->_imageNumber; uint idx; // Find the sprites for the monster, loading them in if necessary @@ -604,12 +603,11 @@ void AnimationInfo::load(const Common::String &name) { /*------------------------------------------------------------------------*/ Map::Map(XeenEngine *vm) : _vm(vm), _mobData(vm) { - _loadDarkSide = false; + _loadCcNum = 0; _sideTownPortal = 0; _sideObjects = 0; _sideMonsters = 0; _sidePictures = 0; - _sideMusic = 0; _isOutdoors = false; _mazeDataIndex = 0; _currentSteppedOn = false; @@ -629,6 +627,7 @@ void Map::load(int mapId) { FileManager &files = *g_vm->_files; Interface &intf = *g_vm->_interface; Party &party = *g_vm->_party; + Patcher &patcher = *g_vm->_patcher; Sound &sound = *g_vm->_sound; IndoorDrawList &indoorList = intf._indoorList; OutdoorDrawList &outdoorList = intf._outdoorList; @@ -636,7 +635,7 @@ void Map::load(int mapId) { PleaseWait waitMsg(intf._falling); waitMsg.show(); - intf._objNumber = 0; + intf._objNumber = -1; party._stepped = true; party._mazeId = mapId; saveMaze(); @@ -648,12 +647,12 @@ void Map::load(int mapId) { if (mapId >= 113 && mapId <= 127) { _sideTownPortal = 0; } else { - _sideTownPortal = _loadDarkSide ? 1 : 0; + _sideTownPortal = _loadCcNum; } if (_vm->getGameID() == GType_Swords || _vm->getGameID() == GType_DarkSide) { _animationInfo.load("dark.dat"); - _monsterData.load("dark.mon"); + _monsterData.load((_vm->getGameID() == GType_Swords) ? "monsters.swd" : "dark.mon"); _wallPicSprites.load("darkpic.dat"); } else if (_vm->getGameID() == GType_Clouds) { _animationInfo.load("animinfo.cld"); @@ -662,7 +661,7 @@ void Map::load(int mapId) { } else if (_vm->getGameID() == GType_WorldOfXeen) { files.setGameCc(1); - if (!_loadDarkSide) { + if (!_loadCcNum) { _animationInfo.load("clouds.dat"); _monsterData.load("xeen.mon"); _wallPicSprites.load("xeenpic.dat"); @@ -709,15 +708,15 @@ void Map::load(int mapId) { } } - files.setGameCc(_loadDarkSide); + files.setGameCc(_loadCcNum); } // Load any events for the new map - loadEvents(mapId); + loadEvents(mapId, _loadCcNum); // Iterate through loading the given maze as well as the two successive // mazes in each of the four cardinal directions - bool isDarkCc = files._isDarkCc; + int ccNum = files._ccNum; MazeData *mazeDataP = &_mazeData[0]; bool textLoaded = false; @@ -735,9 +734,9 @@ void Map::load(int mapId) { mazeDataP->synchronize(datSer); datFile.close(); - if (isDarkCc && mapId == 50) + if (ccNum && mapId == 50) mazeDataP->setAllTilesStepped(); - if (!isDarkCc && party._gameFlags[0][25] && + if (!ccNum && party._gameFlags[0][25] && (mapId == 42 || mapId == 43 || mapId == 4)) { mazeDataP->clearCellSurfaces(); } @@ -747,20 +746,7 @@ void Map::load(int mapId) { // Handle loading text data if (!textLoaded) { textLoaded = true; - - if (g_vm->getGameID() == GType_Clouds) { - _mazeName = Res._cloudsMapNames[mapId]; - } else { - Common::String txtName = Common::String::format("%s%c%03d.txt", - isDarkCc ? "dark" : "xeen", mapId >= 100 ? 'x' : '0', mapId); - File fText(txtName, 1); - char mazeName[33]; - fText.read(mazeName, 33); - mazeName[32] = '\0'; - - _mazeName = Common::String(mazeName); - fText.close(); - } + _mazeName = getMazeName(mapId, ccNum); // Load the monster/object data Common::String mobName = Common::String::format("maze%c%03d.mob", @@ -776,7 +762,7 @@ void Map::load(int mapId) { _headData.synchronize(headFile); headFile.close(); - if (!isDarkCc && mapId == 15) { + if (!ccNum && mapId == 15) { if ((_mobData._monsters[0]._position.x > 31 || _mobData._monsters[0]._position.y > 31) && (_mobData._monsters[1]._position.x > 31 || _mobData._monsters[1]._position.y > 31) && (_mobData._monsters[2]._position.x > 31 || _mobData._monsters[2]._position.y > 31)) { @@ -808,13 +794,13 @@ void Map::load(int mapId) { for (uint i = 0; i < _mobData._objectSprites.size(); ++i) { files.setGameCc(_sideObjects); - if (party._cloudsEnd && _mobData._objectSprites[i]._spriteId == 85 && - mapId == 27 && isDarkCc) { + if (party._cloudsCompleted && _mobData._objectSprites[i]._spriteId == 85 && + mapId == 27 && ccNum) { _mobData._objects[29]._spriteId = 0; _mobData._objects[29]._id = 8; _mobData._objectSprites[i]._sprites.clear(); } else if (mapId == 12 && party._gameFlags[0][43] && - _mobData._objectSprites[i]._spriteId == 118 && !isDarkCc) { + _mobData._objectSprites[i]._spriteId == 118 && !ccNum) { filename = "085.obj"; _mobData._objectSprites[0]._spriteId = 85; } else { @@ -847,15 +833,15 @@ void Map::load(int mapId) { _mobData._wallItemSprites[i]._sprites.load(filename, _sidePictures); } - files.setGameCc(isDarkCc); + files.setGameCc(ccNum); // Handle loading miscellaneous sprites for the map if (_isOutdoors) { // Start playing relevant music - sound._musicSide = isDarkCc; + sound._musicSide = ccNum; Common::String musName; - if (_vm->_files->_isDarkCc) { + if (_vm->_files->_ccNum) { int randIndex = _vm->getRandomNumber(6); musName = Res.MUSIC_FILES2[_mazeData->_wallKind][randIndex]; } else { @@ -884,16 +870,15 @@ void Map::load(int mapId) { _surfaceSprites[i].load(Res.SURFACE_NAMES[_mazeData[0]._surfaceTypes[i]]); } } else { - if (files._isDarkCc && (mapId == 125 || mapId == 126 || mapId == 127)) + if (files._ccNum && (mapId == 125 || mapId == 126 || mapId == 127)) files.setGameCc(0); - sound._musicSide = files._isDarkCc; + sound._musicSide = files._ccNum; // Start playing relevant music const int MUS_INDEXES[] = { 1, 2, 3, 4, 3, 5 }; Common::String musName; - _sideMusic = isDarkCc; - if (isDarkCc) { + if (files._ccNum) { int randIndex = _vm->getRandomNumber(6); musName = Res.MUSIC_FILES2[MUS_INDEXES[_mazeData->_wallKind]][randIndex]; } else { @@ -987,7 +972,7 @@ void Map::load(int mapId) { indoorList._ground._sprites = &_groundSprites; // Don't show horizon for certain maps - if (_vm->_files->_isDarkCc) { + if (_vm->_files->_ccNum) { if ((mapId >= 89 && mapId <= 112) || mapId == 128 || mapId == 129) indoorList._horizon._sprites = nullptr; } else { @@ -996,9 +981,10 @@ void Map::load(int mapId) { } } + patcher.patch(); loadSky(); - files.setGameCc(isDarkCc); + files.setGameCc(ccNum); } void Map::findMap(int mapId) { @@ -1066,7 +1052,8 @@ int Map::mazeLookup(const Common::Point &pt, int layerShift, int wallMask) { _currentSurfaceId = _mazeData[_mazeDataIndex]._cells[pos.y][pos.x]._surfaceId; } - if (_currentSurfaceId == SURFTYPE_SPACE || _currentSurfaceId == SURFTYPE_SKY) { + if (mazeData()._surfaceTypes[_currentSurfaceId] == SURFTYPE_SPACE || + mazeData()._surfaceTypes[_currentSurfaceId] == SURFTYPE_SKY) { _currentSteppedOn = true; } else { _currentSteppedOn = _mazeData[_mazeDataIndex]._steppedOnTiles[pos.y][pos.x]; @@ -1080,11 +1067,11 @@ int Map::mazeLookup(const Common::Point &pt, int layerShift, int wallMask) { } } -void Map::loadEvents(int mapId) { +void Map::loadEvents(int mapId, int ccNum) { // Load events Common::String filename = Common::String::format("maze%c%03d.evt", (mapId >= 100) ? 'x' : '0', mapId); - File fEvents(filename); + File fEvents(filename, ccNum); XeenSerializer sEvents(&fEvents, nullptr); _events.synchronize(sEvents); fEvents.close(); @@ -1092,7 +1079,7 @@ void Map::loadEvents(int mapId) { // Load text data filename = Common::String::format("aaze%c%03d.txt", (mapId >= 100) ? 'x' : '0', mapId); - File fText(filename); + File fText(filename, ccNum); _events._text.clear(); while (fText.pos() < fText.size()) _events._text.push_back(fText.readString()); @@ -1114,7 +1101,7 @@ void Map::saveMap() { FileManager &files = *g_vm->_files; Party &party = *g_vm->_party; int mapId = _mazeData[0]._mazeId; - if (!files._isDarkCc && mapId == 85) + if (!files._ccNum && mapId == 85) return; // Save the primary maze @@ -1124,7 +1111,7 @@ void Map::saveMap() { _mazeData[0].synchronize(datSer); datFile.finalize(); - if (!files._isDarkCc && mapId == 15) { + if (!files._ccNum && mapId == 15) { for (uint idx = 0; idx < MIN(_mobData._monsters.size(), (uint)3); ++idx) { MazeMonster &mon = _mobData._monsters[idx]; if (mon._position.x > 31 || mon._position.y > 31) { @@ -1162,7 +1149,7 @@ void Map::saveMonsters() { void Map::saveMaze() { int mazeNum = _mazeData[0]._mazeNumber; - if (!mazeNum || (mazeNum == 85 && !_vm->_files->_isDarkCc)) + if (!mazeNum || (mazeNum == 85 && !_vm->_files->_ccNum)) return; saveEvents(); @@ -1236,7 +1223,8 @@ void Map::setWall(const Common::Point &pt, Direction dir, int v) { } int Map::getCell(int idx) { - int mapId = _vm->_party->_mazeId; + Party &party = *g_vm->_party; + int mapId = party._mazeId; Direction dir = _vm->_party->_mazeDirection; Common::Point pt( _vm->_party->_mazePosition.x + Res.SCREEN_POSITIONING_X[_vm->_party->_mazeDirection][idx], @@ -1244,7 +1232,7 @@ int Map::getCell(int idx) { ); if (pt.x > 31 || pt.y > 31) { - if (_vm->_files->_isDarkCc) { + if (_vm->_files->_ccNum) { if ((mapId >= 53 && mapId <= 88 && mapId != 73) || (mapId >= 74 && mapId <= 120) || mapId == 125 || mapId == 126 || mapId == 128 || mapId == 129) { _currentSurfaceId = SURFTYPE_DESERT; @@ -1270,12 +1258,14 @@ int Map::getCell(int idx) { } if (!mapId) { + mapId = party._mazeId; + if (_isOutdoors) { _currentSurfaceId = SURFTYPE_SPACE; _currentWall = 0; return 0; } else { - if (_vm->_files->_isDarkCc) { + if (_vm->_files->_ccNum) { if ((mapId >= 53 && mapId <= 88 && mapId != 73) || (mapId >= 74 && mapId <= 120) || mapId == 125 || mapId == 126 || mapId == 128 || mapId == 129) { _currentSurfaceId = 6; @@ -1304,12 +1294,14 @@ int Map::getCell(int idx) { } if (!mapId) { + mapId = party._mazeId; + if (_isOutdoors) { _currentSurfaceId = SURFTYPE_SPACE; _currentWall = 0; return 0; } else { - if (_vm->_files->_isDarkCc) { + if (_vm->_files->_ccNum) { if ((mapId >= 53 && mapId <= 88 && mapId != 73) || (mapId >= 74 && mapId <= 120) || mapId == 125 || mapId == 126 || mapId == 128 || mapId == 129) { _currentSurfaceId = 6; @@ -1404,4 +1396,24 @@ void Map::getNewMaze() { load(mapId); } +Common::String Map::getMazeName(int mapId, int ccNum) { + if (ccNum == -1) + ccNum = g_vm->_files->_ccNum; + + if (g_vm->getGameID() == GType_Clouds) { + return Res._cloudsMapNames[mapId]; + } else { + Common::String txtName = Common::String::format("%s%c%03d.txt", + ccNum ? "dark" : "xeen", mapId >= 100 ? 'x' : '0', mapId); + File fText(txtName, 1); + char mazeName[33]; + fText.read(mazeName, 33); + mazeName[32] = '\0'; + + Common::String name = Common::String(mazeName); + fText.close(); + return name; + } +} + } // End of namespace Xeen diff --git a/engines/xeen/map.h b/engines/xeen/map.h index a0bc77a7ec..1c00e549c3 100644 --- a/engines/xeen/map.h +++ b/engines/xeen/map.h @@ -42,11 +42,15 @@ namespace Xeen { class XeenEngine; enum MonsterType { - MONSTER_0 = 0, MONSTER_ANIMAL = 1, MONSTER_INSECT = 2, + MONSTER_MONSTERS = 0, MONSTER_ANIMAL = 1, MONSTER_INSECT = 2, MONSTER_HUMANOID = 3, MONSTER_UNDEAD = 4, MONSTER_GOLEM = 5, MONSTER_DRAGON = 6 }; +enum MapId { + XEEN_CASTLE1 = 75, XEEN_CASTLE4 = 78 +}; + class MonsterStruct { public: Common::String _name; @@ -165,16 +169,16 @@ enum SurfaceType { union MazeWallLayers { struct MazeWallIndoors { - int _wallNorth : 4; - int _wallEast : 4; - int _wallSouth : 4; - int _wallWest : 4; + uint _wallNorth : 4; + uint _wallEast : 4; + uint _wallSouth : 4; + uint _wallWest : 4; } _indoors; struct MazeWallOutdoors { - SurfaceType _surfaceId : 4; - int _iMiddle : 4; - int _iTop : 4; - int _iOverlay : 4; + uint _surfaceId : 4; // SurfaceType, but needs to be unsigned + uint _iMiddle : 4; + uint _iTop : 4; + uint _iOverlay : 4; } _outdoors; uint16 _data; }; @@ -318,7 +322,6 @@ public: }; private: XeenEngine *_vm; - Common::Array<SpriteResourceEntry> _objectSprites; Common::Array<SpriteResourceEntry> _monsterSprites; Common::Array<SpriteResourceEntry> _monsterAttackSprites; Common::Array<SpriteResourceEntry> _wallItemSprites; @@ -326,6 +329,7 @@ public: Common::Array<MazeObject> _objects; Common::Array<MazeMonster> _monsters; Common::Array<MazeWallItem> _wallItems; + Common::Array<SpriteResourceEntry> _objectSprites; public: MonsterObjectData(XeenEngine *vm); @@ -403,13 +407,12 @@ private: int _sidePictures; int _sideObjects; int _sideMonsters; - int _sideMusic; int _mazeDataIndex; /** * Load the events for a new map */ - void loadEvents(int mapId); + void loadEvents(int mapId, int ccNum); /** * Save the events for a map @@ -454,7 +457,7 @@ public: int _currentTile; int _currentSurfaceId; bool _currentSteppedOn; - bool _loadDarkSide; + int _loadCcNum; int _sideTownPortal; public: Map(XeenEngine *vm); @@ -516,6 +519,13 @@ public: * position to the relative position on the new map */ void getNewMaze(); + + /** + * Return the name of a specified maze + * @param mapId Map Id + * @param ccNum Cc file number. If -1, uses the current C + */ + static Common::String getMazeName(int mapId, int ccNum = -1); }; } // End of namespace Xeen diff --git a/engines/xeen/module.mk b/engines/xeen/module.mk index 0154b8533b..5b3f69710f 100644 --- a/engines/xeen/module.mk +++ b/engines/xeen/module.mk @@ -6,7 +6,6 @@ MODULE_OBJS := \ worldofxeen/worldofxeen_cutscenes.o \ worldofxeen/worldofxeen_menu.o \ worldofxeen/worldofxeen.o \ - worldofxeen/worldofxeen_resources.o \ swordsofxeen/swordsofxeen.o \ swordsofxeen/swordsofxeen_menu.o \ dialogs/credits_screen.o \ @@ -14,6 +13,7 @@ MODULE_OBJS := \ dialogs/dialogs_awards.o \ dialogs/dialogs_char_info.o \ dialogs/dialogs_control_panel.o \ + dialogs/dialogs_copy_protection.o \ dialogs/dialogs_create_char.o \ dialogs/dialogs_difficulty.o \ dialogs/dialogs_dismiss.o \ @@ -46,6 +46,7 @@ MODULE_OBJS := \ locations.o \ map.o \ party.o \ + patcher.o \ resources.o \ saves.o \ screen.o \ diff --git a/engines/xeen/party.cpp b/engines/xeen/party.cpp index 3f0cdf0433..1fee0980d2 100644 --- a/engines/xeen/party.cpp +++ b/engines/xeen/party.cpp @@ -54,6 +54,14 @@ Roster::Roster() { } void Roster::synchronize(Common::Serializer &s) { + Party &party = *g_vm->_party; + + if (s.isSaving()) { + // Copy out the party's characters back to the roster + for (uint idx = 0; idx < party._activeParty.size(); ++idx) + (*this)[party._activeParty[idx]._rosterId] = party._activeParty[idx]; + } + for (uint i = 0; i < TOTAL_CHARACTERS; ++i) (*this)[i].synchronize(s); } @@ -70,6 +78,21 @@ Treasure::Treasure() { _categories[3] = &_misc[0]; } +void Treasure::clear() { + for (int idx = 0; idx < MAX_TREASURE_ITEMS; ++idx) { + _weapons[idx].clear(); + _armor[idx].clear(); + _accessories[idx].clear(); + _misc[idx].clear(); + } +} + +void Treasure::reset() { + clear(); + _hasItems = false; + _gold = _gems = 0; +} + /*------------------------------------------------------------------------*/ const int BLACKSMITH_DATA1[4][4] = { @@ -104,9 +127,7 @@ void BlacksmithWares::regenerate() { ItemCategory itemCat = tempChar.makeItem(idx2 + 1, 0, 0); if (catCount[itemCat] < 8) { XeenItem &item = (*this)[itemCat][0][slotNum][catCount[itemCat]]; - item._id = tempChar._weapons[0]._id; - item._material = tempChar._weapons[0]._material; - item._bonusFlags = tempChar._weapons[0]._bonusFlags; + item = tempChar._items[itemCat][0]; ++catCount[itemCat]; } @@ -123,9 +144,7 @@ void BlacksmithWares::regenerate() { ItemCategory itemCat = tempChar.makeItem(idx2 + (slotNum >= 2 ? 3 : 1), 0, 0); if (catCount[itemCat] < 8) { XeenItem &item = (*this)[itemCat][1][slotNum][catCount[itemCat]]; - item._id = tempChar._misc[0]._id; - item._material = tempChar._misc[0]._material; - item._bonusFlags = tempChar._misc[0]._bonusFlags; + item = tempChar._items[itemCat][0]; ++catCount[itemCat]; } @@ -135,21 +154,21 @@ void BlacksmithWares::regenerate() { } void BlacksmithWares::blackData2CharData(Character &c) { - bool isDarkCc = g_vm->_files->_isDarkCc; + int ccNum = g_vm->_files->_ccNum; int slotIndex = getSlotIndex(); for (ItemCategory cat = CATEGORY_WEAPON; cat <= CATEGORY_MISC; cat = (ItemCategory)((int)cat + 1)) for (int idx = 0; idx < INV_ITEMS_TOTAL; ++idx) - c._items[cat][idx] = (*this)[cat][isDarkCc][slotIndex][idx]; + c._items[cat][idx] = (*this)[cat][ccNum][slotIndex][idx]; } void BlacksmithWares::charData2BlackData(Character &c) { - bool isDarkCc = g_vm->_files->_isDarkCc; + int ccNum = g_vm->_files->_ccNum; int slotIndex = getSlotIndex(); for (ItemCategory cat = CATEGORY_WEAPON; cat <= CATEGORY_MISC; cat = (ItemCategory)((int)cat + 1)) for (int idx = 0; idx < INV_ITEMS_TOTAL; ++idx) - (*this)[cat][isDarkCc][slotIndex][idx] = c._items[cat][idx]; + (*this)[cat][ccNum][slotIndex][idx] = c._items[cat][idx]; } BlacksmithItems &BlacksmithWares::operator[](ItemCategory category) { @@ -163,10 +182,10 @@ BlacksmithItems &BlacksmithWares::operator[](ItemCategory category) { uint BlacksmithWares::getSlotIndex() const { Party &party = *g_vm->_party; - bool isDarkCc = g_vm->_files->_isDarkCc; + int ccNum = g_vm->_files->_ccNum; int slotIndex = 0; - while (slotIndex < 4 && party._mazeId != (int)Res.BLACKSMITH_MAP_IDS[isDarkCc][slotIndex]) + while (slotIndex < 4 && party._mazeId != (int)Res.BLACKSMITH_MAP_IDS[ccNum][slotIndex]) ++slotIndex; if (slotIndex == 4) slotIndex = 0; @@ -199,9 +218,9 @@ Party::Party(XeenEngine *vm) { _holyBonus = 0; _heroism = 0; _difficulty = ADVENTURER; - _cloudsEnd = false; - _darkSideEnd = false; - _worldEnd = false; + _cloudsCompleted = false; + _darkSideCompleted = false; + _worldCompleted = false; _ctr24 = 0; _day = 0; _year = 0; @@ -226,14 +245,12 @@ Party::Party(XeenEngine *vm) { Common::fill(&_gameFlags[0][0], &_gameFlags[0][256], false); Common::fill(&_gameFlags[1][0], &_gameFlags[1][256], false); Common::fill(&_worldFlags[0], &_worldFlags[128], false); - Common::fill(&_questFlags[0][0], &_questFlags[0][30], false); - Common::fill(&_questFlags[1][0], &_questFlags[1][30], false); + Common::fill(&_questFlags[0], &_questFlags[60], false); Common::fill(&_questItems[0], &_questItems[85], 0); for (int i = 0; i < TOTAL_CHARACTERS; ++i) Common::fill(&_characterFlags[i][0], &_characterFlags[i][24], false); - _partyDead = false; _newDay = false; _isNight = false; _stepped = false; @@ -287,9 +304,9 @@ void Party::synchronize(Common::Serializer &s) { _blacksmithWares.synchronize(s, 0); - s.syncAsUint16LE(_cloudsEnd); - s.syncAsUint16LE(_darkSideEnd); - s.syncAsUint16LE(_worldEnd); + s.syncAsUint16LE(_cloudsCompleted); + s.syncAsUint16LE(_darkSideCompleted); + s.syncAsUint16LE(_worldCompleted); s.syncAsUint16LE(_ctr24); s.syncAsUint16LE(_day); s.syncAsUint16LE(_year); @@ -313,8 +330,7 @@ void Party::synchronize(Common::Serializer &s) { File::syncBitFlags(s, &_gameFlags[0][0], &_gameFlags[0][256]); File::syncBitFlags(s, &_gameFlags[1][0], &_gameFlags[1][256]); File::syncBitFlags(s, &_worldFlags[0], &_worldFlags[128]); - File::syncBitFlags(s, &_questFlags[0][0], &_questFlags[0][30]); - File::syncBitFlags(s, &_questFlags[1][0], &_questFlags[1][30]); + File::syncBitFlags(s, &_questFlags[0], &_questFlags[60]); for (int i = 0; i < 85; ++i) s.syncAsByte(_questItems[i]); @@ -324,6 +340,10 @@ void Party::synchronize(Common::Serializer &s) { for (int i = 0; i < TOTAL_CHARACTERS; ++i) File::syncBitFlags(s, &_characterFlags[i][0], &_characterFlags[i][24]); s.syncBytes(&dummy[0], 30); + + if (s.isLoading()) + _newDay = _minutes < 300; + _dead = false; } void Party::loadActiveParty() { @@ -444,8 +464,12 @@ void Party::changeTime(int numMinutes) { } } - player._conditions[WEAK] = player._conditions[DRUNK]; - player._conditions[DRUNK] = 0; + // WORKAROUND: Original incorrectly reset weakness (due to lack of sleep) even when party + // wasn't drunk. We now have any resetting drunkness add to, rather than replace, weakness + if (player._conditions[WEAK] != -1) { + player._conditions[WEAK] += player._conditions[DRUNK]; + player._conditions[DRUNK] = 0; + } if (player._conditions[DEPRESSED]) { player._conditions[DEPRESSED] = (player._conditions[DEPRESSED] + 1) % 4; @@ -502,7 +526,7 @@ void Party::addTime(int numMinutes) { _newDay = true; if (_newDay && _minutes >= 300) { - if (_vm->_mode != MODE_RECORD_EVENTS && _vm->_mode != MODE_17) { + if (_vm->_mode != MODE_SCRIPT_IN_PROGRESS && _vm->_mode != MODE_INTERACTIVE7) { resetTemps(); if (_rested || _vm->_mode == MODE_SLEEPING) { _rested = false; @@ -673,11 +697,8 @@ void Party::giveTreasure() { if (!_treasure._hasItems && !_treasure._gold && !_treasure._gems) return; - bool monstersPresent = false; - for (int idx = 0; idx < 26 && !monstersPresent; ++idx) - monstersPresent = combat._attackMonsters[idx] != -1; - - if (_vm->_mode != MODE_RECORD_EVENTS && monstersPresent) + bool monstersPresent = combat.areMonstersPresent(); + if (_vm->_mode != MODE_SCRIPT_IN_PROGRESS && monstersPresent) return; combat.clearShooting(); @@ -702,18 +723,13 @@ void Party::giveTreasure() { for (int categoryNum = 0; categoryNum < NUM_ITEM_CATEGORIES; ++categoryNum) { for (int itemNum = 0; itemNum < MAX_TREASURE_ITEMS; ++itemNum) { if (arePacksFull()) { - if (_treasure._weapons[itemNum]._id == 34) { - // Important item, so clear a slot for it + if (_treasure._weapons[itemNum]._id >= XEEN_SLAYER_SWORD) { + // Xeen Slayer Sword, so clear a slot for it _activeParty[0]._weapons[INV_ITEMS_TOTAL - 1].clear(); } else { // Otherwise, clear all the remaining treasure items, // since all the party's packs are full - for (int idx = 0; idx < MAX_TREASURE_ITEMS; ++idx) { - _treasure._weapons[idx].clear(); - _treasure._armor[idx].clear(); - _treasure._accessories[idx].clear(); - _treasure._armor[idx].clear(); - } + _treasure.clear(); } } @@ -767,7 +783,7 @@ void Party::giveTreasure() { events.clearEvents(); if (_vm->_mode != MODE_COMBAT) - _vm->_mode = MODE_1; + _vm->_mode = MODE_INTERACTIVE; w.close(); _gold += _treasure._gold; @@ -776,13 +792,7 @@ void Party::giveTreasure() { _treasure._gems = 0; _treasure._hasItems = false; - for (int idx = 0; idx < MAX_TREASURE_ITEMS; ++idx) { - _treasure._weapons[idx].clear(); - _treasure._armor[idx].clear(); - _treasure._accessories[idx].clear(); - _treasure._armor[idx].clear(); - } - + _treasure.clear(); combat._combatTarget = 1; } @@ -790,10 +800,10 @@ bool Party::arePacksFull() const { uint total = 0; for (uint idx = 0; idx < _activeParty.size(); ++idx) { const Character &c = _activeParty[idx]; - total += (c._weapons[INV_ITEMS_TOTAL - 1]._id != 0 ? 1 : 0) - + (c._armor[INV_ITEMS_TOTAL - 1]._id != 0 ? 1 : 0) - + (c._accessories[INV_ITEMS_TOTAL - 1]._id != 0 ? 1 : 0) - + (c._misc[INV_ITEMS_TOTAL - 1]._id != 0 ? 1 : 0); + total += (c._weapons[INV_ITEMS_TOTAL - 1].empty() ? 0 : 1) + + (c._armor[INV_ITEMS_TOTAL - 1].empty() ? 0 : 1) + + (c._accessories[INV_ITEMS_TOTAL - 1].empty() ? 0 : 1) + + (c._misc[INV_ITEMS_TOTAL - 1].empty() ? 0 : 1); } return total == (_activeParty.size() * NUM_ITEM_CATEGORIES); @@ -816,11 +826,20 @@ void Party::giveTreasureToCharacter(Character &c, ItemCategory category, int ite w.update(); events.ipause(5); - const char *itemName = XeenItem::getItemName(category, treasureItem._id); - w.writeString(Common::String::format(Res.X_FOUND_Y, c._name.c_str(), itemName)); + int index = (category == CATEGORY_MISC) ? treasureItem._material : treasureItem._id; + const char *itemName = XeenItem::getItemName(category, index); + + if (index >= (_vm->getGameID() == GType_Swords ? 88 : 82)) { + // Quest item, give an extra '*' prefix + Common::String format = Common::String::format("\f04 * \fd%s", itemName); + w.writeString(Common::String::format(Res.X_FOUND_Y, c._name.c_str(), format.c_str())); + } else { + w.writeString(Common::String::format(Res.X_FOUND_Y, c._name.c_str(), itemName)); + } + w.update(); c._items[category].sort(); - events.ipause(5); + events.ipause(8); } bool Party::canShoot() const { @@ -880,7 +899,7 @@ bool Party::giveTake(int takeMode, uint takeVal, int giveMode, uint giveVal, int ps._tempAge -= takeVal; break; case 13: - ps._skills[THIEVERY] = 0; + ps._skills[takeVal] = 0; break; case 15: ps.setAward(takeVal, false); @@ -895,26 +914,11 @@ bool Party::giveTake(int takeMode, uint takeVal, int giveMode, uint giveVal, int ps._conditions[takeVal] = 0; break; case 19: { - int idx2 = 0; - switch (ps._class) { - case CLASS_PALADIN: - case CLASS_CLERIC: - idx2 = 0; - break; - case CLASS_ARCHER: - case CLASS_SORCERER: - idx2 = 1; - break; - case CLASS_DRUID: - case CLASS_RANGER: - idx2 = 2; - break; - default: - break; - } + SpellsCategory category = ps.getSpellsCategory(); + assert(category != SPELLCAT_INVALID); - for (int idx = 0; idx < 39; ++idx) { - if (Res.SPELLS_ALLOWED[idx2][idx] == (int)takeVal) { + for (int idx = 0; idx < SPELLS_PER_CLASS; ++idx) { + if (Res.SPELLS_ALLOWED[category][idx] == (int)takeVal) { ps._spells[idx] = false; break; } @@ -922,45 +926,53 @@ bool Party::giveTake(int takeMode, uint takeVal, int giveMode, uint giveVal, int break; } case 20: - _gameFlags[files._isDarkCc][takeVal] = false; + assert(takeVal < 256); + _gameFlags[_vm->getGameID() == GType_Swords ? 0 : files._ccNum][takeVal] = false; break; case 21: { - bool found = false; - for (int idx = 0; idx < 9; ++idx) { - if (takeVal < 35) { - if (ps._weapons[idx]._id == takeVal) { - ps._weapons[idx].clear(); - ps._weapons.sort(); - found = true; - break; - } - } else if (takeVal < 49) { - if (ps._armor[idx]._id == (takeVal - 35)) { - ps._armor[idx].clear(); - ps._armor.sort(); - found = true; - break; - } - } else if (takeVal < 60) { - if (ps._accessories[idx]._id == (takeVal - 49)) { - ps._accessories[idx].clear(); - ps._accessories.sort(); - found = true; - break; - } - } else if (takeVal < 82) { - if (ps._misc[idx]._material == ((int)takeVal - 60)) { - ps._misc[idx].clear(); - ps._misc.sort(); - found = true; - break; + const uint WEAPONS_END = _vm->getGameID() != GType_Swords ? 35 : 41; + const uint ARMOR_END = _vm->getGameID() != GType_Swords ? 49 : 55; + const uint ACCESSORIES_END = _vm->getGameID() != GType_Swords ? 60 : 66; + const uint MISC_END = _vm->getGameID() != GType_Swords ? 82 : 88; + + if (takeVal >= MISC_END) { + _questItems[takeVal - MISC_END]--; + } else { + bool found = false; + for (int idx = 0; idx < 9; ++idx) { + if (takeVal < WEAPONS_END) { + if (ps._weapons[idx]._id == takeVal) { + ps._weapons[idx].clear(); + ps._weapons.sort(); + found = true; + break; + } + } else if (takeVal < ARMOR_END) { + if (ps._armor[idx]._id == (takeVal - WEAPONS_END)) { + ps._armor[idx].clear(); + ps._armor.sort(); + found = true; + break; + } + } else if (takeVal < ACCESSORIES_END) { + if (ps._accessories[idx]._id == (takeVal - ARMOR_END)) { + ps._accessories[idx].clear(); + ps._accessories.sort(); + found = true; + break; + } + } else { + if (ps._misc[idx]._material == (int)(takeVal - ACCESSORIES_END)) { + ps._misc[idx].clear(); + ps._misc.sort(); + found = true; + break; + } } - } else { - _questItems[takeVal - 82]--; } + if (!found) + return true; } - if (!found) - return true; break; } case 25: @@ -1099,7 +1111,7 @@ bool Party::giveTake(int takeMode, uint takeVal, int giveMode, uint giveVal, int _worldFlags[takeVal] = false; break; case 104: - _questFlags[files._isDarkCc][takeVal] = false; + _questFlags[(_vm->getGameID() == GType_Swords ? 0 : files._ccNum * 30) + takeVal] = false; break; case 107: _characterFlags[ps._rosterId][takeVal] = false; @@ -1137,6 +1149,7 @@ bool Party::giveTake(int takeMode, uint takeVal, int giveMode, uint giveVal, int case 13: assert(giveVal < 18); ps._skills[giveVal]++; + intf.spellFX(&ps); break; case 15: ps.setAward(giveVal, true); @@ -1164,66 +1177,59 @@ bool Party::giveTake(int takeMode, uint takeVal, int giveMode, uint giveVal, int ps._currentHp = 0; break; case 19: { - int idx2 = 0; - switch (ps._class) { - case CLASS_PALADIN: - case CLASS_CLERIC: - idx2 = 0; - break; - case CLASS_ARCHER: - case CLASS_SORCERER: - idx2 = 1; - break; - case CLASS_DRUID: - case CLASS_RANGER: - idx2 = 2; - break; - default: - break; - } - - for (int idx = 0; idx < 39; ++idx) { - if (Res.SPELLS_ALLOWED[idx2][idx] == (int)giveVal) { - ps._spells[idx] = true; - intf.spellFX(&ps); - break; + // Give spell to character + SpellsCategory category = ps.getSpellsCategory(); + + if (category != SPELLCAT_INVALID) { + for (int idx = 0; idx < SPELLS_PER_CLASS; ++idx) { + if (Res.SPELLS_ALLOWED[category][idx] == (int)giveVal) { + ps._spells[idx] = true; + intf.spellFX(&ps); + break; + } } } break; } case 20: - _gameFlags[files._isDarkCc][giveVal] = true; + assert(giveVal < 256); + _gameFlags[_vm->getGameID() == GType_Swords ? 0 : files._ccNum][giveVal] = true; break; case 21: { + const uint WEAPONS_END = _vm->getGameID() != GType_Swords ? 35 : 41; + const uint ARMOR_END = _vm->getGameID() != GType_Swords ? 49 : 55; + const uint ACCESSORIES_END = _vm->getGameID() != GType_Swords ? 60 : 66; + const uint MISC_END = _vm->getGameID() != GType_Swords ? 82 : 88; + int idx; - if (giveVal >= 82) { - _questItems[giveVal - 82]++; + if (giveVal >= MISC_END) { + _questItems[giveVal - MISC_END]++; } - if (giveVal < 35 || giveVal >= 82) { - for (idx = 0; idx < 10 && _treasure._weapons[idx]._id; ++idx); - if (idx < 10) { + if (giveVal < WEAPONS_END || giveVal >= MISC_END) { + for (idx = 0; idx < MAX_TREASURE_ITEMS && !_treasure._weapons[idx].empty(); ++idx) {} + if (idx < MAX_TREASURE_ITEMS) { _treasure._weapons[idx]._id = giveVal; _treasure._hasItems = true; return false; } - } else if (giveVal < 49) { - for (idx = 0; idx < 10 && _treasure._armor[idx]._id; ++idx); - if (idx < 10) { - _treasure._armor[idx]._id = giveVal - 35; + } else if (giveVal < ARMOR_END) { + for (idx = 0; idx < MAX_TREASURE_ITEMS && !_treasure._armor[idx].empty(); ++idx) {} + if (idx < MAX_TREASURE_ITEMS) { + _treasure._armor[idx]._id = giveVal - WEAPONS_END; _treasure._hasItems = true; return false; } - } else if (giveVal < 60) { - for (idx = 0; idx < 10 && _treasure._accessories[idx]._id; ++idx); - if (idx < 10) { - _treasure._accessories[idx]._id = giveVal - 49; + } else if (giveVal < ACCESSORIES_END) { + for (idx = 0; idx < MAX_TREASURE_ITEMS && !_treasure._accessories[idx].empty(); ++idx) {} + if (idx < MAX_TREASURE_ITEMS) { + _treasure._accessories[idx]._id = giveVal - ARMOR_END; _treasure._hasItems = true; return false; } } else { - for (idx = 0; idx < 10 && _treasure._misc[idx]._material; ++idx); - if (idx < 10) { - _treasure._accessories[idx]._material = giveVal - 60; + for (idx = 0; idx < MAX_TREASURE_ITEMS && _treasure._misc[idx]._material; ++idx) {} + if (idx < MAX_TREASURE_ITEMS) { + _treasure._accessories[idx]._material = giveVal - ACCESSORIES_END; _treasure._hasItems = true; return false; } @@ -1355,7 +1361,7 @@ bool Party::giveTake(int takeMode, uint takeVal, int giveMode, uint giveVal, int Character &tempChar = _itemsCharacter; int idx = -1; if (scripts._itemType != 0) { - for (idx = 0; idx < 10 && _treasure._misc[idx]._material; ++idx); + for (idx = 0; idx < 10 && _treasure._misc[idx]._material; ++idx) {} if (idx == 10) return true; } @@ -1366,14 +1372,14 @@ bool Party::giveTake(int takeMode, uint takeVal, int giveMode, uint giveVal, int XeenItem *trItems = _treasure[itemCat]; // Check for a free treasure slot - for (idx = 0; idx < 10 && trItems[idx]._id; ++idx); + for (idx = 0; idx < 10 && trItems[idx]._id; ++idx) {} if (idx == 10) return true; // Found a free slot, so copy the created item into it trItems[idx]._material = srcItem._material; trItems[idx]._id = srcItem._id; - trItems[idx]._bonusFlags = srcItem._bonusFlags; + trItems[idx]._state = srcItem._state; _treasure._hasItems = true; break; } @@ -1435,16 +1441,16 @@ bool Party::giveTake(int takeMode, uint takeVal, int giveMode, uint giveVal, int _gold += _vm->getRandomNumber(1, giveVal); break; case 103: - assert(takeVal < 128); - _worldFlags[takeVal] = true; + assert(giveVal < (uint)(_vm->getGameID() == GType_Swords ? 49 : 128)); + _worldFlags[giveVal] = true; break; case 104: - assert(giveVal < 30); - _questFlags[files._isDarkCc][giveVal] = true; + assert(giveVal < (uint)(_vm->getGameID() == GType_Swords ? 60 : 30)); + _questFlags[(_vm->getGameID() == GType_Swords ? 0 : files._ccNum * 30) + giveVal] = true; break; case 107: - assert(takeVal < 24); - _characterFlags[ps._rosterId][takeVal] = true; + assert(giveVal < 24); + _characterFlags[ps._rosterId][giveVal] = true; break; default: break; @@ -1460,19 +1466,21 @@ bool Party::giveExt(int mode1, uint val1, int mode2, uint val2, int mode3, uint Map &map = *g_vm->_map; Scripts &scripts = *g_vm->_scripts; Sound &sound = *g_vm->_sound; - Character &c = _itemsCharacter; - if (intf._objNumber && !scripts._animCounter) { - MazeObject &obj = map._mobData._objects[intf._objNumber - 1]; + // WORKAROUND: Ali Baba's chest in Dark Side requires the character in the first slot to have Lockpicking. + // This is obviously a mistake, since the chest is meant to be opened via a password + if (intf._objNumber != -1 && !scripts._animCounter && !(files._ccNum && _mazeId == 63 && intf._objNumber == 15)) { + MazeObject &obj = map._mobData._objects[intf._objNumber]; switch (obj._spriteId) { case 15: - if (!files._isDarkCc) + if (!files._ccNum) break; // Intentional fall-through case 16: case 58: - case 73: + case 73: { + Character &c = _activeParty[charId]; obj._frame = 1; if (obj._position.x != 20) { @@ -1490,19 +1498,24 @@ bool Party::giveExt(int mode1, uint val1, int mode2, uint val2, int mode3, uint sound.playFX(10); intf.draw3d(true, false); Common::String msg = Common::String::format(Res.PICKS_THE_LOCK, c._name.c_str()); - ErrorScroll::show(g_vm, msg); + ErrorScroll::show(g_vm, msg, WT_NONFREEZED_WAIT); } else { sound.playFX(21); obj._frame = 0; scripts._animCounter = 0; Common::String msg = Common::String::format(Res.UNABLE_TO_PICK_LOCK, c._name.c_str()); - ErrorScroll::show(g_vm, msg); + ErrorScroll::show(g_vm, msg, WT_NONFREEZED_WAIT); scripts._animCounter = 255; return true; } } + break; + } + + default: + break; } } @@ -1520,7 +1533,7 @@ bool Party::giveExt(int mode1, uint val1, int mode2, uint val2, int mode3, uint break; case 66: - c.clear(); + _itemsCharacter.clear(); if (giveTake(0, 0, mode, val, charId)) return true; @@ -1590,7 +1603,7 @@ uint Party::getScore() { uint time = _vm->_events->playTime() / GAME_FRAME_RATE; int minutes = (time % 3600) / 60; int hours = time / 3600; - + score += minutes + (hours * 100); return score; } diff --git a/engines/xeen/party.h b/engines/xeen/party.h index 881b1502c5..fde6defeee 100644 --- a/engines/xeen/party.h +++ b/engines/xeen/party.h @@ -53,6 +53,7 @@ enum PartyBank { #define MAX_PARTY_COUNT 8 #define TOTAL_STATS 7 #define TOTAL_QUEST_ITEMS 85 +#define TOTAL_QUEST_ITEMS_SWORDS 51 #define TOTAL_QUEST_FLAGS 56 #define MAX_TREASURE_ITEMS 10 @@ -81,6 +82,16 @@ public: * Returns a particular category's array */ XeenItem *operator[](int category) { return _categories[category]; } + + /** + * Clears the treasure list + */ + void clear(); + + /** + * Completely reset the treasure data + */ + void reset(); }; /** @@ -184,9 +195,9 @@ public: int _heroism; Difficulty _difficulty; BlacksmithWares _blacksmithWares; - bool _cloudsEnd; - bool _darkSideEnd; - bool _worldEnd; + bool _cloudsCompleted; + bool _darkSideCompleted; + bool _worldCompleted; int _ctr24; // Unused counter int _day; uint _year; @@ -209,14 +220,13 @@ public: bool _rested; bool _gameFlags[2][256]; bool _worldFlags[128]; - bool _questFlags[2][30]; + bool _questFlags[60]; int _questItems[TOTAL_QUEST_ITEMS]; bool _characterFlags[30][24]; public: // Other party related runtime data Roster _roster; - Common::Array<Character> _activeParty; - bool _partyDead; + CharacterArray _activeParty; bool _newDay; bool _isNight; bool _stepped; diff --git a/engines/xeen/patcher.cpp b/engines/xeen/patcher.cpp new file mode 100644 index 0000000000..10c8664776 --- /dev/null +++ b/engines/xeen/patcher.cpp @@ -0,0 +1,101 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "xeen/patcher.h" +#include "xeen/xeen.h" +#include "xeen/map.h" +#include "xeen/party.h" +#include "common/memstream.h" +#include "common/serializer.h" + +namespace Xeen { + +struct ScriptEntry { + uint _gameId; + int _mapId; + const byte *_data; +}; + +const byte DS_MAP54_LINE8[] = { 8, 10, 10, DIR_EAST, 8, OP_MoveWallObj, 20, 100, 100 }; +const byte SW_MAP53_LINE8[] = { 5, 14, 6, DIR_EAST, 8, OP_Exit }; +const byte DS_MAP116[] = { 9, 10, 6, 4, 2, OP_TakeOrGive, 0, 0, 103, 127 }; + +#define SCRIPT_PATCHES_COUNT 3 +static const ScriptEntry SCRIPT_PATCHES[] = { + { GType_DarkSide, 54, DS_MAP54_LINE8 }, // Fix curtain on level 2 of Ellinger's Tower + { GType_Swords, 53, SW_MAP53_LINE8 }, // Fix chest in Hart having gems, but saying "Nothing Here" + { GType_DarkSide, 116, DS_MAP116 } // Fix statue in Dark Tower setting invalid world flag +}; + +/*------------------------------------------------------------------------*/ + +void Patcher::patch() { + patchScripts(); + patchObjects(); +} + +void Patcher::patchScripts() { + FileManager &files = *g_vm->_files; + Map &map = *g_vm->_map; + Party &party = *g_vm->_party; + + uint gameId = g_vm->getGameID(); + if (gameId == GType_WorldOfXeen) + gameId = files._ccNum ? GType_DarkSide : GType_Clouds; + + for (int patchIdx = 0; patchIdx < SCRIPT_PATCHES_COUNT; ++patchIdx) { + const ScriptEntry &se = SCRIPT_PATCHES[patchIdx]; + if (se._gameId != gameId || se._mapId != party._mazeId) + continue; + + MazeEvent evt; + Common::MemoryReadStream memStream(se._data, se._data[0] + 1); + Common::Serializer s(&memStream, nullptr); + evt.synchronize(s); + + // Scan through the events to find a matching line + int idx = 0; + while (idx < (int)map._events.size() && (evt._position != map._events[idx]._position + || evt._direction != map._events[idx]._direction || evt._line != map._events[idx]._line)) + ++idx; + + // Set the event + if (idx == (int)map._events.size()) + map._events.push_back(evt); + else + map._events[idx] = evt; + } +} + +void Patcher::patchObjects() { + FileManager &files = *g_vm->_files; + Map &map = *g_vm->_map; + Party &party = *g_vm->_party; + + if ((g_vm->getGameID() == GType_Clouds || (g_vm->getGameID() == GType_WorldOfXeen && !files._ccNum)) && + party._mazeId == 24) { + // Remove floating statue in the distance off SE corner of Clouds of Xeen map + map._mobData._objects[15]._position = Common::Point(-128, -128); + } +} + +} // End of namespace Xeen diff --git a/engines/xeen/worldofxeen/worldofxeen_resources.h b/engines/xeen/patcher.h index e69f52028d..24d6cd5a8d 100644 --- a/engines/xeen/worldofxeen/worldofxeen_resources.h +++ b/engines/xeen/patcher.h @@ -20,29 +20,34 @@ * */ -#ifndef XEEN_WORLDOFXEEN_RESOURCES_H -#define XEEN_WORLDOFXEEN_RESOURCES_H - -#include "xeen/resources.h" +#ifndef XEEN_PATCHER_H +#define XEEN_PATCHER_H namespace Xeen { -namespace WorldOfXeen { -#ifdef Res -#undef Res -#endif -#define Res (*(WorldOfXeenResources *)g_resources) +class Patcher { +private: + /** + * Patches incorrect script lines + */ + void patchScripts(); -class WorldOfXeenResources : public Resources { + /** + * Patches incorrect map objects + */ + void patchObjects(); public: - static const char *const CLOUDS_INTRO1; - static const char *const DARKSIDE_ENDING1; - static const char *const DARKSIDE_ENDING2; - static const char *const PHAROAH_ENDING_TEXT1; - static const char *const PHAROAH_ENDING_TEXT2; + /** + * Constructor + */ + Patcher() {} + + /** + * Called after a map is loaded to patch any problems + */ + void patch(); }; -} // End of namespace WorldOfXeen } // End of namespace Xeen -#endif /* XEEN_RESOURCES_H */ +#endif /* XEEN_PATCHER_H */ diff --git a/engines/xeen/resources.cpp b/engines/xeen/resources.cpp index c2732a9bf9..160c60a7a9 100644 --- a/engines/xeen/resources.cpp +++ b/engines/xeen/resources.cpp @@ -24,7 +24,6 @@ #include "xeen/resources.h" #include "xeen/files.h" #include "xeen/xeen.h" -#include "xeen/worldofxeen/worldofxeen_resources.h" namespace Xeen { @@ -33,7 +32,7 @@ Resources *g_resources; Resources::Resources() { g_resources = this; g_vm->_files->setGameCc(1); - + _globalSprites.load("global.icn"); if (g_vm->getGameID() == GType_Swords) _logoSprites.load("logo.int"); @@ -72,6 +71,7 @@ void Resources::loadData() { file.syncString(WHO_WILL); file.syncString(HOW_MUCH); file.syncString(WHATS_THE_PASSWORD); + file.syncString(PASSWORD_INCORRECT); file.syncString(IN_NO_CONDITION); file.syncString(NOTHING_HERE); file.syncStrings(TERRAIN_TYPES, 6); @@ -165,7 +165,7 @@ void Resources::loadData() { file.syncString(FOOD_AND_DRINK); file.syncString(TEMPLE_TEXT); file.syncString(EXPERIENCE_FOR_LEVEL); - file.syncString(LEARNED_ALL); + file.syncString(TRAINING_LEARNED_ALL); file.syncString(ELIGIBLE_FOR_LEVEL); file.syncString(TRAINING_TEXT); file.syncString(GOLD_GEMS); @@ -182,18 +182,20 @@ void Resources::loadData() { file.syncString(GUILD_OPTIONS); file.syncNumbers((int *)MISC_SPELL_INDEX, 74); file.syncNumbers((int *)SPELL_COSTS, 77); - file.syncNumbers2D((int *)CLOUDS_SPELL_OFFSETS, 5, 20); + file.syncNumbers2D((int *)CLOUDS_GUILD_SPELLS, 5, 20); file.syncNumbers2D((int *)DARK_SPELL_OFFSETS, 3, 39); file.syncNumbers2D((int *)DARK_SPELL_RANGES, 12, 2); + file.syncNumbers2D((int *)SWORDS_SPELL_RANGES, 12, 2); file.syncNumbers((int *)SPELL_GEM_COST, 77); file.syncString(NOT_A_SPELL_CASTER); + file.syncString(SPELLS_LEARNED_ALL); file.syncString(SPELLS_FOR); file.syncString(SPELL_LINES_0_TO_9); file.syncString(SPELLS_DIALOG_SPELLS); file.syncString(SPELL_PTS); file.syncString(GOLD); - file.syncString(SPELLS_PRESS_A_KEY); - file.syncString(SPELLS_PURCHASE); + file.syncString(SPELL_INFO); + file.syncString(SPELL_PURCHASE); file.syncString(MAP_TEXT); file.syncString(LIGHT_COUNT_TEXT); file.syncString(FIRE_RESISTENCE_TEXT); @@ -247,15 +249,18 @@ void Resources::loadData() { file.syncString(BTN_GOLD); file.syncString(ITEM_BROKEN); file.syncString(ITEM_CURSED); + file.syncString(ITEM_OF); file.syncStrings(BONUS_NAMES, 7); - file.syncStrings(WEAPON_NAMES, 35); + file.syncStrings(WEAPON_NAMES, 41); file.syncStrings(ARMOR_NAMES, 14); file.syncStrings(ACCESSORY_NAMES, 11); file.syncStrings(MISC_NAMES, 22); + file.syncStrings(SPECIAL_NAMES, 74); file.syncStrings(ELEMENTAL_NAMES, 6); file.syncStrings(ATTRIBUTE_NAMES, 10); file.syncStrings(EFFECTIVENESS_NAMES, 7); file.syncStrings(QUEST_ITEM_NAMES, 85); + file.syncStrings(QUEST_ITEM_NAMES_SWORDS, 51); file.syncNumbers((int *)WEAPON_BASE_COSTS, 35); file.syncNumbers((int *)ARMOR_BASE_COSTS, 14); file.syncNumbers((int *)ACCESSORY_BASE_COSTS, 11); @@ -310,6 +315,7 @@ void Resources::loadData() { file.syncString(QUESTS_DIALOG_TEXT); file.syncString(CLOUDS_OF_XEEN_LINE); file.syncString(DARKSIDE_OF_XEEN_LINE); + file.syncString(SWORDS_OF_XEEN_LINE); file.syncString(NO_QUEST_ITEMS); file.syncString(NO_CURRENT_QUESTS); file.syncString(NO_AUTO_NOTES); @@ -322,6 +328,7 @@ void Resources::loadData() { file.syncString(TOO_DANGEROUS_TO_REST); file.syncString(SOME_CHARS_MAY_DIE); file.syncString(CANT_DISMISS_LAST_CHAR); + file.syncString(DELETE_CHAR_WITH_ELDER_WEAPON); file.syncStrings(REMOVE_DELETE, 2); file.syncString(REMOVE_OR_DELETE_WHICH); file.syncString(YOUR_PARTY_IS_FULL); @@ -333,6 +340,7 @@ void Resources::loadData() { file.syncString(SELECT_CLASS_BEFORE_SAVING); file.syncString(EXCHANGE_ATTR_WITH); file.syncNumbers((int *)NEW_CHAR_SKILLS, 10); + file.syncNumbers((int *)NEW_CHAR_SKILLS_OFFSET, 10); file.syncNumbers((int *)NEW_CHAR_SKILLS_LEN, 10); file.syncNumbers((int *)NEW_CHAR_RACE_SKILLS, 10); file.syncNumbers((int *)RACE_MAGIC_RESISTENCES, 5); @@ -358,7 +366,8 @@ void Resources::loadData() { file.syncString(LLOYDS_BEACON); file.syncString(HOW_MANY_SQUARES); file.syncString(TOWN_PORTAL); - file.syncNumbers2D((int *)TOWN_MAP_NUMBERS, 2, 5); + file.syncString(TOWN_PORTAL_SWORDS); + file.syncNumbers2D((int *)TOWN_MAP_NUMBERS, 3, 5); file.syncString(MONSTER_DETAILS); file.syncStrings(MONSTER_SPECIAL_ATTACKS, 23); file.syncString(IDENTIFY_MONSTERS); @@ -392,6 +401,12 @@ void Resources::loadData() { file.syncStrings(MUSIC_FILES1, 5); file.syncStrings2D(&MUSIC_FILES2[0][0], 6, 7); file.syncString(DIFFICULTY_TEXT); + file.syncString(SAVE_OFF_LIMITS); + file.syncString(CLOUDS_INTRO1); + file.syncString(DARKSIDE_ENDING1); + file.syncString(DARKSIDE_ENDING2); + file.syncString(PHAROAH_ENDING_TEXT1); + file.syncString(PHAROAH_ENDING_TEXT2); } } // End of namespace Xeen diff --git a/engines/xeen/resources.h b/engines/xeen/resources.h index 0659de45c9..55f0ed3413 100644 --- a/engines/xeen/resources.h +++ b/engines/xeen/resources.h @@ -126,6 +126,7 @@ public: const char *WHO_WILL; const char *HOW_MUCH; const char *WHATS_THE_PASSWORD; + const char *PASSWORD_INCORRECT; const char *IN_NO_CONDITION; const char *NOTHING_HERE; const char *TERRAIN_TYPES[6]; @@ -219,7 +220,7 @@ public: const char *FOOD_AND_DRINK; const char *TEMPLE_TEXT; const char *EXPERIENCE_FOR_LEVEL; - const char *LEARNED_ALL; + const char *TRAINING_LEARNED_ALL; const char *ELIGIBLE_FOR_LEVEL; const char *TRAINING_TEXT; const char *GOLD_GEMS; @@ -236,19 +237,20 @@ public: const char *GUILD_OPTIONS; int MISC_SPELL_INDEX[74]; int SPELL_COSTS[77]; - int CLOUDS_SPELL_OFFSETS[5][20]; + int CLOUDS_GUILD_SPELLS[5][20]; int DARK_SPELL_OFFSETS[3][39]; int DARK_SPELL_RANGES[12][2]; - int SPELL_LEVEL_OFFSETS[3][39]; + int SWORDS_SPELL_RANGES[12][2]; int SPELL_GEM_COST[77]; const char *NOT_A_SPELL_CASTER; + const char *SPELLS_LEARNED_ALL; const char *SPELLS_FOR; const char *SPELL_LINES_0_TO_9; const char *SPELLS_DIALOG_SPELLS; const char *SPELL_PTS; const char *GOLD; - const char *SPELLS_PRESS_A_KEY; - const char *SPELLS_PURCHASE; + const char *SPELL_INFO; + const char *SPELL_PURCHASE; const char *MAP_TEXT; const char *LIGHT_COUNT_TEXT; const char *FIRE_RESISTENCE_TEXT; @@ -302,15 +304,18 @@ public: const char *BTN_GOLD; const char *ITEM_BROKEN; const char *ITEM_CURSED; + const char *ITEM_OF; const char *BONUS_NAMES[7]; - const char *WEAPON_NAMES[35]; + const char *WEAPON_NAMES[41]; const char *ARMOR_NAMES[14]; const char *ACCESSORY_NAMES[11]; const char *MISC_NAMES[22]; + const char *SPECIAL_NAMES[74]; const char *ELEMENTAL_NAMES[6]; const char *ATTRIBUTE_NAMES[10]; const char *EFFECTIVENESS_NAMES[7]; const char *QUEST_ITEM_NAMES[85]; + const char *QUEST_ITEM_NAMES_SWORDS[51]; int WEAPON_BASE_COSTS[35]; int ARMOR_BASE_COSTS[14]; int ACCESSORY_BASE_COSTS[11]; @@ -365,6 +370,7 @@ public: const char *QUESTS_DIALOG_TEXT; const char *CLOUDS_OF_XEEN_LINE; const char *DARKSIDE_OF_XEEN_LINE; + const char *SWORDS_OF_XEEN_LINE; const char *NO_QUEST_ITEMS; const char *NO_CURRENT_QUESTS; const char *NO_AUTO_NOTES; @@ -377,6 +383,7 @@ public: const char *TOO_DANGEROUS_TO_REST; const char *SOME_CHARS_MAY_DIE; const char *CANT_DISMISS_LAST_CHAR; + const char *DELETE_CHAR_WITH_ELDER_WEAPON; const char *REMOVE_DELETE[2]; const char *REMOVE_OR_DELETE_WHICH; const char *YOUR_PARTY_IS_FULL; @@ -388,6 +395,7 @@ public: const char *SELECT_CLASS_BEFORE_SAVING; const char *EXCHANGE_ATTR_WITH; int NEW_CHAR_SKILLS[10]; + int NEW_CHAR_SKILLS_OFFSET[10]; int NEW_CHAR_SKILLS_LEN[10]; int NEW_CHAR_RACE_SKILLS[10]; int RACE_MAGIC_RESISTENCES[5]; @@ -413,7 +421,8 @@ public: const char *LLOYDS_BEACON; const char *HOW_MANY_SQUARES; const char *TOWN_PORTAL; - int TOWN_MAP_NUMBERS[2][5]; + const char *TOWN_PORTAL_SWORDS; + int TOWN_MAP_NUMBERS[3][5]; const char *MONSTER_DETAILS; const char *MONSTER_SPECIAL_ATTACKS[23]; const char *IDENTIFY_MONSTERS; @@ -447,6 +456,12 @@ public: const char *MUSIC_FILES1[5]; const char *MUSIC_FILES2[6][7]; const char *DIFFICULTY_TEXT; + const char *SAVE_OFF_LIMITS; + const char *CLOUDS_INTRO1; + const char *DARKSIDE_ENDING1; + const char *DARKSIDE_ENDING2; + const char *PHAROAH_ENDING_TEXT1; + const char *PHAROAH_ENDING_TEXT2; public: /** * Constructor diff --git a/engines/xeen/saves.cpp b/engines/xeen/saves.cpp index 7bd938180e..82e294921b 100644 --- a/engines/xeen/saves.cpp +++ b/engines/xeen/saves.cpp @@ -48,9 +48,8 @@ SavesManager::~SavesManager() { const char *const SAVEGAME_STR = "XEEN"; #define SAVEGAME_STR_SIZE 6 -bool SavesManager::readSavegameHeader(Common::InSaveFile *in, XeenSavegameHeader &header) { +WARN_UNUSED_RESULT bool SavesManager::readSavegameHeader(Common::InSaveFile *in, XeenSavegameHeader &header, bool skipThumbnail) { char saveIdentBuffer[SAVEGAME_STR_SIZE + 1]; - header._thumbnail = nullptr; // Validate the header Id in->read(saveIdentBuffer, SAVEGAME_STR_SIZE + 1); @@ -68,9 +67,9 @@ bool SavesManager::readSavegameHeader(Common::InSaveFile *in, XeenSavegameHeader header._saveName += ch; // Get the thumbnail - header._thumbnail = Graphics::loadThumbnail(*in); - if (!header._thumbnail) + if (!Graphics::loadThumbnail(*in, header._thumbnail, skipThumbnail)) { return false; + } // Read in save date/time header._year = in->readSint16LE(); @@ -153,6 +152,7 @@ Common::Error SavesManager::saveGameState(int slot, const Common::String &desc) } Common::Error SavesManager::loadGameState(int slot) { + Combat &combat = *g_vm->_combat; EventsManager &events = *g_vm->_events; FileManager &files = *g_vm->_files; Map &map = *g_vm->_map; @@ -168,11 +168,6 @@ Common::Error SavesManager::loadGameState(int slot) { if (!readSavegameHeader(saveFile, header)) error("Invalid savegame"); - if (header._thumbnail) { - header._thumbnail->free(); - delete header._thumbnail; - } - // Set the total play time events.setPlayTime(header._totalFrames); @@ -182,9 +177,13 @@ Common::Error SavesManager::loadGameState(int slot) { uint fileSize = saveFile->readUint32LE(); if (archives[idx]) { - Common::SeekableSubReadStream arcStream(saveFile, saveFile->pos(), - saveFile->pos() + fileSize); - archives[idx]->load(arcStream); + if (fileSize) { + Common::SeekableSubReadStream arcStream(saveFile, saveFile->pos(), + saveFile->pos() + fileSize); + archives[idx]->load(arcStream); + } else { + archives[idx]->reset((idx == 1) ? File::_darkCc : File::_xeenCc); + } } else { assert(!fileSize); } @@ -193,9 +192,16 @@ Common::Error SavesManager::loadGameState(int slot) { // Read in miscellaneous files.load(*saveFile); + // Load the character roster and party + File::_currentSave->loadParty(); + + // Reset any combat information from the previous game + combat.reset(); + party._treasure.reset(); + // Load the new map map.clearMaze(); - map._loadDarkSide = files._isDarkCc; + map._loadCcNum = files._ccNum; map.load(party._mazeId); delete saveFile; @@ -212,6 +218,10 @@ void SavesManager::newGame() { File::_xeenSave = nullptr; File::_darkSave = nullptr; + // Reset any combat information from the previous game + g_vm->_combat->reset(); + + // Reset the game states if (g_vm->getGameID() != GType_Clouds) { File::_darkSave = new SaveArchive(g_vm->_party); File::_darkSave->reset(File::_darkCc); @@ -225,6 +235,9 @@ void SavesManager::newGame() { File::_darkSave : File::_xeenSave; assert(File::_currentSave); + // Load the character roster and party + File::_currentSave->loadParty(); + // Set any final initial values Party &party = *g_vm->_party; party.resetBlacksmithWares(); @@ -245,32 +258,37 @@ void SavesManager::newGame() { } bool SavesManager::loadGame() { - if (!g_vm->canLoadGameStateCurrently()) - return false; - GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Load game:"), _("Load"), false); int slotNum = dialog->runModalWithCurrentTarget(); delete dialog; - if (slotNum != -1) + if (slotNum != -1) { loadGameState(slotNum); + g_vm->_interface->drawParty(true); + } return slotNum != -1; } bool SavesManager::saveGame() { - if (!g_vm->canSaveGameStateCurrently()) - return false; + Map &map = *g_vm->_map; - GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"), true); - int slotNum = dialog->runModalWithCurrentTarget(); - Common::String saveName = dialog->getResultString(); - delete dialog; + if (map.mazeData()._mazeFlags & RESTRICTION_SAVE) { + ErrorScroll::show(g_vm, Res.SAVE_OFF_LIMITS, WT_NONFREEZED_WAIT); + return false; + } else if (!g_vm->canSaveGameStateCurrently()) { + return false; + } else { + GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"), true); + int slotNum = dialog->runModalWithCurrentTarget(); + Common::String saveName = dialog->getResultString(); + delete dialog; - if (slotNum != -1) - saveGameState(slotNum, saveName); + if (slotNum != -1) + saveGameState(slotNum, saveName); - return slotNum != -1; + return slotNum != -1; + } } } // End of namespace Xeen diff --git a/engines/xeen/saves.h b/engines/xeen/saves.h index b18e04a822..9b1bea62a3 100644 --- a/engines/xeen/saves.h +++ b/engines/xeen/saves.h @@ -65,7 +65,7 @@ public: /** * Read in a savegame header */ - static bool readSavegameHeader(Common::InSaveFile *in, XeenSavegameHeader &header); + WARN_UNUSED_RESULT static bool readSavegameHeader(Common::InSaveFile *in, XeenSavegameHeader &header, bool skipThumbnail = true); /** * Write out a savegame header diff --git a/engines/xeen/screen.cpp b/engines/xeen/screen.cpp index c9781b3cf1..47735478ed 100644 --- a/engines/xeen/screen.cpp +++ b/engines/xeen/screen.cpp @@ -169,7 +169,7 @@ bool Screen::doScroll(bool rollUp, bool fadeInFlag) { const int SCROLL_L[8] = { 29, 23, 15, -5, -11, -23, -49, -71 }; const int SCROLL_R[8] = { 165, 171, 198, 218, 228, 245, 264, 281 }; - if (_vm->_files->_isDarkCc) { + if (_vm->_files->_ccNum) { if (fadeInFlag) screen.fadeIn(2); return _vm->shouldExit(); diff --git a/engines/xeen/scripts.cpp b/engines/xeen/scripts.cpp index 8777cdc6e0..bc0b179d4c 100644 --- a/engines/xeen/scripts.cpp +++ b/engines/xeen/scripts.cpp @@ -21,7 +21,10 @@ */ #include "common/config-manager.h" +#include "common/textconsole.h" +#include "backends/audiocd/audiocd.h" #include "xeen/scripts.h" +#include "xeen/dialogs/dialogs_copy_protection.h" #include "xeen/dialogs/dialogs_input.h" #include "xeen/dialogs/dialogs_whowill.h" #include "xeen/dialogs/dialogs_query.h" @@ -138,7 +141,7 @@ int Scripts::checkEvents() { Party &party = *_vm->_party; Sound &sound = *_vm->_sound; Windows &windows = *_vm->_windows; - bool isDarkCc = files._isDarkCc; + int ccNum = files._ccNum; _refreshIcons = false; _itemType = 0; @@ -182,10 +185,10 @@ int Scripts::checkEvents() { for (eventIndex = 0; eventIndex < map._events.size() && !_vm->shouldExit(); ++eventIndex) { MazeEvent &event = map._events[eventIndex]; - if (event._position == _currentPos && party._mazeDirection != - (_currentPos.x | _currentPos.y) && event._line == _lineNum) { + if (event._position == _currentPos && event._line == _lineNum && + (party._mazeDirection | _currentPos.x | _currentPos.y)) { if (event._direction == party._mazeDirection || event._direction == DIR_ALL) { - _vm->_mode = MODE_RECORD_EVENTS; + _vm->_mode = MODE_SCRIPT_IN_PROGRESS; _scriptExecuted = true; doOpcode(event); break; @@ -209,24 +212,27 @@ int Scripts::checkEvents() { if (party._treasure._hasItems || party._treasure._gold || party._treasure._gems) party.giveTreasure(); - if (_animCounter > 0 && intf._objNumber) { - MazeObject &selectedObj = map._mobData._objects[intf._objNumber - 1]; + if (_animCounter > 0 && intf._objNumber != -1) { + MazeObject &selectedObj = map._mobData._objects[intf._objNumber]; - if (selectedObj._spriteId == (isDarkCc ? 15 : 16)) { - for (uint idx = 0; idx < 16; ++idx) { - MazeObject &obj = map._mobData._objects[idx]; - if (obj._spriteId == (isDarkCc ? 62 : 57)) { + if (selectedObj._spriteId == (ccNum ? 15 : 16)) { + // Treasure chests that were opened will be set to be in an open, empty state + for (uint idx = 0; idx < map._mobData._objectSprites.size(); ++idx) { + MonsterObjectData::SpriteResourceEntry &e = map._mobData._objectSprites[idx]; + if (e._spriteId == (ccNum ? 57 : 62)) { selectedObj._id = idx; - selectedObj._spriteId = isDarkCc ? 62 : 57; + selectedObj._spriteId = ccNum ? 57 : 62; + selectedObj._sprites = &e._sprites; break; } } } else if (selectedObj._spriteId == 73) { - for (uint idx = 0; idx < 16; ++idx) { - MazeObject &obj = map._mobData._objects[idx]; - if (obj._spriteId == 119) { + for (uint idx = 0; idx < map._mobData._objectSprites.size(); ++idx) { + MonsterObjectData::SpriteResourceEntry &e = map._mobData._objectSprites[idx]; + if (e._spriteId == 119) { selectedObj._id = idx; selectedObj._spriteId = 119; + selectedObj._sprites = &e._sprites; break; } } @@ -237,8 +243,17 @@ int Scripts::checkEvents() { _vm->_mode = oldMode; windows.closeAll(); - if (_scriptExecuted || !intf._objNumber || _dirFlag) { - if (_dirFlag && !_scriptExecuted && intf._objNumber && !map._currentIsEvent) { + if (g_vm->getIsCD() && g_system->getAudioCDManager()->isPlaying()) + // Stop any playing voice + g_system->getAudioCDManager()->stop(); + + if (g_vm->shouldExit()) + return g_vm->_gameMode; + + if (_scriptExecuted) + intf.clearEvents(); + if (_scriptExecuted || intf._objNumber == -1 || _dirFlag) { + if (_dirFlag && !_scriptExecuted && intf._objNumber != -1 && !map._currentIsEvent) { sound.playFX(21); } } else { @@ -269,69 +284,71 @@ int Scripts::checkEvents() { return _scriptResult; } -void Scripts::openGrate(int wallVal, int action) { +bool Scripts::openGrate(int wallVal, int action) { Combat &combat = *_vm->_combat; FileManager &files = *_vm->_files; Interface &intf = *_vm->_interface; Map &map = *_vm->_map; Party &party = *_vm->_party; Sound &sound = *_vm->_sound; - bool isDarkCc = files._isDarkCc; - - if ((wallVal != 13 || map._currentGrateUnlocked) && (!isDarkCc || wallVal != 9 || - map.mazeData()._wallKind != 2)) { - if (wallVal != 9 && !map._currentGrateUnlocked) { - int charIndex = WhoWill::show(_vm, 13, action, false) - 1; - if (charIndex < 0) { - intf.draw3d(true); - return; - } + int ccNum = files._ccNum; - // There is a 1 in 4 chance the character will receive damage - if (_vm->getRandomNumber(1, 4) == 1) { - combat.giveCharDamage(map.mazeData()._trapDamage, - (DamageType)_vm->getRandomNumber(0, 6), charIndex); - } + if (!((wallVal != 13 || map._currentGrateUnlocked) && (!ccNum || wallVal != 9 || + map.mazeData()._wallKind != 2))) + return false; - // Check whether character can unlock the door - Character &c = party._activeParty[charIndex]; - if ((c.getThievery() + _vm->getRandomNumber(1, 20)) < - map.mazeData()._difficulties._unlockDoor) - return; + if (wallVal != 9 && !map._currentGrateUnlocked) { + int charIndex = WhoWill::show(_vm, 13, action, false) - 1; + if (charIndex < 0) { + intf.draw3d(true); + return true; + } - c._experience += map.mazeData()._difficulties._unlockDoor * c.getCurrentLevel(); + // There is a 1 in 4 chance the character will receive damage + if (_vm->getRandomNumber(1, 4) == 1) { + combat.giveCharDamage(map.mazeData()._trapDamage, + (DamageType)_vm->getRandomNumber(0, 6), charIndex); } - // Flag the grate as unlocked, and the wall the grate is on - map.setCellSurfaceFlags(party._mazePosition, 0x80); - map.setWall(party._mazePosition, party._mazeDirection, wallVal); + // Check whether character can unlock the door + Character &c = party._activeParty[charIndex]; + if ((c.getThievery() + _vm->getRandomNumber(1, 20)) < + map.mazeData()._difficulties._unlockDoor) + return true; - // Set the grate as opened and the wall on the other side of the grate - Common::Point pt = party._mazePosition; - Direction dir = (Direction)((int)party._mazeDirection ^ 2); - switch (party._mazeDirection) { - case DIR_NORTH: - pt.y++; - break; - case DIR_EAST: - pt.x++; - break; - case DIR_SOUTH: - pt.y--; - break; - case DIR_WEST: - pt.x--; - break; - default: - break; - } + c._experience += map.mazeData()._difficulties._unlockDoor * c.getCurrentLevel(); + } - map.setCellSurfaceFlags(pt, 0x80); - map.setWall(pt, dir, wallVal); + // Flag the grate as unlocked, and the wall the grate is on + map.setCellSurfaceFlags(party._mazePosition, 0x80); + map.setWall(party._mazePosition, party._mazeDirection, wallVal); - sound.playFX(10); - intf.draw3d(true); + // Set the grate as opened and the wall on the other side of the grate + Common::Point pt = party._mazePosition; + Direction dir = (Direction)((int)party._mazeDirection ^ 2); + switch (party._mazeDirection) { + case DIR_NORTH: + pt.y++; + break; + case DIR_EAST: + pt.x++; + break; + case DIR_SOUTH: + pt.y--; + break; + case DIR_WEST: + pt.x--; + break; + default: + break; } + + map.setCellSurfaceFlags(pt, 0x80); + map.setWall(pt, dir, wallVal); + + sound.playFX(10); + intf.draw3d(true); + return true; } bool Scripts::doOpcode(MazeEvent &event) { @@ -373,7 +390,7 @@ bool Scripts::doOpcode(MazeEvent &event) { bool result = (this->*COMMAND_LIST[event._opcode])(params); if (result) // Move to next line - _lineNum = _vm->_party->_partyDead ? -1 : _lineNum + 1; + _lineNum = _vm->_party->_dead ? SCRIPT_ABORT : _lineNum + 1; return result; } @@ -465,7 +482,7 @@ bool Scripts::cmdTeleport(ParamsIterator ¶ms) { Sound &sound = *_vm->_sound; windows.closeAll(); - + bool restartFlag = _event->_opcode == OP_TeleportAndContinue; int mapId = params.readByte(); Common::Point pt; @@ -491,8 +508,7 @@ bool Scripts::cmdTeleport(ParamsIterator ¶ms) { party._stepped = true; if (mapId != party._mazeId) { - int spriteId = (intf._objNumber == 0) ? -1 : - map._mobData._objects[intf._objNumber - 1]._spriteId; + int spriteId = (intf._objNumber == -1) ? -1 : map._mobData._objects[intf._objNumber]._spriteId; switch (spriteId) { case 47: @@ -519,7 +535,7 @@ bool Scripts::cmdTeleport(ParamsIterator ¶ms) { if (restartFlag) { // Draw the new location and start any script at that location - intf.draw3d(true); + events.ipause(2); _lineNum = SCRIPT_RESET; return false; } else { @@ -592,7 +608,7 @@ bool Scripts::cmdTakeOrGive(ParamsIterator ¶ms) { Combat &combat = *_vm->_combat; Party &party = *_vm->_party; Windows &windows = *_vm->_windows; - int mode1, mode2, mode3, param2; + int mode1, mode2, mode3; uint32 val1, val2, val3; _refreshIcons = true; @@ -614,7 +630,7 @@ bool Scripts::cmdTakeOrGive(ParamsIterator ¶ms) { break; } - param2 = mode2 = params.readByte(); + mode2 = params.readByte(); switch (mode2) { case 16: case 34: @@ -707,7 +723,7 @@ bool Scripts::cmdTakeOrGive(ParamsIterator ¶ms) { if (_charIndex == 0 || _charIndex == 8) { for (uint idx = 0; idx < party._activeParty.size(); ++idx) { if (_charIndex == 0 || (_charIndex == 8 && (int)idx != combat._combatTarget)) { - party.giveTake(mode1, val1, mode2, val2, idx); + bool flag = party.giveTake(mode1, val1, mode2, val2, idx); switch (mode1) { case 8: @@ -715,7 +731,7 @@ bool Scripts::cmdTakeOrGive(ParamsIterator ¶ms) { // fall through case 21: case 66: - if (param2) { + if (flag) { switch (mode2) { case 82: mode1 = 0; @@ -728,13 +744,18 @@ bool Scripts::cmdTakeOrGive(ParamsIterator ¶ms) { case 100: case 101: case 106: - if (param2) + if (flag) continue; // Break out of character loop idx = party._activeParty.size(); break; + default: + break; } + } else { + // Break out of character loop + idx = party._activeParty.size(); } break; @@ -744,7 +765,7 @@ bool Scripts::cmdTakeOrGive(ParamsIterator ¶ms) { case 100: case 101: case 106: - if (param2) { + if (flag) { _lineNum = -1; return false; } @@ -766,7 +787,7 @@ bool Scripts::cmdTakeOrGive(ParamsIterator ¶ms) { case 100: case 101: case 106: - if (param2) + if (flag) continue; // Break out of character loop @@ -793,9 +814,9 @@ bool Scripts::cmdRemove(ParamsIterator ¶ms) { Interface &intf = *_vm->_interface; Map &map = *_vm->_map; - if (intf._objNumber) { + if (intf._objNumber != -1) { // Give the active object a completely way out of bounds position - MazeObject &obj = map._mobData._objects[intf._objNumber - 1]; + MazeObject &obj = map._mobData._objects[intf._objNumber]; obj._position = Common::Point(128, 128); } @@ -845,7 +866,7 @@ bool Scripts::cmdSpawn(ParamsIterator ¶ms) { } bool Scripts::cmdDoTownEvent(ParamsIterator ¶ms) { - _scriptResult = _vm->_locations->doAction((LocationAction)params.readByte()); + _scriptResult = _vm->_locations->doAction(params.readByte()); _vm->_party->_stepped = true; _refreshIcons = true; @@ -930,40 +951,46 @@ bool Scripts::cmdConfirmWord(ParamsIterator ¶ms) { int param2 = params.readByte(); int param3 = params.readByte(); - Common::String msg1 = param2 ? map._events._text[param2] : _message; - Common::String msg2; + Common::String expected2; + Common::String title; if (_event->_opcode == OP_ConfirmWord_2) { - msg2 = ""; + title = ""; } else if (param3) { - msg2 = map._events._text[param3]; + title = map._events._text[param3]; } else { - msg2 = Res.WHATS_THE_PASSWORD; + title = Res.WHATS_THE_PASSWORD; } - _mirrorId = StringInput::show(_vm, inputType, msg1, msg2, _event->_opcode); + if (!param2) { + expected2 = _message; + } else if (param2 < (int)map._events._text.size()) { + expected2 = map._events._text[param2]; + } + + _mirrorId = StringInput::show(_vm, inputType, expected2, title, _event->_opcode); if (_mirrorId) { - if (_mirrorId == 33 && files._isDarkCc) { + if (_mirrorId == 33 && files._ccNum) { doDarkSideEnding(); - } else if (_mirrorId == 34 && files._isDarkCc) { + } else if (_mirrorId == 34 && files._ccNum) { doWorldEnding(); - } else if (_mirrorId == 35 && files._isDarkCc && + } else if (_mirrorId == 35 && files._ccNum && _vm->getGameID() == GType_WorldOfXeen) { doCloudsEnding(); - } else if (_mirrorId == 40 && !files._isDarkCc) { + } else if (_mirrorId == 40 && !files._ccNum) { doCloudsEnding(); - } else if (_mirrorId == 60 && !files._isDarkCc) { + } else if (_mirrorId == 60 && !files._ccNum) { doDarkSideEnding(); - } else if (_mirrorId == 61 && !files._isDarkCc) { + } else if (_mirrorId == 61 && !files._ccNum) { doWorldEnding(); } else { - if (_mirrorId == 59 && !files._isDarkCc) { + if (_mirrorId == 59 && !files._ccNum) { for (int idx = 0; idx < MAX_TREASURE_ITEMS; ++idx) { XeenItem &item = party._treasure._weapons[idx]; if (!item._id) { - item._id = 34; + item._id = XEEN_SLAYER_SWORD; item._material = 0; - item._bonusFlags = 0; + item._state.clear(); party._treasure._hasItems = true; return cmdExit(params); @@ -990,7 +1017,7 @@ bool Scripts::cmdDamage(ParamsIterator ¶ms) { int damage = params.readUint16LE(); DamageType damageType = (DamageType)params.readByte(); - combat.giveCharDamage(damage, damageType, _charIndex); + combat.giveCharDamage(damage, damageType, _charIndex - 1); return true; } @@ -1033,11 +1060,16 @@ bool Scripts::cmdCallEvent(ParamsIterator ¶ms) { } bool Scripts::cmdReturn(ParamsIterator ¶ms) { - StackEntry &se = _stack.top(); - _currentPos = se; - _lineNum = se.line; + if (_stack.empty()) { + // WORKAROUND: Some scripts in Swords of Xeen use cmdReturn as a substitute for cmdExit + return cmdExit(params); + } else { + StackEntry se = _stack.pop(); + _currentPos = se; + _lineNum = se.line; - return true; + return true; + } } bool Scripts::cmdSetVar(ParamsIterator ¶ms) { @@ -1114,17 +1146,17 @@ bool Scripts::cmdRndDamage(ParamsIterator ¶ms) { DamageType dmgType = (DamageType)params.readByte(); int max = params.readByte(); - combat.giveCharDamage(_vm->getRandomNumber(1, max), dmgType, _charIndex); + combat.giveCharDamage(_vm->getRandomNumber(1, max), dmgType, _charIndex - 1); return true; } bool Scripts::cmdMoveWallObj(ParamsIterator ¶ms) { Map &map = *_vm->_map; - int itemNum = params.readByte(); + int index = params.readByte(); int x = params.readShort(); int y = params.readShort(); - map._mobData._wallItems[itemNum]._position = Common::Point(x, y); + map._mobData._wallItems[index]._position = Common::Point(x, y); return true; } @@ -1203,14 +1235,25 @@ bool Scripts::cmdDisplayBottom(ParamsIterator ¶ms) { bool Scripts::cmdIfMapFlag(ParamsIterator ¶ms) { Map &map = *_vm->_map; - MazeMonster &monster = map._mobData._monsters[params.readByte()]; + int monsterNum = params.readByte(); + int lineNum = params.readByte(); - if (monster._position.x >= 32 || monster._position.y >= 32) { - _lineNum = params.readByte(); - return false; + if (monsterNum == 0xff) { + for (monsterNum = 0; monsterNum < (int)map._mobData._monsters.size(); ++monsterNum) { + MazeMonster &monster = map._mobData._monsters[monsterNum]; + + if ((uint)monster._position.x < 32 && (uint)monster._position.y < 32) + return true; + } + } else { + MazeMonster &monster = map._mobData._monsters[monsterNum]; + + if ((uint)monster._position.x < 32 && (uint)monster._position.y < 32) + return true; } - return true; + _lineNum = lineNum; + return false; } bool Scripts::cmdSelectRandomChar(ParamsIterator ¶ms) { @@ -1220,64 +1263,47 @@ bool Scripts::cmdSelectRandomChar(ParamsIterator ¶ms) { bool Scripts::cmdGiveEnchanted(ParamsIterator ¶ms) { Party &party = *_vm->_party; - + int itemOffset = _vm->getGameID() == GType_Swords ? 6 : 0; + XeenItem *item; + int invIndex; int id = params.readByte(); - int material = params.readByte(); - int flags = params.readByte(); - - if (id >= 35) { - if (id < 49) { - for (int idx = 0; idx < MAX_TREASURE_ITEMS; ++idx) { - XeenItem &item = party._treasure._armor[idx]; - if (!item.empty()) { - item._id = id - 35; - item._material = material; - item._bonusFlags = flags; - party._treasure._hasItems = true; - break; - } - } - return true; - } else if (id < 60) { - for (int idx = 0; idx < MAX_TREASURE_ITEMS; ++idx) { - XeenItem &item = party._treasure._accessories[idx]; - if (!item.empty()) { - item._id = id - 49; - item._material = material; - item._bonusFlags = flags; - party._treasure._hasItems = true; - break; - } - } + // Get category of item to add + ItemCategory cat = CATEGORY_WEAPON; + if (id < (35 + itemOffset)) { + } else if (id < (49 + itemOffset)) { + cat = CATEGORY_ARMOR; + id -= 35 + itemOffset; + } else if (id < (60 + itemOffset)) { + cat = CATEGORY_ACCESSORY; + id -= 49 + itemOffset; + } else if (id < (82 + itemOffset)) { + cat = CATEGORY_MISC; + id -= 60 + itemOffset; + } else { + party._questItems[id - (82 + itemOffset)]++; + } - return true; - } else if (id < 82) { - for (int idx = 0; idx < MAX_TREASURE_ITEMS; ++idx) { - XeenItem &item = party._treasure._misc[idx]; - if (!item.empty()) { - item._id = id; - item._material = material; - item._bonusFlags = flags; - party._treasure._hasItems = true; - break; - } - } + // Check for an empty slot + for (invIndex = 0, item = party._treasure[cat]; invIndex < MAX_TREASURE_ITEMS && !item->empty(); ++invIndex, ++item) + ; - return true; - } else { - party._questItems[id - 82]++; - } - } + if (invIndex == MAX_TREASURE_ITEMS) { + // Treasure category entirely full. Should never happen + warning("Treasure category was completely filled up"); + } else { + party._treasure._hasItems = true; - for (int idx = 0; idx < MAX_TREASURE_ITEMS; ++idx) { - XeenItem &item = party._treasure._weapons[idx]; - if (!item.empty()) { - item._id = id; - item._material = material; - item._bonusFlags = flags; - party._treasure._hasItems = true; - break; + if (cat == CATEGORY_MISC) { + // Handling of misc items. Note that for them, id actually specifies the material field + item->_material = id; + item->_id = params.readByte(); + item->_state._counter = (item->_material == 10 || item->_material == 11) ? 1 : _vm->getRandomNumber(3, 10); + } else { + // Weapons, armor, and accessories + item->_id = id; + item->_material = params.readByte(); + item->_state = params.readByte(); } } @@ -1391,6 +1417,8 @@ bool Scripts::cmdFallToMap(ParamsIterator ¶ms) { } bool Scripts::cmdDisplayMain(ParamsIterator ¶ms) { + _windowIndex = 11; + display(false, 0); return true; } @@ -1415,7 +1443,7 @@ bool Scripts::cmdCutsceneEndDarkside(ParamsIterator ¶ms) { Party &party = *_vm->_party; _vm->_saves->_wonDarkSide = true; party._questItems[53] = 1; - party._darkSideEnd = true; + party._darkSideCompleted = true; party._mazeId = 29; party._mazeDirection = DIR_NORTH; party._mazePosition = Common::Point(25, 21); @@ -1436,24 +1464,55 @@ bool Scripts::cmdCutsceneEndWorld(ParamsIterator ¶ms) { g_vm->saveSettings(); _vm->_saves->_wonWorld = true; - _vm->_party->_worldEnd = true; + _vm->_party->_worldCompleted = true; doWorldEnding(); return false; } bool Scripts::cmdFlipWorld(ParamsIterator ¶ms) { - _vm->_map->_loadDarkSide = params.readByte() != 0; + _vm->_map->_loadCcNum = params.readByte(); + return true; +} + +bool Scripts::cmdPlayCD(ParamsIterator ¶ms) { + int trackNum = params.readByte(); + int start = params.readUint16LE(); + int finish = params.readUint16LE(); + debugC(3, kDebugScripts, "cmdPlayCD Track=%d start=%d finish=%d", trackNum, start, finish); + + if (_vm->_files->_ccNum && trackNum < 31) + trackNum += 30; + assert(trackNum <= 60); + + start = convertCDTime(start); + finish = convertCDTime(finish); + + g_system->getAudioCDManager()->play(trackNum, 1, start, finish - start, false, Audio::Mixer::kSpeechSoundType); return true; } -bool Scripts::cmdPlayCD(ParamsIterator ¶ms) { error("TODO"); } +#define CD_FRAME_RATE 75 +uint Scripts::convertCDTime(uint srcTime) { + // Times are encoded as MMSSCC - MM=Minutes, SS=Seconds, CC=Centiseconds (1/100th second) + uint mins = srcTime / 10000; + uint csec = srcTime % 10000; + return (mins * 6000 + csec) * CD_FRAME_RATE / 100; +} void Scripts::doCloudsEnding() { + g_vm->_party->_cloudsCompleted = true; doEnding("ENDGAME"); + + g_vm->_mode = MODE_INTERACTIVE; + g_vm->_saves->saveGame(); + + g_vm->_gameMode = GMODE_MENU; + g_vm->_mode = MODE_STARTUP; } void Scripts::doDarkSideEnding() { + g_vm->_party->_darkSideCompleted = true; doEnding("ENDGAME2"); } @@ -1487,56 +1546,56 @@ void Scripts::doEnding(const Common::String &endStr) { bool Scripts::ifProc(int action, uint32 val, int mode, int charIndex) { FileManager &files = *_vm->_files; Party &party = *_vm->_party; - Character &ps = party._activeParty[charIndex]; + Character *ps = (charIndex == -1) ? nullptr : &party._activeParty[charIndex]; uint v = 0; switch (action) { case 3: // Player sex - v = (uint)ps._sex; + v = (uint)ps->_sex; break; case 4: // Player race - v = (uint)ps._race; + v = (uint)ps->_race; break; case 5: // Player class - v = (uint)ps._class; + v = (uint)ps->_class; break; case 8: // Current health points - v = (uint)ps._currentHp; + v = (uint)ps->_currentHp; break; case 9: // Current spell points - v = (uint)ps._currentSp; + v = (uint)ps->_currentSp; break; case 10: // Get armor class - v = (uint)ps.getArmorClass(false); + v = (uint)ps->getArmorClass(false); break; case 11: // Level bonus (extra beyond base) - v = ps._level._temporary; + v = ps->_level._temporary; break; case 12: // Current age, including unnatural aging - v = ps.getAge(false); + v = ps->getAge(false); break; case 13: assert(val < 18); - if (ps._skills[val]) + if (ps->_skills[val]) v = val; break; case 15: // Award assert(val < AWARDS_TOTAL); - if (ps.hasAward(val)) + if (ps->hasAward(val)) v = val; break; case 16: // Experience - v = ps._experience; + v = ps->_experience; break; case 17: // Party poison resistence @@ -1544,38 +1603,19 @@ bool Scripts::ifProc(int action, uint32 val, int mode, int charIndex) { break; case 18: // Condition - assert(val < 16); - if (!ps._conditions[val] && !(val & 0x10)) - v = val; + assert(val <= NO_CONDITION); + v = (ps->_conditions[val] || val == NO_CONDITION) ? val : 0xffffffff; break; case 19: { // Can player cast a given spell - - // Get the type of character - int category; - switch (ps._class) { - case CLASS_KNIGHT: - case CLASS_ARCHER: - category = 0; - break; - case CLASS_PALADIN: - case CLASS_CLERIC: - category = 1; - break; - case CLASS_BARBARIAN: - case CLASS_DRUID: - category = 2; - break; - default: - category = 0; - break; - } + SpellsCategory category = ps->getSpellsCategory(); + assert(category != SPELLCAT_INVALID); // Check if the character class can cast the particular spell - for (int idx = 0; idx < 39; ++idx) { + for (int idx = 0; idx < SPELLS_PER_CLASS; ++idx) { if (Res.SPELLS_ALLOWED[category][idx] == (int)val) { // Can cast it. Check if the player has it in their spellbook - if (ps._spells[idx]) + if (ps->_spells[idx]) v = val; break; } @@ -1583,42 +1623,44 @@ bool Scripts::ifProc(int action, uint32 val, int mode, int charIndex) { break; } case 20: - if (files._isDarkCc) + assert(val < 256); + if (files._ccNum && _vm->getGameID() != GType_Swords) val += 256; - assert(val < 512); v = party._gameFlags[val / 256][val % 256] ? val : 0xffffffff; break; - case 21: + case 21: { // Scans inventories for given item number + uint itemOffset = _vm->getGameID() == GType_Swords ? 6 : 0; v = 0xFFFFFFFF; - if (val < 82) { - for (int idx = 0; idx < 9; ++idx) { - if (val == 35) { - if (ps._weapons[idx]._id == val) { + if (val < (82 + itemOffset)) { + for (int idx = 0; idx < INV_ITEMS_TOTAL; ++idx) { + if (val < (35 + itemOffset)) { + if (ps->_weapons[idx]._id == val) { v = val; break; } - } else if (val < 49) { - if (ps._armor[idx]._id == (val - 35)) { + } else if (val < (49 + itemOffset)) { + if (ps->_armor[idx]._id == (val - 35)) { v = val; break; } - } else if (val < 60) { - if (ps._accessories[idx]._id == (val - 49)) { + } else if (val < (60 + itemOffset)) { + if (ps->_accessories[idx]._id == (val - (49 + itemOffset))) { v = val; break; } } else { - if (ps._misc[idx]._id == (val - 60)) { + if (ps->_misc[idx]._id == (val - (60 + itemOffset))) { v = val; break; } } } - } else if (party._questItems[val - 82]) { + } else if (party._questItems[val - (82 + itemOffset)]) { v = val; } break; + } case 25: // Returns number of minutes elapsed in the day (0-1440) v = party._minutes; @@ -1632,32 +1674,32 @@ bool Scripts::ifProc(int action, uint32 val, int mode, int charIndex) { v = party._gems; break; case 37: - // Might bonus (extra beond base) - v = ps._might._temporary; + // Might bonus (extra beyond base) + v = ps->_might._temporary; break; case 38: // Intellect bonus (extra beyond base) - v = ps._intellect._temporary; + v = ps->_intellect._temporary; break; case 39: // Personality bonus (extra beyond base) - v = ps._personality._temporary; + v = ps->_personality._temporary; break; case 40: // Endurance bonus (extra beyond base) - v = ps._endurance._temporary; + v = ps->_endurance._temporary; break; case 41: // Speed bonus (extra beyond base) - v = ps._speed._temporary; + v = ps->_speed._temporary; break; case 42: // Accuracy bonus (extra beyond base) - v = ps._accuracy._temporary; + v = ps->_accuracy._temporary; break; case 43: // Luck bonus (extra beyond base) - v = ps._luck._temporary; + v = ps->_luck._temporary; break; case 44: v = YesNo::show(_vm, val); @@ -1665,83 +1707,83 @@ bool Scripts::ifProc(int action, uint32 val, int mode, int charIndex) { break; case 45: // Might base (before bonus) - v = ps._might._permanent; + v = ps->_might._permanent; break; case 46: // Intellect base (before bonus) - v = ps._intellect._permanent; + v = ps->_intellect._permanent; break; case 47: // Personality base (before bonus) - v = ps._personality._permanent; + v = ps->_personality._permanent; break; case 48: // Endurance base (before bonus) - v = ps._endurance._permanent; + v = ps->_endurance._permanent; break; case 49: // Speed base (before bonus) - v = ps._speed._permanent; + v = ps->_speed._permanent; break; case 50: // Accuracy base (before bonus) - v = ps._accuracy._permanent; + v = ps->_accuracy._permanent; break; case 51: // Luck base (before bonus) - v = ps._luck._permanent; + v = ps->_luck._permanent; break; case 52: // Fire resistence (before bonus) - v = ps._fireResistence._permanent; + v = ps->_fireResistence._permanent; break; case 53: // Elecricity resistence (before bonus) - v = ps._electricityResistence._permanent; + v = ps->_electricityResistence._permanent; break; case 54: // Cold resistence (before bonus) - v = ps._coldResistence._permanent; + v = ps->_coldResistence._permanent; break; case 55: // Poison resistence (before bonus) - v = ps._poisonResistence._permanent; + v = ps->_poisonResistence._permanent; break; case 56: // Energy reistence (before bonus) - v = ps._energyResistence._permanent; + v = ps->_energyResistence._permanent; break; case 57: // Energy resistence (before bonus) - v = ps._magicResistence._permanent; + v = ps->_magicResistence._permanent; break; case 58: // Fire resistence (extra beyond base) - v = ps._fireResistence._temporary; + v = ps->_fireResistence._temporary; break; case 59: // Electricity resistence (extra beyond base) - v = ps._electricityResistence._temporary; + v = ps->_electricityResistence._temporary; break; case 60: // Cold resistence (extra beyond base) - v = ps._coldResistence._temporary; + v = ps->_coldResistence._temporary; break; case 61: // Poison resistence (extra beyod base) - v = ps._poisonResistence._temporary; + v = ps->_poisonResistence._temporary; break; case 62: // Energy resistence (extra beyond base) - v = ps._energyResistence._temporary; + v = ps->_energyResistence._temporary; break; case 63: // Magic resistence (extra beyond base) - v = ps._magicResistence._temporary; + v = ps->_magicResistence._temporary; break; case 64: // Level (before bonus) - v = ps._level._permanent; + v = ps->_level._permanent; break; case 65: // Total party food @@ -1772,19 +1814,19 @@ bool Scripts::ifProc(int action, uint32 val, int mode, int charIndex) { break; case 77: // Armor class (extra beyond base) - v = ps._ACTemp; + v = ps->_ACTemp; break; case 78: - // Test whether current Hp is equal to or exceeds the max HP - v = ps._currentHp >= ps.getMaxHP() ? 1 : 0; + // Test whether current Hp exceeds max HP or not + v = ps->_currentHp <= ps->getMaxHP() ? 1 : 0; break; case 79: // Test for Wizard Eye being active v = party._wizardEyeActive ? 1 : 0; break; case 81: - // Test whether current Sp is equal to or exceeds the max SP - v = ps._currentSp >= ps.getMaxSP() ? 1 : 0; + // Test whether current Sp exceeds the max SP or not + v = ps->_currentSp <= ps->getMaxSP() ? 1 : 0; break; case 84: // Current facing direction @@ -1802,7 +1844,7 @@ bool Scripts::ifProc(int action, uint32 val, int mode, int charIndex) { case 91: case 92: // Get a player stat - v = ps.getStat((Attribute)(action - 86), 0); + v = ps->getStat((Attribute)(action - 86), 0); break; case 93: // Current day of the week (10 days per week) @@ -1818,7 +1860,7 @@ bool Scripts::ifProc(int action, uint32 val, int mode, int charIndex) { break; case 102: // Thievery skill - v = ps.getThievery(); + v = ps->getThievery(); break; case 103: // Get value of world flag @@ -1826,7 +1868,7 @@ bool Scripts::ifProc(int action, uint32 val, int mode, int charIndex) { break; case 104: // Get value of quest flag - v = party._questFlags[files._isDarkCc][val] ? val : 0xffffffff; + v = party._questFlags[(_vm->getGameID() == GType_Swords ? 0 : files._ccNum * 30) + val] ? val : 0xffffffff; break; case 105: // Test number of Megacredits in party. Only used by King's Engineer in Castle Burlock @@ -1834,7 +1876,7 @@ bool Scripts::ifProc(int action, uint32 val, int mode, int charIndex) { break; case 107: // Get value of character flag - error("Unused"); + v = party._characterFlags[ps->_rosterId][val] ? val : 0xffffffff; break; default: break; @@ -1857,8 +1899,8 @@ bool Scripts::copyProtectionCheck() { if (!ConfMan.getBool("copy_protection")) return true; - // Currently not implemented - return true; + // Show the copy protection dialog + return CopyProtection::show(_vm); } void Scripts::display(bool justifyFlag, int var46) { diff --git a/engines/xeen/scripts.h b/engines/xeen/scripts.h index 40564f3dda..89b6bda250 100644 --- a/engines/xeen/scripts.h +++ b/engines/xeen/scripts.h @@ -220,7 +220,6 @@ private: MazeEvent *_event; Common::Point _currentPos; Common::Stack<StackEntry> _stack; - Common::String _message; Common::String _displayMessage; typedef EventParameters::Iterator ParamsIterator; @@ -279,7 +278,7 @@ private: * Moves the position of an object */ bool cmdMoveObj(ParamsIterator ¶ms); - + /** * Take or give amounts from various character or party figures */ @@ -320,13 +319,13 @@ private: * Gives up to three different item/amounts to various character and/or party properties */ bool cmdGiveMulti(ParamsIterator ¶ms); - + /** * Prompts the user to enter a word for passwords or mirror * teleport destinations */ bool cmdConfirmWord(ParamsIterator ¶ms); - + /** * Deals damage to a character */ @@ -545,6 +544,11 @@ private: * Displays a message */ void display(bool justifyFlag, int var46); + + /** + * Convert a CD time from the World of Xeen playCD opcodes to ScummVM CD frame number (which is at 75Hz) + */ + uint convertCDTime(uint srcTime); public: int _animCounter; bool _eventSkipped; @@ -552,6 +556,7 @@ public: DamageType _nEdamageType; int _itemType; Common::Array<MirrorEntry> _mirror; + Common::String _message; public: Scripts(XeenEngine *vm); @@ -563,8 +568,9 @@ public: /** * Handles opening grates + * @returns If true, no further event checking should be done */ - void openGrate(int wallVal, int action); + bool openGrate(int wallVal, int action); }; } // End of namespace Xeen diff --git a/engines/xeen/sound.cpp b/engines/xeen/sound.cpp index be15028f42..ae70c1f5d8 100644 --- a/engines/xeen/sound.cpp +++ b/engines/xeen/sound.cpp @@ -29,7 +29,8 @@ namespace Xeen { Sound::Sound(Audio::Mixer *mixer) : _mixer(mixer), _fxOn(true), _musicOn(true), _subtitles(false), - _songData(nullptr), _effectsData(nullptr), _musicSide(0), _musicPercent(100) { + _songData(nullptr), _effectsData(nullptr), _musicSide(0), _musicPercent(100), + _musicVolume(0), _sfxVolume(0) { _SoundDriver = new AdlibSoundDriver(); } @@ -106,44 +107,32 @@ void Sound::setFxOn(bool isOn) { g_vm->syncSoundSettings(); } -void Sound::updateSoundSettings() { - _fxOn = !ConfMan.getBool("sfx_mute"); - if (!_fxOn) - stopFX(); - - _musicOn = !ConfMan.getBool("music_mute"); - if (!_musicOn) - stopSong(); - - _subtitles = ConfMan.hasKey("subtitles") ? ConfMan.getBool("subtitles") : true; -} - void Sound::loadEffectsData() { // Stop any prior FX stopFX(); - delete[] _effectsData; - - // Load in an entire driver so we have quick access to the effects data - // that's hardcoded within it - File file("blastmus"); - byte *effectsData = new byte[file.size()]; - file.seek(0); - file.read(effectsData, file.size()); - file.close(); - _effectsData = effectsData; - - // Locate the playFX routine - const byte *fx = effectsData + READ_LE_UINT16(effectsData + 10) + 12; - assert(READ_BE_UINT16(fx + 28) == 0x81FB); - uint numEffects = READ_LE_UINT16(fx + 30); - assert(READ_BE_UINT16(fx + 36) == 0x8B87); - const byte *table = effectsData + READ_LE_UINT16(fx + 38); - - // Extract the effects offsets - _effectsOffsets.resize(numEffects); - for (uint idx = 0; idx < numEffects; ++idx) - _effectsOffsets[idx] = READ_LE_UINT16(&table[idx * 2]); + if (!_effectsData) { + // Load in an entire driver so we have quick access to the effects data that's hardcoded within it + File file("blastmus"); + byte *effectsData = new byte[file.size()]; + file.seek(0); + file.read(effectsData, file.size()); + file.close(); + _effectsData = effectsData; + + // Locate the playFX routine + const byte *fx = effectsData + READ_LE_UINT16(effectsData + 10) + 12; + assert(READ_BE_UINT16(fx + 28) == 0x81FB); + uint numEffects = READ_LE_UINT16(fx + 30); + + assert(READ_BE_UINT16(fx + 36) == 0x8B87); + const byte *table = effectsData + READ_LE_UINT16(fx + 38); + + // Extract the effects offsets + _effectsOffsets.resize(numEffects); + for (uint idx = 0; idx < numEffects; ++idx) + _effectsOffsets[idx] = READ_LE_UINT16(&table[idx * 2]); + } } void Sound::playFX(uint effectId) { @@ -160,8 +149,8 @@ void Sound::stopFX() { _SoundDriver->stopFX(); } -int Sound::songCommand(uint commandId, byte volume) { - int result = _SoundDriver->songCommand(commandId, volume); +int Sound::songCommand(uint commandId, byte musicVolume, byte sfxVolume) { + int result = _SoundDriver->songCommand(commandId, musicVolume, sfxVolume); if (commandId == STOP_SONG) { delete[] _songData; _songData = nullptr; @@ -212,8 +201,26 @@ void Sound::setMusicPercent(byte percent) { assert(percent <= 100); _musicPercent = percent; - songCommand(SET_VOLUME, (int)percent * 127 / 100); + updateVolume(); } +void Sound::updateSoundSettings() { + _fxOn = !ConfMan.getBool("sfx_mute"); + if (!_fxOn) + stopFX(); + + _musicOn = !ConfMan.getBool("music_mute"); + if (!_musicOn) + stopSong(); + + _subtitles = ConfMan.hasKey("subtitles") ? ConfMan.getBool("subtitles") : true; + _musicVolume = CLIP(ConfMan.getInt("music_volume"), 0, 255); + _sfxVolume = CLIP(ConfMan.getInt("sfx_volume"), 0, 255); + updateVolume(); +} + +void Sound::updateVolume() { + songCommand(SET_VOLUME, _musicPercent * _musicVolume / 100, _sfxVolume); +} } // End of namespace Xeen diff --git a/engines/xeen/sound.h b/engines/xeen/sound.h index 86303f682f..613299c6dc 100644 --- a/engines/xeen/sound.h +++ b/engines/xeen/sound.h @@ -40,6 +40,7 @@ private: Audio::Mixer *_mixer; Audio::SoundHandle _soundHandle; byte _musicPercent; + int _musicVolume, _sfxVolume; private: /** * Loads effects data that was embedded in the music driver @@ -50,6 +51,11 @@ private: * Updates any playing music */ void update(); + + /** + * Updates the music and sound effects playing volume + */ + void updateVolume(); public: bool _fxOn; bool _musicOn; @@ -73,7 +79,7 @@ public: /** * Executes special music command */ - int songCommand(uint commandId, byte volume = 0); + int songCommand(uint commandId, byte musicVolume = 0, byte sfxVolume = 0); /** * Stops any currently playing music diff --git a/engines/xeen/sound_driver.cpp b/engines/xeen/sound_driver.cpp index e79fcdd501..967f53ac49 100644 --- a/engines/xeen/sound_driver.cpp +++ b/engines/xeen/sound_driver.cpp @@ -225,7 +225,7 @@ void SoundDriver::playSong(const byte *data) { debugC(1, kDebugSound, "Starting song"); } -int SoundDriver::songCommand(uint commandId, byte volume) { +int SoundDriver::songCommand(uint commandId, byte musicVolume, byte sfxVolume) { if (commandId == STOP_SONG) { _musicPlaying = false; } else if (commandId == RESTART_SONG) { @@ -262,7 +262,7 @@ const CommandFn SoundDriver::FX_COMMANDS[16] = { /*------------------------------------------------------------------------*/ AdlibSoundDriver::AdlibSoundDriver() : _field180(0), _field181(0), _field182(0), - _volume(127) { + _musicVolume(0), _sfxVolume(0) { Common::fill(&_musInstrumentPtrs[0], &_musInstrumentPtrs[16], (const byte *)nullptr); Common::fill(&_fxInstrumentPtrs[0], &_fxInstrumentPtrs[16], (const byte *)nullptr); @@ -304,9 +304,9 @@ void AdlibSoundDriver::playSong(const byte *data) { resetFrequencies(); } -int AdlibSoundDriver::songCommand(uint commandId, byte volume) { +int AdlibSoundDriver::songCommand(uint commandId, byte musicVolume, byte sfxVolume) { Common::StackLock slock(_driverMutex); - SoundDriver::songCommand(commandId, volume); + SoundDriver::songCommand(commandId, musicVolume, sfxVolume); if (commandId == STOP_SONG) { _field180 = 0; @@ -320,7 +320,8 @@ int AdlibSoundDriver::songCommand(uint commandId, byte volume) { _field182 = 63; } } else if (commandId == SET_VOLUME) { - _volume = volume; + _musicVolume = musicVolume; + _sfxVolume = sfxVolume; } else if (commandId == GET_STATUS) { return _field180; } @@ -428,7 +429,7 @@ void AdlibSoundDriver::setOutputLevel(byte channelNum, uint level) { (_channels[channelNum]._scalingValue & 0xC0)); } -void AdlibSoundDriver::playInstrument(byte channelNum, const byte *data) { +void AdlibSoundDriver::playInstrument(byte channelNum, const byte *data, byte volume) { byte op1 = OPERATOR1_INDEXES[channelNum]; byte op2 = OPERATOR2_INDEXES[channelNum]; debugC(2, kDebugSound, "---START-playInstrument - %d", channelNum); @@ -441,7 +442,7 @@ void AdlibSoundDriver::playInstrument(byte channelNum, const byte *data) { int scalingVal = *data++; _channels[channelNum]._scalingValue = scalingVal; - scalingVal += (127 - _volume) / 2; + scalingVal += (127 - volume) / 2; if (scalingVal > 63) { scalingVal = 63; @@ -535,7 +536,7 @@ bool AdlibSoundDriver::musPlayInstrument(const byte *&srcP, byte param) { debugC(3, kDebugSound, "musPlayInstrument %d, %d", param, instrument); if (param < 7) - playInstrument(param, _musInstrumentPtrs[instrument]); + playInstrument(param, _musInstrumentPtrs[instrument], _musicVolume); return false; } @@ -633,7 +634,7 @@ bool AdlibSoundDriver::fxPlayInstrument(const byte *&srcP, byte param) { debugC(3, kDebugSound, "fxPlayInstrument %d, %d", param, instrument); if (!_exclude7 || param != 7) - playInstrument(param, _fxInstrumentPtrs[instrument]); + playInstrument(param, _fxInstrumentPtrs[instrument], _sfxVolume); return false; } diff --git a/engines/xeen/sound_driver.h b/engines/xeen/sound_driver.h index c78408b047..d4edd49c05 100644 --- a/engines/xeen/sound_driver.h +++ b/engines/xeen/sound_driver.h @@ -40,8 +40,7 @@ namespace OPL { namespace Xeen { enum MusicCommand { - STOP_SONG = 0, RESTART_SONG = 1, SET_VOLUME = 0x100, - GET_STATUS = 0xFFE0 + STOP_SONG = 0, RESTART_SONG = 1, SET_VOLUME = 0x100, GET_STATUS = 0xFFE0 }; class SoundDriver; @@ -170,7 +169,7 @@ public: /** * Executes special music command */ - virtual int songCommand(uint commandId, byte volume = 0); + virtual int songCommand(uint commandId, byte musicVolume = 0, byte sfxVolume = 0); /** * Returns whether music is currently playing @@ -200,7 +199,7 @@ private: int _field180; int _field181; int _field182; - int _volume; + int _musicVolume, _sfxVolume; private: /** * Initializes the state of the Adlib OPL driver @@ -246,7 +245,7 @@ private: /** * Starts playing an instrument */ - void playInstrument(byte channelNum, const byte *data); + void playInstrument(byte channelNum, const byte *data, byte volume); protected: virtual bool musSetInstrument(const byte *&srcP, byte param); virtual bool musSetPitchWheel(const byte *&srcP, byte param); @@ -301,7 +300,7 @@ public: /** * Executes special music command */ - virtual int songCommand(uint commandId, byte volume = 0); + virtual int songCommand(uint commandId, byte musicVolume = 0, byte sfxVolume = 0); }; } // End of namespace Xeen diff --git a/engines/xeen/spells.cpp b/engines/xeen/spells.cpp index 9ba581249b..04d77e1f66 100644 --- a/engines/xeen/spells.cpp +++ b/engines/xeen/spells.cpp @@ -93,47 +93,49 @@ void Spells::spellFailed() { } void Spells::castItemSpell(int itemSpellId) { + assert(itemSpellId != 0); + switch (itemSpellId) { - case 15: + case 16: if (_vm->_mode == MODE_COMBAT) { NotWhileEngaged::show(_vm, MS_Jump); return; } break; - case 20: + case 21: if (_vm->_mode == MODE_COMBAT) { NotWhileEngaged::show(_vm, MS_WizardEye); return; } break; - case 27: + case 28: if (_vm->_mode == MODE_COMBAT) { NotWhileEngaged::show(_vm, MS_LloydsBeacon); return; } break; - case 32: + case 33: frostbite2(); break; - case 41: + case 42: if (_vm->_mode == MODE_COMBAT) { NotWhileEngaged::show(_vm, MS_Teleport); return; } break; - case 47: + case 48: if (_vm->_mode == MODE_COMBAT) { NotWhileEngaged::show(_vm, MS_SuperShelter); return; } break; - case 54: + case 55: if (_vm->_mode == MODE_COMBAT) { NotWhileEngaged::show(_vm, MS_TownPortal); return; } break; - case 57: + case 58: if (_vm->_mode == MODE_COMBAT) { NotWhileEngaged::show(_vm, MS_Etheralize); return; @@ -143,8 +145,8 @@ void Spells::castItemSpell(int itemSpellId) { break; } - static const MagicSpell spells[73] = { - MS_Light, MS_Awaken, MS_MagicArrow, MS_FirstAid, MS_FlyingFist, + static const MagicSpell spells[74] = { + NO_SPELL, MS_Light, MS_Awaken, MS_MagicArrow, MS_FirstAid, MS_FlyingFist, MS_EnergyBlast, MS_Sleep, MS_Revitalize, MS_CureWounds, MS_Sparks, MS_Shrapmetal, MS_InsectSpray, MS_ToxicCloud, MS_ProtFromElements, MS_Pain, MS_Jump, MS_BeastMaster, MS_Clairvoyance, MS_TurnUndead, MS_Levitate, @@ -426,52 +428,7 @@ void Spells::deadlySwarm() { } void Spells::detectMonster() { - EventsManager &events = *_vm->_events; - Interface &intf = *_vm->_interface; - Map &map = *_vm->_map; - Party &party = *_vm->_party; - Sound &sound = *_vm->_sound; - Windows &windows = *_vm->_windows; - Window &w = windows[19]; - bool isDarkCc = _vm->_files->_isDarkCc; - int grid[7][7]; - - SpriteResource sprites(isDarkCc ? "detectmn.icn" : "detctmon.icn"); - Common::fill(&grid[0][0], &grid[6][6], 0); - - w.open(); - w.writeString(Res.DETECT_MONSTERS); - sprites.draw(w, 0, Common::Point(243, 80)); - - for (int yDiff = 3; yDiff >= -3; --yDiff) { - for (int xDiff = -3; xDiff <= 3; ++xDiff) { - for (uint monIndex = 0; monIndex < map._mobData._monsters.size(); ++monIndex) { - MazeMonster &monster = map._mobData._monsters[monIndex]; - Common::Point pt = party._mazePosition + Common::Point(xDiff, yDiff); - if (monster._position == pt) { - int &gridEntry = grid[yDiff + 3][xDiff + 3]; - if (++gridEntry > 3) - gridEntry = 3; - - sprites.draw(w, gridEntry, Common::Point(xDiff * 9 + 244, - yDiff * 7 + 81)); - } - } - } - } - - sprites.draw(w, party._mazeDirection + 1, Common::Point(270, 101)); - sound.playFX(20); - w.update(); - - do { - events.updateGameCounter(); - intf.draw3d(true); - - events.wait(1, false); - } while (!events.isKeyMousePressed()); - - w.close(); + DetectMonsters::show(_vm); } void Spells::divineIntervention() { @@ -520,8 +477,8 @@ void Spells::elementalStorm() { combat._monsterDamage = 150; combat._damageType = (DamageType)_vm->getRandomNumber(DT_FIRE, DT_POISON); combat._rangeType = RT_ALL; - sound.playFX(STORM_FX_LIST[combat._damageType]); - combat.rangedAttack(STORM_MA_LIST[combat._damageType]); + sound.playFX(STORM_FX_LIST[combat._damageType - DT_FIRE]); + combat.rangedAttack(STORM_MA_LIST[combat._damageType - DT_FIRE]); } void Spells::enchantItem() { @@ -1148,7 +1105,7 @@ void Spells::superShelter() { spellFailed(); } else { Mode oldMode = _vm->_mode; - _vm->_mode = MODE_12; + _vm->_mode = MODE_INTERACTIVE2; sound.playFX(30); intf.rest(); _vm->_mode = oldMode; @@ -1246,11 +1203,16 @@ void Spells::townPortal() { return; sound.playFX(51); - map._loadDarkSide = map._sideTownPortal; - _vm->_files->_isDarkCc = map._sideTownPortal > 0; - map.load(Res.TOWN_MAP_NUMBERS[map._sideTownPortal][townNumber - 1]); + map._loadCcNum = map._sideTownPortal; + _vm->_files->_ccNum = map._sideTownPortal > 0; + + int arrIndex = _vm->getGameID() == GType_Swords ? 2 : map._sideTownPortal; + map.load(Res.TOWN_MAP_NUMBERS[arrIndex][townNumber - 1]); - if (!_vm->_files->_isDarkCc) { + if (_vm->getGameID() == GType_Swords) { + party._mazePosition = Common::Point(8, 3); + party._mazeDirection = DIR_NORTH; + } else if (!_vm->_files->_ccNum) { party.moveToRunLocation(); } else { switch (townNumber) { diff --git a/engines/xeen/spells.h b/engines/xeen/spells.h index a333ea2f5f..24e9dd1f57 100644 --- a/engines/xeen/spells.h +++ b/engines/xeen/spells.h @@ -56,7 +56,7 @@ enum MagicSpell { MS_SuppressDisease = 67, MS_SuppressPoison = 68, MS_Teleport = 69, MS_TimeDistortion = 70, MS_TownPortal = 71, MS_ToxicCloud = 72, MS_TurnUndead = 73, MS_WalkOnWater = 74, MS_WizardEye = 75, - NO_SPELL = 76 + NO_SPELL = 76, TOTAL_SPELLS = 76 }; class Spells { diff --git a/engines/xeen/sprites.cpp b/engines/xeen/sprites.cpp index 4811f10628..7b484b53e3 100644 --- a/engines/xeen/sprites.cpp +++ b/engines/xeen/sprites.cpp @@ -146,6 +146,9 @@ void SpriteResource::drawOffset(XSurface &dest, uint16 offset, const Common::Poi dest.create(xOffset + width, yOffset + height); bounds = Common::Rect(0, 0, dest.w, dest.h); } + if (flags & SPRFLAG_SCENE_CLIPPED) { + bounds.clip(Common::Rect(8, 8, 223, 141)); + } uint16 scaleMaskXCopy = scaleMaskX; Common::Rect drawBounds; @@ -281,17 +284,23 @@ void SpriteResource::drawOffset(XSurface &dest, uint16 offset, const Common::Poi if (bit) { // Check whether there's a pixel to write, and we're within the allowable bounds. Note that for // the SPRFLAG_SCENE_CLIPPED or when enlarging, we also have an extra horizontal bounds check - if (*lineP != -1 && xp >= bounds.left && xp < bounds.right && - ((!(flags & SPRFLAG_SCENE_CLIPPED) && !enlarge) || (xp >= SCENE_CLIP_LEFT && xp < SCENE_CLIP_RIGHT))) { + if (*lineP != -1 && xp >= bounds.left && xp < bounds.right) { drawBounds.left = MIN(drawBounds.left, xp); drawBounds.right = MAX((int)drawBounds.right, xp + 1); *destP = (byte)*lineP; - if (enlarge) + if (enlarge) { *(destP + SCREEN_WIDTH) = (byte)*lineP; + *(destP + 1) = (byte)*lineP; + *(destP + 1 + SCREEN_WIDTH) = (byte)*lineP; + } } - ++destP; ++xp; + ++destP; + if (enlarge) { + ++destP; + ++xp; + } } } diff --git a/engines/xeen/subtitles.cpp b/engines/xeen/subtitles.cpp index 86dff2dac8..168ed1d4ea 100644 --- a/engines/xeen/subtitles.cpp +++ b/engines/xeen/subtitles.cpp @@ -40,7 +40,7 @@ Subtitles::~Subtitles() { void Subtitles::loadSubtitles() { File f("special.bin"); - if (!g_vm->_files->_isDarkCc) { + if (!g_vm->_files->_ccNum) { // The first subtitle line contains all the text for the Clouds intro. Since ScummVM allows // both voice and subtitles at the same time, unlike the original, we need to split up the // first subtitle into separate lines to allow them to better interleave with the voice diff --git a/engines/xeen/subtitles.h b/engines/xeen/subtitles.h index 37148dbc92..55c5faaa26 100644 --- a/engines/xeen/subtitles.h +++ b/engines/xeen/subtitles.h @@ -65,7 +65,7 @@ public: * Set which subtitle line to display */ void setLine(int line); - + /** * Resets subtitles, stopping any display */ diff --git a/engines/xeen/swordsofxeen/swordsofxeen.cpp b/engines/xeen/swordsofxeen/swordsofxeen.cpp index bbe0a74c1c..6f3963e1d0 100644 --- a/engines/xeen/swordsofxeen/swordsofxeen.cpp +++ b/engines/xeen/swordsofxeen/swordsofxeen.cpp @@ -49,10 +49,10 @@ void SwordsOfXeenEngine::death() { _sound->playSound("laff1.voc"); bool breakFlag = false; - for (int idx = 0, idx2 = 0; idx < (_files->_isDarkCc ? 10 : 23); ++idx) { + for (int idx = 0, idx2 = 0; idx < (_files->_ccNum ? 10 : 23); ++idx) { _events->updateGameCounter(); - if (_files->_isDarkCc) { + if (_files->_ccNum) { breakFlag = _events->wait(2); } else { if (idx == 1 || idx == 11) @@ -63,8 +63,8 @@ void SwordsOfXeenEngine::death() { _sound->playFX(34); } - if ((_files->_isDarkCc ? 9 : 10) == idx) { - if ((_files->_isDarkCc ? 2 : 1) > idx2) { + if ((_files->_ccNum ? 9 : 10) == idx) { + if ((_files->_ccNum ? 2 : 1) > idx2) { // Restart loop idx = -1; ++idx2; @@ -82,12 +82,44 @@ void SwordsOfXeenEngine::death() { _sound->stopAllAudio(); } +bool SwordsOfXeenEngine::showEnding() { + Windows &windows = *_windows; + SpriteResource win("win.int"); + + _screen->loadBackground("blank.raw"); + windows[28].setBounds(Common::Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)); + _screen->fadeIn(0x81); + _screen->loadPalette("scr.pal"); + _screen->fadeIn(0x81); + + win.draw(0, 0, Common::Point(0, 0)); + win.draw(0, 1, Common::Point(160, 0)); + _sound->playSound("ch1.voc"); + _events->waitForPress(); + + _screen->fadeOut(); + _screen->loadBackground("blank.raw"); + return true; +} + void SwordsOfXeenEngine::dream() { // Swords of Xeen doesn't have any dreams } void SwordsOfXeenEngine::showCutscene(const Common::String &name, int status, uint score) { + _sound->stopAllAudio(); + _events->clearEvents(); + + if (name != "ENDGAME") + error("Unknown cutscene specified"); + + showEnding(); + + _screen->freePages(); + _sound->stopAllAudio(); + _events->clearEvents(); _gameMode = GMODE_MENU; + } } // End of namespace SwordsOfXeen diff --git a/engines/xeen/swordsofxeen/swordsofxeen.h b/engines/xeen/swordsofxeen/swordsofxeen.h index d301423287..b19f4e1d53 100644 --- a/engines/xeen/swordsofxeen/swordsofxeen.h +++ b/engines/xeen/swordsofxeen/swordsofxeen.h @@ -34,6 +34,11 @@ namespace SwordsOfXeen { * Swords of Xeen specific game code */ class SwordsOfXeenEngine: public XeenEngine { +private: + /** + * Show the ending "You won" screen + */ + bool showEnding(); protected: /** * Show the starting sequence/intro diff --git a/engines/xeen/window.cpp b/engines/xeen/window.cpp index 47732dc841..caa17b7563 100644 --- a/engines/xeen/window.cpp +++ b/engines/xeen/window.cpp @@ -41,21 +41,21 @@ Windows::Windows() { Window windows[48] = { Window(Common::Rect(0, 0, 320, 200), 0, 0, 0, 0, 320, 200), Window(Common::Rect(237, 9, 317, 74), 0, 0, 237, 12, 307, 68), - Window(Common::Rect(225, 1, 319, 73), 1, 8, 225, 1, 319, 73), + Window(Common::Rect(225, 1, 320, 73), 1, 8, 225, 1, 319, 73), Window(Common::Rect(0, 0, 230, 149), 0, 0, 9, 8, 216, 140), Window(Common::Rect(235, 148, 309, 189), 2, 8, 0, 0, 0, 0), Window(Common::Rect(70, 20, 250, 183), 3, 8, 80, 38, 240, 166), - Window(Common::Rect(52, 149, 268, 197), 4, 8, 0, 0, 0, 0), + Window(Common::Rect(52, 149, 268, 198), 4, 8, 0, 0, 0, 0), Window(Common::Rect(108, 0, 200, 200), 5, 0, 0, 0, 0, 0), Window(Common::Rect(232, 9, 312, 74), 0, 0, 0, 0, 0, 0), Window(Common::Rect(103, 156, 217, 186), 6, 8, 0, 0, 0, 0), - Window(Common::Rect(226, 0, 319, 146), 7, 8, 0, 0, 0, 0), + Window(Common::Rect(226, 0, 320, 146), 7, 8, 0, 0, 0, 0), Window(Common::Rect(8, 8, 224, 140), 8, 8, 8, 8, 224, 200), Window(Common::Rect(0, 143, 320, 199), 9, 8, 0, 0, 0, 0), Window(Common::Rect(50, 103, 266, 139), 10, 8, 0, 0, 0, 0), Window(Common::Rect(0, 7, 320, 138), 11, 8, 0, 0, 0, 0), Window(Common::Rect(50, 71, 182, 129), 12, 8, 0, 0, 0, 0), - Window(Common::Rect(228, 106, 319, 146), 13, 8, 0, 0, 0, 0), + Window(Common::Rect(228, 106, 320, 146), 13, 8, 0, 0, 0, 0), Window(Common::Rect(20, 142, 290, 199), 14, 8, 0, 0, 0, 0), Window(Common::Rect(0, 20, 320, 180), 15, 8, 0, 0, 0, 0), Window(Common::Rect(231, 48, 317, 141), 16, 8, 0, 0, 0, 0), @@ -74,10 +74,10 @@ Windows::Windows() { Window(Common::Rect(12, 11, 164, 94), 0, 0, 0, 0, 52, 0), Window(Common::Rect(8, 147, 224, 192), 0, 8, 0, 0, 0, 94), Window(Common::Rect(232, 74, 312, 138), 29, 8, 0, 0, 0, 0), - Window(Common::Rect(226, 26, 319, 146), 30, 8, 0, 0, 0, 0), - Window(Common::Rect(225, 74, 319, 154), 31, 8, 0, 0, 0, 0), + Window(Common::Rect(226, 26, 320, 146), 30, 8, 0, 0, 0, 0), + Window(Common::Rect(225, 74, 320, 154), 31, 8, 0, 0, 0, 0), Window(Common::Rect(27, 6, 195, 142), 0, 8, 0, 0, 0, 0), - Window(Common::Rect(225, 140, 319, 199), 0, 8, 0, 0, 0, 0), + Window(Common::Rect(225, 140, 320, 199), 0, 8, 0, 0, 0, 0), Window(Common::Rect(12, 8, 162, 198), 0, 0, 128, 0, 119, 0), Window(Common::Rect(0, 0, 320, 200), 32, 8, 0, 0, 320, 190), Window(Common::Rect(0, 0, 320, 200), 33, 8, 0, 0, 320, 200) diff --git a/engines/xeen/worldofxeen/clouds_cutscenes.cpp b/engines/xeen/worldofxeen/clouds_cutscenes.cpp index 0e8149e37e..2ae921cf19 100644 --- a/engines/xeen/worldofxeen/clouds_cutscenes.cpp +++ b/engines/xeen/worldofxeen/clouds_cutscenes.cpp @@ -21,7 +21,6 @@ */ #include "xeen/worldofxeen/clouds_cutscenes.h" -#include "xeen/worldofxeen/worldofxeen_resources.h" #include "xeen/sound.h" namespace Xeen { @@ -40,8 +39,10 @@ bool CloudsCutscenes::showCloudsIntro() { Screen &screen = *g_vm->_screen; Sound &sound = *g_vm->_sound; - bool darkCc = files._isDarkCc; + bool darkCc = files._ccNum; files.setGameCc(0); + sound._musicSide = 0; + _subtitles.reset(); bool seenIntro = showCloudsTitle() && showCloudsIntroInner(); @@ -278,7 +279,7 @@ bool CloudsCutscenes::showCloudsIntroInner() { sound.playVoice(_INTRO_VOCS[lineCtr]); } - for (int frameCtr = 0, lookup = 0; sound.isSoundPlaying() || + for (int frameCtr = 0, lookup = 0; sound.isSoundPlaying() || (_subtitles.active() && (lineCtr == 0 || lineCtr == 4 || lineCtr == 10 || lineCtr == 13)); ) { groupo.draw(0, 0); groupo.draw(0, 1, Common::Point(160, 0)); @@ -332,7 +333,7 @@ bool CloudsCutscenes::showCloudsIntroInner() { windows[0].writeString(Res.CLOUDS_INTRO1); ctr5 = (ctr5 + 1) % 19; - + WAIT(1); continue; } @@ -379,8 +380,9 @@ void CloudsCutscenes::showCloudsEnding(uint finalScore) { FileManager &files = *g_vm->_files; Sound &sound = *g_vm->_sound; - bool darkCc = files._isDarkCc; + bool darkCc = files._ccNum; files.setGameCc(0); + _subtitles.reset(); _mirror.load("mirror.end"); _mirrBack.load("mirrback.end"); @@ -406,7 +408,7 @@ bool CloudsCutscenes::showCloudsEnding1() { Screen &screen = *_vm->_screen; Sound &sound = *_vm->_sound; - files._isDarkCc = false; + files._ccNum = false; files.setGameCc(0); // Show the castle with swirling clouds and lightning @@ -985,7 +987,7 @@ bool CloudsCutscenes::showCloudsEnding5() { king.draw(0, 1, Common::Point(160, 0)); screen.fadeIn(); _subtitles.setLine(13); - + sound.playVoice("king4.voc"); do { king.draw(0, 0, Common::Point(0, 0)); diff --git a/engines/xeen/worldofxeen/darkside_cutscenes.cpp b/engines/xeen/worldofxeen/darkside_cutscenes.cpp index 0e931ce9d7..c0ef2e98d7 100644 --- a/engines/xeen/worldofxeen/darkside_cutscenes.cpp +++ b/engines/xeen/worldofxeen/darkside_cutscenes.cpp @@ -24,7 +24,6 @@ #include "xeen/xeen.h" #include "xeen/worldofxeen/darkside_cutscenes.h" #include "xeen/worldofxeen/worldofxeen.h" -#include "xeen/worldofxeen/worldofxeen_resources.h" #define WAIT(TIME) if (_subtitles.wait(TIME)) return false @@ -74,7 +73,8 @@ const int LEFT_CLAW_IDLE_Y[32] = { bool DarkSideCutscenes::showDarkSideTitle(bool seenIntro) { Screen &screen = *g_vm->_screen; Sound &sound = *g_vm->_sound; - g_vm->_files->_isDarkCc = true; + g_vm->_files->_ccNum = true; + _subtitles.reset(); screen.loadPalette("dark.pal"); SpriteResource nwc[4] = { @@ -105,7 +105,7 @@ bool DarkSideCutscenes::showDarkSideTitle(bool seenIntro) { // Render the next frame screen.vertMerge(0); nwc[nwcIndex].draw(0, nwcFrame); - + switch (idx) { case 17: sound.playSound(voc[0]); @@ -129,7 +129,7 @@ bool DarkSideCutscenes::showDarkSideTitle(bool seenIntro) { for (int idx = 0; idx < 42 && !g_vm->shouldExit(); ++idx) { screen.vertMerge(SCREEN_HEIGHT); nwc[3].draw(0, idx); - + switch (idx) { case 3: sound.playFX(40); @@ -168,8 +168,9 @@ bool DarkSideCutscenes::showDarkSideIntro(bool seenIntro) { Screen &screen = *g_vm->_screen; Sound &sound = *g_vm->_sound; - files._isDarkCc = true; + files._ccNum = true; files.setGameCc(1); + _subtitles.reset(); if (showDarkSideTitle(seenIntro)) { if (seenIntro) { @@ -809,7 +810,7 @@ bool DarkSideCutscenes::showWorldOfXeenLogo() { for (int idx = 0; idx < 21; ++idx) { screen.restoreBackground(); wfire[6].draw(0, idx, Common::Point(0, 45)); - + switch (idx) { case 0: case 11: @@ -837,6 +838,7 @@ void DarkSideCutscenes::showDarkSideEnding(uint endingScore) { Sound &sound = *g_vm->_sound; files.setGameCc(1); + _subtitles.reset(); sound._musicSide = 1; screen.fadeOut(); @@ -1285,7 +1287,7 @@ bool DarkSideCutscenes::showDarkSideEnding3() { screen.horizMerge(0); sc16.draw(0, 0, Common::Point(7, 29)); _subtitles.show(); - sound.playSound("fail1.voc"); + sound.playVoice("fail1.voc", 2); for (int idx = 0; idx < 5; ++idx) { screen.horizMerge(0); @@ -1689,21 +1691,25 @@ void DarkSideCutscenes::showDarkSideScore(uint endingScore) { sound.stopAllAudio(); - if (g_vm->shouldExit()) { + if (!g_vm->shouldExit()) { sound.playSong("outday3.m"); Common::String str = Common::String::format(Res.DARKSIDE_ENDING1, endingScore); showPharaohEndText(str.c_str(), Res.DARKSIDE_ENDING2); + g_vm->_mode = MODE_INTERACTIVE; if (!g_vm->shouldExit()) saves.saveGame(); } } bool DarkSideCutscenes::showPharaohEndText(const char *msg1, const char *msg2, const char *msg3) { + Windows &windows = *g_vm->_windows; _ball.load("ball.int"); _claw.load("claw.int"); _dragon1.load("dragon1.int"); + + windows[39].setBounds(Common::Rect(12, 8, 162, 198)); bool result = showPharaohEndTextInner(msg1, msg2, msg3); _ball.clear(); diff --git a/engines/xeen/worldofxeen/worldofxeen.cpp b/engines/xeen/worldofxeen/worldofxeen.cpp index 962bdfec6a..ecaae8303b 100644 --- a/engines/xeen/worldofxeen/worldofxeen.cpp +++ b/engines/xeen/worldofxeen/worldofxeen.cpp @@ -71,7 +71,7 @@ void WorldOfXeenEngine::death() { } w.update(); - _events->wait(1); + _events->wait(1, false); } deathSprites.draw(0, 34, Common::Point(0, 0)); @@ -79,10 +79,10 @@ void WorldOfXeenEngine::death() { w.update(); savedBg.blitFrom(*_screen); - _sound->playSong(_files->_isDarkCc ? "laff1.voc" : "xeenlaff.voc"); + _sound->playSound(_files->_ccNum ? "laff1.voc" : "xeenlaff.voc", _files->_ccNum, 0); // Animation of Xeen or Alamar laughing - for (int idx = 0, idx2 = 0; idx < (_files->_isDarkCc ? 10 : 23); ++idx) { + for (int idx = 0, idx2 = 0; idx < (_files->_ccNum ? 10 : 23); ++idx) { _events->updateGameCounter(); _screen->blitFrom(savedBg); @@ -90,18 +90,18 @@ void WorldOfXeenEngine::death() { death1Sprites.draw(0, idx - 1); w.update(); - if (_files->_isDarkCc) { - _events->wait(2); + if (_files->_ccNum) { + _events->wait(2, false); } else { if (idx == 1 || idx == 11) _sound->playFX(33); - _events->wait(2); + _events->wait(2, false); if (idx == 15) _sound->playFX(34); } - if (idx == (_files->_isDarkCc ? 9 : 10)) { - if (idx2 < (_files->_isDarkCc ? 2 : 1)) { + if (idx == (_files->_ccNum ? 9 : 10)) { + if (idx2 < (_files->_ccNum ? 2 : 1)) { idx = -1; ++idx2; } @@ -111,6 +111,9 @@ void WorldOfXeenEngine::death() { idx = 23; } + while (_sound->isSoundPlaying()) + _events->wait(1, false); + _screen->blitFrom(savedBg); w.update(); } @@ -131,11 +134,11 @@ void WorldOfXeenEngine::dream() { while (!shouldExit() && _events->timeElapsed() < 7) _events->pollEventsAndWait(); - _sound->playSound("dreams2.voc", 1); + _sound->playSound("dreams2.voc", 1, 0); while (!shouldExit() && _sound->isSoundPlaying()) _events->pollEventsAndWait(); - _sound->playSound("laff1.voc", 1); + _sound->playSound("laff1.voc", 1, 0); while (!shouldExit() && _sound->isSoundPlaying()) _events->pollEventsAndWait(); diff --git a/engines/xeen/worldofxeen/worldofxeen_cutscenes.cpp b/engines/xeen/worldofxeen/worldofxeen_cutscenes.cpp index 9d35733de9..fafbb8ef8d 100644 --- a/engines/xeen/worldofxeen/worldofxeen_cutscenes.cpp +++ b/engines/xeen/worldofxeen/worldofxeen_cutscenes.cpp @@ -479,14 +479,14 @@ bool WorldOfXeenCutscenes::worldEnding2() { if (idx == 2 || idx == 15 || idx == 21) sound.playSound("photon.voc", 1, 0); - screen.restoreBackground(); + screen.blitFrom(savedBg); sc24.draw(0, idx, Common::Point(103, 6)); WAIT(3); } for (int idx = 20; idx < 35; ++idx) { - screen.restoreBackground(); + screen.blitFrom(savedBg); sc24.draw(0, idx, Common::Point(103, 6)); WAIT(3); @@ -599,13 +599,13 @@ bool WorldOfXeenCutscenes::worldEnding3() { SpriteResource sc30("sc30.eg2"); SpriteResource sc28[14] = { - SpriteResource("sc28a.eg2"), SpriteResource("sc28b1.eg2"), - SpriteResource("sc28c.eg2"), SpriteResource("sc28d.eg2"), - SpriteResource("sc28e.eg2"), SpriteResource("sc28f.eg2"), - SpriteResource("sc28g.eg2"), SpriteResource("sc28h.eg2"), - SpriteResource("sc28i.eg2"), SpriteResource("sc28j.eg2"), - SpriteResource("sc28k.eg2"), SpriteResource("sc28l.eg2"), - SpriteResource("sc28m.eg2"), SpriteResource("sc28n.eg2"), + SpriteResource("sc28a.eg2", 2), SpriteResource("sc28b1.eg2", 2), + SpriteResource("sc28c.eg2", 2), SpriteResource("sc28d.eg2", 2), + SpriteResource("sc28e.eg2", 2), SpriteResource("sc28f.eg2", 2), + SpriteResource("sc28g.eg2", 2), SpriteResource("sc28h.eg2", 2), + SpriteResource("sc28i.eg2", 2), SpriteResource("sc28j.eg2", 2), + SpriteResource("sc28k.eg2", 2), SpriteResource("sc28l.eg2", 2), + SpriteResource("sc28m.eg2", 2), SpriteResource("sc28n.eg2", 2), }; // Transformation of Xeen into a globe @@ -652,7 +652,7 @@ bool WorldOfXeenCutscenes::worldEnding3() { } screen.fadeOut(); - while (sound.isMusicPlaying()) { + while (sound.isSoundPlaying()) { WAIT(2); } diff --git a/engines/xeen/worldofxeen/worldofxeen_menu.cpp b/engines/xeen/worldofxeen/worldofxeen_menu.cpp index 3e4e14455e..6d80e62806 100644 --- a/engines/xeen/worldofxeen/worldofxeen_menu.cpp +++ b/engines/xeen/worldofxeen/worldofxeen_menu.cpp @@ -41,7 +41,10 @@ void MainMenuContainer::show() { menu = new DarkSideMainMenuContainer(); break; case GType_WorldOfXeen: - menu = new WorldOfXeenMainMenuContainer(); + if (g_vm->getIsCD()) + menu = new WorldOfXeenCDMainMenuContainer(); + else + menu = new WorldOfXeenMainMenuContainer(); break; default: error("Invalid game"); @@ -51,9 +54,20 @@ void MainMenuContainer::show() { delete menu; } -MainMenuContainer::MainMenuContainer(const Common::String &spritesName, uint frameCount) : - _frameCount(frameCount), _animateCtr(0), _dialog(nullptr) { - _backgroundSprites.load(spritesName); +MainMenuContainer::MainMenuContainer(const char *spritesName1, const char *spritesName2, const char *spritesName3) : + _animateCtr(0), _dialog(nullptr) { + g_vm->_files->setGameCc(g_vm->getGameID() == GType_Clouds ? 0 : 1); + + _backgroundSprites.resize(1 + (spritesName2 ? 1 : 0) + (spritesName3 ? 1 : 0)); + _backgroundSprites[0].load(spritesName1); + if (spritesName2) + _backgroundSprites[1].load(spritesName2); + if (spritesName3) + _backgroundSprites[2].load(spritesName3); + + _frameCount = 0; + for (uint idx = 0; idx < _backgroundSprites.size(); ++idx) + _frameCount += _backgroundSprites[idx].size(); } MainMenuContainer::~MainMenuContainer() { @@ -66,7 +80,17 @@ MainMenuContainer::~MainMenuContainer() { void MainMenuContainer::draw() { g_vm->_screen->restoreBackground(); _animateCtr = (_animateCtr + 1) % _frameCount; - _backgroundSprites.draw(0, _animateCtr); + + // Draw the next background frame + uint frameNum = _animateCtr; + for (uint idx = 0; idx < _backgroundSprites.size(); ++idx) { + if (frameNum < _backgroundSprites[idx].size()) { + _backgroundSprites[idx].draw(0, frameNum); + return; + } else { + frameNum -= _backgroundSprites[idx].size(); + } + } } void MainMenuContainer::execute() { @@ -96,7 +120,7 @@ void MainMenuContainer::execute() { // Check for events events.updateGameCounter(); - + if (events.wait(4, true)) { if (_dialog) { // There's a dialog active, so let it handle the event @@ -109,11 +133,9 @@ void MainMenuContainer::execute() { } else { // No active dialog. If Escape pressed, exit game entirely. Otherwise, // open up the main menu dialog - if (events.isKeyPending()) { - Common::KeyState key; - if (events.getKey(key) && key.keycode == Common::KEYCODE_ESCAPE) - g_vm->_gameMode = GMODE_QUIT; - } + PendingEvent pe; + if (events.getEvent(pe) && pe._keyState.keycode == Common::KEYCODE_ESCAPE) + g_vm->_gameMode = GMODE_QUIT; events.clearEvents(); showMenuDialog(); @@ -124,7 +146,7 @@ void MainMenuContainer::execute() { /*------------------------------------------------------------------------*/ -CloudsMainMenuContainer::CloudsMainMenuContainer() : MainMenuContainer("intro.vga", 9) { +CloudsMainMenuContainer::CloudsMainMenuContainer() : MainMenuContainer("intro.vga") { } void CloudsMainMenuContainer::display() { @@ -149,7 +171,7 @@ void CloudsMainMenuContainer::showMenuDialog() { /*------------------------------------------------------------------------*/ -DarkSideMainMenuContainer::DarkSideMainMenuContainer() : MainMenuContainer("title2a.int", 10) { +DarkSideMainMenuContainer::DarkSideMainMenuContainer() : MainMenuContainer("title2a.int") { Screen &screen = *g_vm->_screen; Sound &sound = *g_vm->_sound; screen.loadPalette("dark.pal"); @@ -185,7 +207,7 @@ void DarkSideMainMenuContainer::showMenuDialog() { /*------------------------------------------------------------------------*/ -WorldOfXeenMainMenuContainer::WorldOfXeenMainMenuContainer() : MainMenuContainer("world.int", 5) { +WorldOfXeenMainMenuContainer::WorldOfXeenMainMenuContainer() : MainMenuContainer("world.int") { } void WorldOfXeenMainMenuContainer::display() { @@ -210,7 +232,33 @@ void WorldOfXeenMainMenuContainer::showMenuDialog() { /*------------------------------------------------------------------------*/ +WorldOfXeenCDMainMenuContainer::WorldOfXeenCDMainMenuContainer() : MainMenuContainer("world0.int", "world1.int", "world2.int") { +} + +void WorldOfXeenCDMainMenuContainer::display() { + FileManager &files = *g_vm->_files; + Screen &screen = *g_vm->_screen; + Sound &sound = *g_vm->_sound; + + sound._musicSide = 1; + files.setGameCc(1); + + screen.loadPalette("dark.pal"); + screen.loadBackground("world.raw"); + screen.saveBackground(); + + if (!sound.isMusicPlaying()) + sound.playSong("newbrigh.m"); +} + +void WorldOfXeenCDMainMenuContainer::showMenuDialog() { + setOwner(new WorldMenuDialog(this)); +} + +/*------------------------------------------------------------------------*/ + bool MainMenuDialog::handleEvents() { + FileManager &files = *g_vm->_files; checkEvents(g_vm); int difficulty; @@ -227,14 +275,18 @@ bool MainMenuDialog::handleEvents() { g_vm->_gameMode = GMODE_PLAY_GAME; break; - case Common::KEYCODE_l: + case Common::KEYCODE_l: { // Load existing game + int ccNum = files._ccNum; g_vm->_saves->newGame(); - if (!g_vm->_saves->loadGame()) + if (!g_vm->_saves->loadGame()) { + files.setGameCc(ccNum); return true; + } g_vm->_gameMode = GMODE_PLAY_GAME; break; + } case Common::KEYCODE_c: case Common::KEYCODE_v: @@ -285,7 +337,7 @@ void CloudsMenuDialog::loadButtons() { void CloudsMenuDialog::draw() { Windows &windows = *g_vm->_windows; Window &w = windows[GAME_WINDOW]; - + w.frame(); w.writeString(Common::String::format(Res.OPTIONS_MENU, Res.GAME_NAMES[0], g_vm->_gameWon[0] ? 117 : 92, 1992)); drawButtons(&w); @@ -532,7 +584,7 @@ void OtherOptionsDialog::draw() { w.frame(); w.writeString(Common::String::format(Res.OPTIONS_MENU, - Res.GAME_NAMES[g_vm->getGameID() == GType_WorldOfXeen ? 2 : 1], + Res.GAME_NAMES[g_vm->getGameID() == GType_WorldOfXeen ? 2 : 1], w.getBounds().height() - 33, 1993)); drawButtons(&w); } diff --git a/engines/xeen/worldofxeen/worldofxeen_menu.h b/engines/xeen/worldofxeen/worldofxeen_menu.h index e753665f08..ba4a465047 100644 --- a/engines/xeen/worldofxeen/worldofxeen_menu.h +++ b/engines/xeen/worldofxeen/worldofxeen_menu.h @@ -25,6 +25,7 @@ #include "xeen/xeen.h" #include "xeen/dialogs/dialogs.h" +#include "common/array.h" namespace Xeen { namespace WorldOfXeen { @@ -35,7 +36,7 @@ class MainMenuContainer { private: uint _animateCtr; uint _frameCount; - SpriteResource _backgroundSprites; + Common::Array<SpriteResource> _backgroundSprites; MenuContainerDialog *_dialog; protected: /** @@ -61,7 +62,7 @@ public: /** * Constructor */ - MainMenuContainer(const Common::String &spritesName, uint frameCount); + MainMenuContainer(const char *spritesName1, const char *spritesName2 = nullptr, const char *spritesName3 = nullptr); /** * Destructor @@ -128,6 +129,21 @@ public: WorldOfXeenMainMenuContainer(); }; +class WorldOfXeenCDMainMenuContainer : public MainMenuContainer { +protected: + /** + * Called when the menu screen is first shown + */ + virtual void display(); + + /** + * Shows the main menu dialog + */ + virtual void showMenuDialog(); +public: + WorldOfXeenCDMainMenuContainer(); +}; + class MenuContainerDialog : public ButtonContainer { protected: MainMenuContainer *_owner; diff --git a/engines/xeen/worldofxeen/worldofxeen_resources.cpp b/engines/xeen/worldofxeen/worldofxeen_resources.cpp deleted file mode 100644 index 5bd6247d08..0000000000 --- a/engines/xeen/worldofxeen/worldofxeen_resources.cpp +++ /dev/null @@ -1,62 +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. - * - */ - -#include "xeen/worldofxeen/worldofxeen_resources.h" - -namespace Xeen { -namespace WorldOfXeen { - -const char *const WorldOfXeenResources::CLOUDS_INTRO1 = "\xC" "00\xB" "082\x9" "040\x3" - "cKing Burlock\xB" "190\x9" "040Peasants\xB" "082\x9" "247" - "Lord Xeen\xB" "190\x9" "258Xeen's Pet\xB" "179\x9" "150Crodo"; - -const char *const WorldOfXeenResources::DARKSIDE_ENDING1 = "\n\x3" "cCongratulations\n" - "\n" - "Your Final Score is:\n" - "\n" - "%010lu\n" - "\x3" "l\n" - "Please send this score to the Ancient's Headquarters " - "where you'll be added to the Hall of Legends!\n" - "\n" - "Ancient's Headquarters\n" - "New World Computing, Inc.\n" - "P.O. Box 4302\n" - "Hollywood, CA 90078"; - -const char *const WorldOfXeenResources::DARKSIDE_ENDING2 = "\n" - "Adventurers,\n" - "\n" - "I will save your game in Castleview.\n" - "\n" - "The World of Xeen still needs you!\n" - "\n" - "Load your game afterwards and come visit me in the " - "Great Pyramid for further instructions"; - -const char *const WorldOfXeenResources::PHAROAH_ENDING_TEXT1 = "\xC" "d\xB" - "001\x9" "001%s\x3" "c\x9" "000\xB" "180Press a Key!\x3" "l"; -const char *const WorldOfXeenResources::PHAROAH_ENDING_TEXT2 = "\xC" "04\xB" - "000\x9" "000%s\x3" "c\x9" "000\xB" "180Press a Key!\x3" "l\xC" "d"; - -} // End of namespace WorldOfXeen -} // End of namespace Xeen diff --git a/engines/xeen/xeen.cpp b/engines/xeen/xeen.cpp index c8fb40d2a0..371f437172 100644 --- a/engines/xeen/xeen.cpp +++ b/engines/xeen/xeen.cpp @@ -49,6 +49,7 @@ XeenEngine::XeenEngine(OSystem *syst, const XeenGameDescription *gameDesc) _locations = nullptr; _map = nullptr; _party = nullptr; + _patcher = nullptr; _resources = nullptr; _saves = nullptr; _screen = nullptr; @@ -75,6 +76,7 @@ XeenEngine::~XeenEngine() { delete _locations; delete _map; delete _party; + delete _patcher; delete _saves; delete _screen; delete _scripts; @@ -100,6 +102,7 @@ bool XeenEngine::initialize() { _locations = new LocationManager(); _map = new Map(this); _party = new Party(this); + _patcher = new Patcher(); _saves = new SavesManager(_targetName); _screen = new Screen(this); _scripts = new Scripts(this); @@ -114,19 +117,26 @@ bool XeenEngine::initialize() { syncSoundSettings(); // Load settings + loadSettings(); + + return true; +} + +void XeenEngine::loadSettings() { _gameWon[0] = ConfMan.hasKey("game_won") && ConfMan.getBool("game_won"); _gameWon[1] = ConfMan.hasKey("game_won2") && ConfMan.getBool("game_won2"); _gameWon[2] = ConfMan.hasKey("game_won3") && ConfMan.getBool("game_won3"); _finalScore = ConfMan.hasKey("final_score") ? ConfMan.getInt("final_score") : 0; + _extOptions._showItemCosts = ConfMan.hasKey("ShowItemCosts") && ConfMan.getBool("ShowItemCosts"); + _extOptions._durableArmor = ConfMan.hasKey("DurableArmor") && ConfMan.getBool("DurableArmor"); + // If requested, load a savegame instead of showing the intro if (ConfMan.hasKey("save_slot")) { int saveSlot = ConfMan.getInt("save_slot"); if (saveSlot >= 0 && saveSlot <= 999) _loadSaveSlot = saveSlot; } - - return true; } Common::Error XeenEngine::run() { @@ -183,11 +193,12 @@ Common::Error XeenEngine::loadGameState(int slot) { } bool XeenEngine::canLoadGameStateCurrently() { - return _mode != MODE_COMBAT; + return _mode != MODE_STARTUP; } bool XeenEngine::canSaveGameStateCurrently() { - return _mode != MODE_COMBAT; + return _mode != MODE_COMBAT && _mode != MODE_STARTUP && _mode != MODE_SCRIPT_IN_PROGRESS + && (_map->mazeData()._mazeFlags & RESTRICTION_SAVE) == 0; } void XeenEngine::playGame() { @@ -196,6 +207,7 @@ void XeenEngine::playGame() { SpriteResource::setClippedBottom(140); play(); + _sound->stopAllAudio(); } void XeenEngine::play() { @@ -203,14 +215,15 @@ void XeenEngine::play() { _screen->loadBackground("back.raw"); _screen->loadPalette("mm4.pal"); - if (getGameID() == GType_DarkSide && !_map->_loadDarkSide) { - _map->_loadDarkSide = true; + if (getGameID() == GType_DarkSide && !_map->_loadCcNum) { + _map->_loadCcNum = 1; _party->_mazeId = 29; _party->_mazeDirection = DIR_NORTH; _party->_mazePosition.x = 25; _party->_mazePosition.y = 21; } + _map->clearMaze(); if (_loadSaveSlot >= 0) { _saves->newGame(); _saves->loadGameState(_loadSaveSlot); @@ -231,7 +244,7 @@ void XeenEngine::play() { _combat->_moveMonsters = true; if (_mode == MODE_STARTUP) { - _mode = MODE_1; + _mode = MODE_INTERACTIVE; _screen->fadeIn(); } @@ -243,22 +256,26 @@ void XeenEngine::play() { death(); _mode = MODE_STARTUP; + _gameMode = GMODE_MENU; } void XeenEngine::gameLoop() { // Main game loop - while (!shouldExit()) { - if (_loadSaveSlot >= 0) { + while (isLoadPending() || !shouldExit()) { + if (isLoadPending()) { // Load any pending savegame int saveSlot = _loadSaveSlot; _loadSaveSlot = -1; _saves->loadGameState(saveSlot); + _interface->drawParty(true); } _map->cellFlagLookup(_party->_mazePosition); if (_map->_currentIsEvent) { _gameMode = (GameMode)_scripts->checkEvents(); - if (shouldExit() || _gameMode) + if (isLoadPending()) + continue; + if (shouldExit()) return; } _party->giveTreasure(); diff --git a/engines/xeen/xeen.h b/engines/xeen/xeen.h index c79b240388..98b09e7f23 100644 --- a/engines/xeen/xeen.h +++ b/engines/xeen/xeen.h @@ -39,6 +39,7 @@ #include "xeen/locations.h" #include "xeen/map.h" #include "xeen/party.h" +#include "xeen/patcher.h" #include "xeen/resources.h" #include "xeen/saves.h" #include "xeen/screen.h" @@ -77,7 +78,7 @@ enum XeenDebugChannels { enum Mode { MODE_FF = -1, MODE_STARTUP = 0, - MODE_1 = 1, + MODE_INTERACTIVE = 1, MODE_COMBAT = 2, MODE_3 = 3, MODE_4 = 4, @@ -85,11 +86,11 @@ enum Mode { MODE_6 = 6, MODE_7 = 7, MODE_8 = 8, - MODE_RECORD_EVENTS = 9, + MODE_SCRIPT_IN_PROGRESS = 9, MODE_CHARACTER_INFO = 10, - MODE_12 = 12, + MODE_INTERACTIVE2 = 12, MODE_DIALOG_123 = 13, - MODE_17 = 17, + MODE_INTERACTIVE7 = 17, MODE_86 = 86 }; @@ -106,6 +107,15 @@ struct XeenGameDescription; #define XEEN_SAVEGAME_VERSION 1 class XeenEngine : public Engine { + /** + * Container to a set of options newly introduced under ScummVM + */ + struct ExtendedOptions { + bool _showItemCosts; + bool _durableArmor; + + ExtendedOptions() : _showItemCosts(false), _durableArmor(false) {} + }; private: const XeenGameDescription *_gameDescription; Common::RandomSource _randomSource; @@ -115,6 +125,11 @@ private: */ bool initialize(); + /** + * Load settings + */ + void loadSettings(); + // Engine APIs virtual Common::Error run(); virtual bool hasFeature(EngineFeature f) const; @@ -170,6 +185,7 @@ public: LocationManager *_locations; Map *_map; Party *_party; + Patcher *_patcher; Resources *_resources; SavesManager *_saves; Screen *_screen; @@ -178,13 +194,13 @@ public: Spells *_spells; Windows *_windows; Mode _mode; - GameEvent _gameEvent; GameMode _gameMode; bool _noDirectionSense; bool _startupWindowActive; uint _endingScore; bool _gameWon[3]; uint _finalScore; + ExtendedOptions _extOptions; public: XeenEngine(OSystem *syst, const XeenGameDescription *gameDesc); virtual ~XeenEngine(); @@ -195,6 +211,7 @@ public: uint16 getVersion() const; uint32 getGameID() const; uint32 getGameFeatures() const; + bool getIsCD() const; int getRandomNumber(int maxNumber); @@ -206,9 +223,14 @@ public: void GUIError(const char *msg, ...) GCC_PRINTF(2, 3); /** - * Returns true if the game should be exited (and likely return to game menu) + * Returns true if the game should be exited (either quitting, exiting to the main menu, or loading a savegame) + */ + bool shouldExit() const { return _gameMode != GMODE_NONE || isLoadPending() || shouldQuit(); } + + /** + * Returns true if a savegame load is pending */ - bool shouldExit() const { return _gameMode != GMODE_NONE || shouldQuit(); } + bool isLoadPending() const { return _loadSaveSlot != -1; } /** * Load a savegame diff --git a/engines/zvision/detection.cpp b/engines/zvision/detection.cpp index 5e535a9954..f14263f012 100644 --- a/engines/zvision/detection.cpp +++ b/engines/zvision/detection.cpp @@ -88,8 +88,8 @@ bool ZVisionMetaEngine::hasFeature(MetaEngineFeature f) const { (f == kSavesSupportMetaInfo) || (f == kSavesSupportThumbnail) || (f == kSavesSupportCreationDate) || + (f == kSavesSupportPlayTime) || (f == kSimpleSavesNames); - //(f == kSavesSupportPlayTime); } bool ZVision::ZVision::hasFeature(EngineFeature f) const { @@ -178,7 +178,7 @@ SaveStateDescriptor ZVisionMetaEngine::querySaveMetaInfos(const char *target, in // We only use readSaveGameHeader() here, which doesn't need an engine callback ZVision::SaveManager *zvisionSaveMan = new ZVision::SaveManager(NULL); - bool successfulRead = zvisionSaveMan->readSaveGameHeader(in, header); + bool successfulRead = zvisionSaveMan->readSaveGameHeader(in, header, false); delete zvisionSaveMan; delete in; @@ -192,7 +192,7 @@ SaveStateDescriptor ZVisionMetaEngine::querySaveMetaInfos(const char *target, in desc.setThumbnail(header.thumbnail); - if (header.version > 0) { + if (header.version >= 1) { int day = header.saveDay; int month = header.saveMonth; int year = header.saveYear; @@ -203,8 +203,10 @@ SaveStateDescriptor ZVisionMetaEngine::querySaveMetaInfos(const char *target, in int minutes = header.saveMinutes; desc.setSaveTime(hour, minutes); + } - //desc.setPlayTime(header.playTime * 1000); + if (header.version >= 2) { + desc.setPlayTime(header.playTime * 1000); } return desc; diff --git a/engines/zvision/detection_tables.h b/engines/zvision/detection_tables.h index 3df8f280ee..cb813e6d5b 100644 --- a/engines/zvision/detection_tables.h +++ b/engines/zvision/detection_tables.h @@ -49,7 +49,7 @@ static const ADExtraGuiOptionsMap optionsList[] = { GAMEOPTION_ORIGINAL_SAVELOAD, { _s("Use original save/load screens"), - _s("Use the original save/load screens instead of the ScummVM interface"), + _s("Use the original save/load screens instead of the ScummVM ones"), "originalsaveload", false } diff --git a/engines/zvision/file/save_manager.cpp b/engines/zvision/file/save_manager.cpp index 4259937a3b..dd4425ae90 100644 --- a/engines/zvision/file/save_manager.cpp +++ b/engines/zvision/file/save_manager.cpp @@ -128,6 +128,8 @@ void SaveManager::writeSaveGameHeader(Common::OutSaveFile *file, const Common::S file->writeSint16LE(td.tm_mday); file->writeSint16LE(td.tm_hour); file->writeSint16LE(td.tm_min); + + file->writeUint32LE(g_engine->getTotalPlayTime() / 1000); } Common::Error SaveManager::loadGame(int slot) { @@ -162,8 +164,6 @@ Common::Error SaveManager::loadGame(int slot) { scriptManager->deserialize(saveFile); delete saveFile; - if (header.thumbnail) - delete header.thumbnail; if (_engine->getGameId() == GID_NEMESIS && scriptManager->getCurrentLocation() == "tv2f") { // WORKAROUND for script bug #6793: location tv2f (stairs) has two states: @@ -186,20 +186,26 @@ Common::Error SaveManager::loadGame(int slot) { } } + g_engine->setTotalPlayTime(header.playTime * 1000); + return Common::kNoError; } -bool SaveManager::readSaveGameHeader(Common::InSaveFile *in, SaveGameHeader &header) { +bool SaveManager::readSaveGameHeader(Common::InSaveFile *in, SaveGameHeader &header, bool skipThumbnail) { + header.saveYear = 0; + header.saveMonth = 0; + header.saveDay = 0; + header.saveHour = 0; + header.saveMinutes = 0; + header.playTime = 0; + header.saveName.clear(); + header.thumbnail = nullptr; + header.version = 0; + uint32 tag = in->readUint32BE(); // Check if it's original savegame than fill header structure if (tag == MKTAG('Z', 'N', 'S', 'G')) { - header.saveYear = 0; - header.saveMonth = 0; - header.saveDay = 0; - header.saveHour = 0; - header.saveMinutes = 0; header.saveName = "Original Save"; - header.thumbnail = NULL; header.version = SAVE_ORIGINAL; in->seek(-4, SEEK_CUR); return true; @@ -226,23 +232,26 @@ bool SaveManager::readSaveGameHeader(Common::InSaveFile *in, SaveGameHeader &hea } // Read in the save name - header.saveName.clear(); char ch; while ((ch = (char)in->readByte()) != '\0') header.saveName += ch; // Get the thumbnail - header.thumbnail = Graphics::loadThumbnail(*in); - if (!header.thumbnail) + if (!Graphics::loadThumbnail(*in, header.thumbnail, skipThumbnail)) { return false; + } // Read in save date/time - header.saveYear = in->readSint16LE(); - header.saveMonth = in->readSint16LE(); - header.saveDay = in->readSint16LE(); - header.saveHour = in->readSint16LE(); + header.saveYear = in->readSint16LE(); + header.saveMonth = in->readSint16LE(); + header.saveDay = in->readSint16LE(); + header.saveHour = in->readSint16LE(); header.saveMinutes = in->readSint16LE(); + if (header.version >= 2) { + header.playTime = in->readUint32LE(); + } + return true; } diff --git a/engines/zvision/file/save_manager.h b/engines/zvision/file/save_manager.h index 9e816373ea..e5bf47b47b 100644 --- a/engines/zvision/file/save_manager.h +++ b/engines/zvision/file/save_manager.h @@ -42,8 +42,9 @@ struct SaveGameHeader { byte version; Common::String saveName; Graphics::Surface *thumbnail; - int saveYear, saveMonth, saveDay; - int saveHour, saveMinutes; + int16 saveYear, saveMonth, saveDay; + int16 saveHour, saveMinutes; + uint32 playTime; }; class SaveManager { @@ -64,7 +65,7 @@ private: enum { SAVE_ORIGINAL = 0, - SAVE_VERSION = 1 + SAVE_VERSION = 2 }; Common::MemoryWriteStreamDynamic *_tempThumbnail; @@ -94,7 +95,7 @@ public: Common::Error loadGame(int slot); Common::SeekableReadStream *getSlotFile(uint slot); - bool readSaveGameHeader(Common::SeekableReadStream *in, SaveGameHeader &header); + bool readSaveGameHeader(Common::SeekableReadStream *in, SaveGameHeader &header, bool skipThumbnail = true); void prepareSaveBuffer(); void flushSaveBuffer(); |